For this tutorial you will need:
Several online "how to" guides are available for installing and configuring Ruby on Rails. See Rails Resources for links to setup tutorials and installation bundles
This tutorial walks you through the creation of a very simple Rails application. The Movie Lending Library application is a web application that allows you to add, remove, borrow and return movies from a shared library.
Komodo has a very powerful Ruby on Rails project template with macros and commands for further automating the creation of Rails applications. The entire application can be created within Komodo using these tools and Komodo itself, without having to work at the command-line.
In this tutorial you will:
Komodo ships with a powerful template for Rails projects, containing several macros for:
To create the tutorial project file:
If Rails has been installed correctly, you should see a Komodo
Alert dialog box saying "The movielib project is built". The
movielib.kpf project should open in the Projects
tab on the left, and should contain "Live Folders" of all the
directories created by the rails
command (app,
components, config, db, etc.) plus a Virtual Folder containing
the Komodo macros we'll be using.
Komodo Tip: Komodo project templates
can have a special macro called |
Rails tools have a lot of third-party dependencies, and sometimes a new release of one of them can break the tools in the Rails project template. If a macro or command isn't working as expected, check the the online FAQs for possible solutions.
If MySQL is installed locally and can accept connections by 'root' without a password (it often is by default), click the Create Databases macro in the Rails Tools project folder to create the database.
If you have set a MySQL root password, created another MySQL account that you would like to use, or are running the server on a different host, you will need to configure the database.yml file:
username
and
password
values to match the configuration of your
MySQL server. If you're not sure what values to use, leave the
default values ('username: root
' and
'password:
'). In this tutorial, we will only be
working with the development
database, but you
should modify the test
and production
sections as well to avoid errors with the Create
Databases macro below.hostname:
host to the
development:
configuration block, or modify the
value if that setting is already present (set to
localhost
by default.If you would like to use a database server other than MySQL, consult the Rails documentation on configuring this file, making sure you have the necessary database drivers installed, and creating the database manually. The database macros in the project will only work with MySQL.
In the Rails Tools project folder is a macro called Create Databases. Double-click the macro to run it.
If the database.yml file (and the database server) are configured correctly, a database called movielib (as specified in the database.yml file - derived from the project name) will be created and an alert will appear indicating that the database creation is done.
Now that we have a directory tree with stub files, and a
database for the application to use, we need to create our first
model. At the command line we would use 'ruby
script/generate model name
', but in Komodo we
have a macro to do that for us.
In the Rails Tools/Generators project folder are macros for creating various parts of a Rails application. Double-click the model macro. A dialog-box appears prompting for a model name.
Since our application is a library for movies, we will need a
movie model. Enter movie
in the dialog box and click
OK.
The following output should appear in the Command Output tab showing the files created in this step:
exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/movie.rb create test/unit/movie_test.rb create test/fixtures/movies.yml create db/migrate create db/migrate/001_create_movies.rb
Komodo will also open these files in editor tabs. At this point we need to define the database table that this model will use. Click on the 001_create_movies.rb editor tab (or open it from the db/migrate/ project folder). Notice that Rails has pluralized the name "movie" for the database table name.
Add the following below the 'create_table :movies do
|t|
' line:
t.column :title, :string t.column :created_on, :date
This is the file that will control the creation of the
movies
table in the database. The
create_table
line adds the table, and our additions
create the columns 'title
' (a string) and
'created_on
' (a special Rails value for date
stamping new records).
We will need to track the name of the person borrowing a movie. We'll do this with a person model.
Double-click on the model macro again. This
time, specify person
as the model name and click
OK.
The Command Output tab will again show us which files were
created. The file we need to edit is
002_create_people.rb. Add the following below the
create_table
line:
t.column :name, :string
With both models, Rails will automatically add a
tablename_id
key for the table. We only
need to add the "custom" fields.
Now we need a way to link people with movies. We'll do this
with a 'checkout' model. Double-click on the
model macro again. This time, specify
checkout
as the model name and click
OK.
Add the following to to the newly created
003_create_checkouts.rb below the
'create_table
' line:
t.column :person_id, :integer, :null => false t.column :movie_id, :integer, :null => false t.column :created_on, :date
Now that we've defined the models in Rails, we need to apply
them to our database. In Rails this is done with the 'rake
migrate
' command. Again, there's a macro to do this for
us.
Double-click the db:migrate macro in Rails Tools|Migrate. The Command Output tab should show that the tables 'movies', 'people' and 'checkouts' have been created.
Generating a scaffold is a quick way to get a skeletal, working Rails application that can be modified iteratively. Since most database driven web applications are very similar in their basic design, Rails builds a generic application based on the information in your models which you can then modify to meet your specific requirements.
In the Generators folder, double-click the
scaffold macro. In the dialog box enter
movie
as the model name, and library
as
the scaffold name. Click OK
Several files are created and opened in editor tabs. Since the tutorial won't be dealing with most of these files, feel free to close them to unclutter your workspace - they can all be opened from the project later as needed.
At this point we have a working application. It's not yet useful as a lending library, but we can have a look at what we've got so far.
In the Run folder, double-click the run server macro. This will start the the Webrick web server in a separate console.
Open the http://localhost:3000/library in your favourite web browser. You should see a "Listing movies" page with no data and a "New movie" link. This isn't actually usable yet, but we can leave the web server running and see our changes as we make them.
So far, the main Library page doesn't tell us much. We need to create a basic listing page to view the library contents.
Open the list.rhtml file from the app/views/library project directory, or click on it's editor tab if it is already open. Replace the contents with the following:
<h1>DVD Lending Library</h1> <table> <tr> <th class="dvdlib_header">Title</th> <th class="dvdlib_header">Status</th> </tr> <% for movie in @movies %> <tr> <td class="dvdlib_item"><%=h movie.title %></td> <td class="dvdlib_item">In</td> <td><%= link_to 'Check out', :action => 'checkout', :id => movie %></td> <td><%= link_to 'Details', :action => 'details', :id => movie %></td> <td><%= link_to 'Edit', :action => 'edit', :id => movie %></td> <td><%= link_to 'Remove', { :action => 'destroy', :id => movie }, :confirm => 'Are you sure?', :method => :post %></td> </tr> <% end %> </table> <%= link_to 'Previous page', { :page => @movie_pages.current.previous } if @movie_pages.current.previous %> <%= link_to 'Next page', { :page => @movie_pages.current.next } if @movie_pages.current.next %> <br /> <%= link_to 'Add new movie', :action => 'new' %>
We also need to create a form to add movies. The <%=
link_to 'Add new movie', :action => 'new' %>
uses
new.rhtml which in turn calls _form.rhtml. Open
_form.rhtml (app/views/library/_form.rhtml) and replace
the contents with the following:
<%= error_messages_for 'movie' %> <!--[form:movie]--> <p><label for="movie_title">Title</label><br/> <%= text_field 'movie', 'title' %></p> <p><label for="movie_created_on">Added to library on:</label><br/> <%= date_select 'movie', 'created_on' %></p> <!--[eoform:movie]-->
We've created an editable field for entering the movie title, and shown the current date stamp as the creation time.
Go ahead and add a few of your favourite movies. The "Check out" and "Details" links won't do anything yet, but you can Add, Edit, or Remove titles.
Rails has some built in JavaScript methods that provide some useful functionality. We could use one of these to make the Title field in our main library/list view editable.
Open list.rhtml (apps/views/library/list.rhtml) and
replace the <td class="dvdlib_item"><%=h
movie.title %></td>
line with the following:
<td class="dvdlib_item"> <% @movie = movie %> <%= in_place_editor_field :movie, :title %> </td>
The in_place_editor_field
requires an instance
name, so we need to assign the current value to
@movie
. If you type this in instead of pasting,
you'll notice autocompletion for the
in_place_editor_field
method.
Next, open library.rhtml
(apps/views/layouts/library.rhtml). Add the following line in the
<head>
section:
<%= javascript_include_tag :defaults %>
This line includes the default Rails JavaScript libraries
(<script src="/javascripts/...
) in the library
page allowing the title to be edited in place. Since the 'Edit'
link is now redundant, feel free to remove this line from
list.rhtml:
<td><%= link_to 'Edit', :action => 'edit', :id => movie %></td>
The field in the page is now editable, but the change is not savable until we make a change in library_controller.rb (app/controllers/library_controller.rb). Add the following on line 2, just below the LibraryController class declaration.
in_place_edit_for :movie, :title
Komodo Tip: Komodo has some useful tools for debugging AJAX calls. Check out the HTTP Inspector and JavaScript Debugger. |
On the main library list page, each movie has four actions associated with it: "Check out", "Details", and "Remove". "Remove" (and the "Edit" action that we just removed) is a generic web/database function that Rails provides which works well enough for our library application. "Check out" and "Details" are specific to our application and will have to be defined in our Controller (library_controller.rb).
Our check out function should do the following:
Here are the routines we need to add to
library_controller.rb. Put them at the bottom of the
file, just before the last end
.
def checkout @movie = Movie.find(params[:id]) end def do_checkout movie_id = params[:id] name = params[:person][:name] if name.blank? flash[:notice] = 'No name given' redirect_to :action => :checkout, :id => movie_id elsif (@name = Person.find_by_name(name)).blank? flash[:notice] = "Who is #{name}?" redirect_to :action => checkout, :id => movie_id else @movie = Movie.find(movie_id) checkout = Checkout.create(:movie_id => movie_id, :person_id => @name.id).save redirect_to :action => :list end end def register movie_id = params[:id] name_to_register = params[:person][:name] if name_to_register.blank? flash[:notice] = 'No name given' else person = Person.find_by_name(name_to_register) if person flash[:notice] = "#{person.name} is already registered" else Person.create(:name => name_to_register).save end end redirect_to :action => :checkout, :id => movie_id end
Now we need to create the checkout page. Right-click on the library folder (app/views/library) and select Add|New File.... Enter the filename checkout.rhtml and click Open. Add the following in the new file:
<h1>Checkout a movie</h1> <p>Title: <%= h @movie.title %></p> <% form_tag :action => 'do_checkout', :id => @movie do %> <p>Your name: <%= text_field_with_auto_complete(:person, :name) %> <%= submit_tag 'Check out' %> <% end %> <h2>Please register me</h2> <% form_tag :action => 'register', :id => @movie do %> <p><label for="movie_title">Your name</label><br/> <%= text_field 'person', 'name' %></p> <%= submit_tag 'Add me' %> <% end %> <%= link_to 'Back to the library', :action => 'list' %>
Notice the text_field_with_auto_complete(:person,
:name)
. This should give us autocompletion in the browser
based on people we've added to the library database. Currently
(even after adding a few people to the database) this won't
happen. In fact, the log output in the Webrick window shows
"ActionController::UnknownAction (No action responded to
auto_complete_for_person_name)
". As with the
in_place_editor_field
in list.rhtml, we
need to add something to our LibraryController class. Insert the
following into library_controller.rb at line 3:
auto_complete_for :person, :name
The field should now give autocompletion options when you start typing a name into it.
To change the main library list page to show whether a movie
is "In" or "Out". In list.rhtml, replace the placeholder
line '<td class="dvdlib_item">In</td>
'
with:
<% checkout = Checkout.find_by_movie_id(@movie) %> <td class="dvdlib_item"><%= Checkout.find_by_movie_id(@movie) ? "Out" : "In" %></td>
There is a bug in the do_checkout
function that
can be triggered by entering new (i.e. not registered) name in
the 'Checkout a movie' section of checkout.rhtml.
Entering a new name in the 'Your name' field section generates a
'No action responded to number' error. It should instead
give us a message saying "Who is name?" We can use
Komodo's debugger to
try to find the problem.
Komodo Tip: The Debugging Ruby documentation has information on configuring Komodo for local and remote Ruby debugging. If the steps below do not work automatically, check the configuration and settings described in the documentation or consult the online FAQs. |
Open library_controller.rb in an editor tab. Set a
breakpoint on the 'flash[:notice] = "Who is
#{name}?"
' line by clicking in the blank margin to the
left. An orange octagonal icon should appear indicating the
breakpoint.
The library_controller.rb tab should come to the
foreground in Komodo, with a yellow arrow over the breakpoint we
set earlier. While execution of the application is paused, we can
examine variables and interact with the application via the Ruby
shell. On the Locals tab on the left side of the
bottom pane, we can see the following variables from the
do_checkout
function:
Name | Type | Value |
checkout | null | |
movie_id | string | 1 |
name | string | NewName |
The movie_id
and name
variables are
as expected, but where does this checkout
variable
come from?
The line immediately below our breakpoint has the following:
redirect_to :action => checkout, :id => movie_id
The :action
is checkout
(an
undefined variable) rather than :checkout
(a symbol
for the checkout
function). Add the colon and the
checkout page should work as expected.
You can now leave WEBrick in debugging mode, or shut it down ('Ctrl'+'C' in the console) and restart it with the run server macro.
Now that we can check out a movie, we should add the ability
to return it. Currently we have a 'Check out' link. We can change
this so that it shows 'Check out' if the movie is in, and
'Return' if the movie is out. Replace the entire second
<tr>
section in list.rhtml with the
following:
<tr> <td class="dvdlib_item"> <% @movie = movie %> <%= in_place_editor_field :movie, :title %> </td> <% checkout = Checkout.find_by_movie_id(@movie) %> <td class="dvdlib_item"><%= checkout ? "Out" : "In" %></td> <td><% if checkout %> <%= link_to 'Return', :action => 'return', :id => movie %> <% else %> <%= link_to 'Check out', :action => 'checkout', :id => movie %> <% end %> </td> <td><%= link_to 'Details', :action => 'details', :id => movie %></td> <td><%= link_to 'Remove', { :action => 'destroy', :id => movie }, :confirm => 'Are you sure?', :method => :post %></td> </tr>
Here is the method we need to add to library_controller.rb:
def return movie_id = params[:id] checkout = Checkout.find_by_movie_id(movie_id) if checkout Checkout.delete(checkout) end redirect_to :action => :list end
The application is now mostly working. There's just one crucial component left to implement. The 'Details' link currently doesn't do anything (besides bringing up an "Unknown action" error). We want this page to show us who has the movie, and when it was checked out.
Right-click on the library folder (app/views/library) and select Add|New File.... Enter the filename details.rhtml and click Open. Add the following in the new file:
<h1>Checkout details</h1> <ul> <li>Movie: <%= h @movie.title %></li> <li>Borrower: <%= h(@person.name) %></li> <li>Borrowed: <%= @checkout_date %></li> </ul> </p> <%= link_to 'Return', :action => 'return', :id => @movie %> <%= link_to 'List', :action => 'list' %>
Of course, we'll need another method in library_controller.rb:
def details movie_id = params[:id] @movie = Movie.find(movie_id) checkout_id = params[:checkout] checkout = Checkout.find(checkout_id) if !checkout redirect_to :action => :list else @person = Person.find(checkout.person_id) @checkout_date = checkout.created_on end end
Lastly, we must pass a checkout id as a parameter when the
page is called, because we're viewing the checkout details as
well as the movie details. Modify the 'link_to
'Details'
line in list.rhtml to include the
following:
<td><% if checkout %> <%= link_to 'Details', :action => 'details', :id => movie, :checkout => checkout %> <% end %> </td>
Not only does this pass the checkout_id
parameter, it hides the 'Details' link if the movie is not
checked out.