Ruby on Rails Tutorial

Overview

Before You Start

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

Tutorial Scenario

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:

  1. Create a new Rails scaffold using the Ruby on Rails Komodo project template.
  2. Create a MySQL database.
  3. Create the core of the application using just the project macros.
  4. Write some code to iteratively develop the Movie Library application.
  5. Debug the application.

Creating a Rails Project

Komodo ships with a powerful template for Rails projects, containing several macros for:

To create the tutorial project file:

  1. Make a new directory/folder in a convenient location (e.g. C:\rails on Windows, or /home/username/rails on OS X or Linux).
  2. In Komodo, select File|New|New Project from Template.
  3. In the New Project dialog box, select the Ruby on Rails template from the Common category.
  4. Give the file a useful name. Since this tutorial creates a movie lending library, let's call it movielib.kpf.
  5. Specify your working directory (the one created in the first step) in the Directory field. Use the Browse button to find it if you wish.
  6. Click Open.

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 oncreate which run when a new project is created from the template. The oncreate macro in this Ruby on Rails template runs the command 'rails . --force' in the current working directory to create the project.

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.

Creating the Database

Editing the database.yml file

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:

  1. Open the Config project folder and double-click database.yml to open it in an editor tab.
  2. As necessary, modify the 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.
  3. If you are running MySQL on a remote server, add the setting 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.

Running the Create Databases Macro

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.

Generating Models

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.

The Movie Model

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).

The Person Model

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.

The Checkout Model

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

Migration

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.

Creating a Scaffold

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.

Starting the Webrick Server

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.

Listing and Adding Movies

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.

Editing a Title in Place

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.

Check Out a Movie

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>

Debugging Rails Komodo IDE only

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.

  1. Stop the WEBrick server by entering 'Ctrl'+ 'C' in the terminal window running the server.
  2. Start the debugging version of the server by double-clicking the debug rails app macro. By default, the debugger will stop on the first line of executable code, in this case line 2 of script/server.
  3. Click Go/Continue in the Debug menu ('F5') or the Go/Continue button in the toolbar.
  4. In your browser, navigate to http://localhost:3000/library/checkout/1 (or the checkout page for the movie of your choice).
  5. Enter a new name in the 'Your name' field of the 'Checkout a movie' section (one that is not already in your database).

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.

Returning a movie

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

Viewing Checkout Details

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.

Rails Resources

Tutorials and Reference Sites