Java

Rails by Example

The best way to understand Rails is to see it in action. Go to http://rubyforge.org and download Ruby and RubyGems . (If you use the Windows one-click installer, you'll get RubyGems with that distribution.) If you don't already have one, download a relational database manager, too. I used MySQL. You'll begin to get the Rails experience at install time. RubyGems lets you install Ruby applications and their dependencies. At the command line, type:

    gem install rails -v 0.12.1

Ruby will start the installation process. It goes up to RubyForge (rubyforge.org) and pulls down an index including the appropriate version of Rails and its dependencies. If you were to omit the version number, Ruby would get you the latest stable version. RubyGems will then prompt you for each dependency. Answer "Y," or answer "a" once for all dependencies:

    Attempting remote installation of 'rails'
    Updating Gem source index for: http://gems.rubyforge.org
    Install required dependency rake? [Yn]  Y
    Install required dependency activesupport? [Yn]  Y
    Install required dependency activerecord? [Yn]  Y
    Install required dependency actionpack? [Yn]  Y
    Install required dependency actionmailer? [Yn]  Y
    Install required dependency actionwebservice? [Yn]  Y
    Successfully installed rails, version 0.12.1

You'll notice that RubyGems will then attempt to build the documentation for each of the subcomponents and Rails. And that's it. Rails is installed. You're already getting hints about the approachability of Rails.

Generating a Basic Application

You can now generate a Rails project. Go to your working directory and ask Rails to generate a project called trails:

    rails trails

Ruby creates a full directory structure that will contain your application. There's no guesswork, and all Rails projects will have a consistent format. I'll point out a few important directories:

app

This directory has your application code. You'll see a directory for each component of MVC and a couple of others.

config

This directory will be very light. You'll put in anything that needs special configuration, like the connection parameters for your database. Since Ruby makes excellent use of defaults, your config directory will stay sparse.

script

Your trails app comes with scripts that will help you generate code, and start your application server.

You'll notice a few other goodies as well, but for now, let's use one of the scripts to start Ruby's application server. Change to the trails directory, and type:

    ruby script/server

If things are working, you'll see a server started on port 3000. You can go to http://127.0.0.1:3000/ to make sure things are running. You'll get a Rails welcome message. You just started a development Ruby web server, configured for Rails. If you need to change some properties of the server, you'll just change the script/server script. Notice that Ruby programmers typically do configuration, like this server script, in Ruby scripts. You've already learned that Ruby handles structured data well, without XML. For example, this is the part of the server script that has the configuration options:

    OPTIONS = {
      :port        => 3000,
      :ip          => "0.0.0.0",
      :environment => "development",
      :server_root => File.expand_path(File.dirname(_ _FILE_ _) + "/../public/"),
      :server_type => WEBrick::SimpleServer
    }

This code simply defines a hash map called OPTIONS. The => operator maps keys on the lefthand side to values on the right. Nothing has really happened yet, but you should be paying attention. You've set up a whole lot of infrastructure in a very short time.

Our trails project will collect descriptions of mountain bike trails. We'll start simple, collecting an ID to go with a trail name, description, and difficulty. You'll type the field names once. The Rails metaprogramming features will read the columns from the database and dynamically add properties to your model objects. If you're using MySQL, you can fire up the mysql command processor. Create a database called trails and switch to it. Now, create a table called TRails:

    mysql> CREATE TABLE trails (
        ->   id int(6) NOT NULL auto_increment,
        ->   name varchar(20),
        ->   description text,
        ->   difficulty varchar(20),
        ->   primary key (id));
    Query OK, 0 rows affected (0.36 sec)

Notice the names. They are important. By convention, if you're working with more than one row (as in a table or a list), the name should be a plural. A column or class that refers to a singular object should be singular. Rails is smart enough to translate English plurals, so it knows to create a model called Person for a table called people. Watch the capitalization in these examples, too. It's important. If you follow Rails conventions with your names, you can just stay with Rails defaults, and your code will be much more concise.

You'll need to tell Rails where to find your database. Edit config/database.yml to look like this:

    development:
      adapter: mysql
      database: trails
      host: localhost
      username: root
      password: password

Stop and restart the server. (You only have to do so when you change your database configuration.) Let's generate a simple model. In the trails directory, simply type:

    ruby script/generate model trail

Rails generates the model, some helper files, tests, and fixtures. For example, you can take a look at the model. Edit the file at app/models/trail.rb:

    class Trail < ActiveRecord::Base
    end

That certainly looks anticlimactic. It looks like you'll simply type custom code here, in hopes that Rails will generate the rest of the code somewhere else. But that's not what happens at all. At runtime, Rails will load the ActiveRecord base class. Rails will look at the name of the class and load the definition of a table called trails. Then, it will dynamically add attributes, getters, setters, and database access methods to the trail base class! So, there's a lot more than meets the eye.

One of the scripts that Rails generates lets you manipulate your model from an irb session. Type:

    ruby script/console

You can now easily manipulate your model. For example, you can say:

    Trail.new do |trail|
      trail.name="Walnut Creek"
      trail.description="Meandering trail in Austin park"
      trail.difficulty="hard"
      trail.save
    end

Now, you'll need a controller. You can generate that, too:

    ruby script/generate controller trails
    ruby script/generate model trails

You just created the model and a default controller in app/controllers/trails_controller.rb for a collective page of trails. When you edit it, the controller is empty. Make it look like this:

    class TrailsController < ApplicationController
      def index
        render_text "This will be a trail someday..."
      end
    end

Point your browser to the URL http://localhost:3000/trail. You'll see your message printed. Of course, you didn't learn Rails to print strings, so change your controller to this:

    class TrailsController < ApplicationController
      scaffold :trails
    end

scaffold is a method. The first parameter is :trails, a literal pointing to the TRails class. Save it, and load the same URL. Now, that's more interesting. You see a listing of trails. Click on the new trail link on the bottom. That's beyond interesting! You'll get a form like the one shown in Figure 7-1. You can see that the metaprogramming framework is working overtime. The scaffold method inferred the properties of the database and propagated them through the model and up to the user interface. You'll see exactly what goes into a scaffold controller later, but trust the magic for now.

Managing Relationships and Updating Views

A list of trails will not get you very far by itself. It's the interactions between objects that gets difficult. Say you want to access trails by their city. The first job is to generate the model for locations. First you'll need a database table:

    mysql> CREATE TABLE locations (
        ->   id int(6) NOT NULL auto_increment,
        ->   city varchar(20),
        ->   state varchar(20),
        ->   primary key (id));
    Query OK, 0 rows affected (0.35 sec)

Instead of dynamically generating the scaffolding, you can simultaneously generate the source code for the controller, and view, complete with scaffolding with ruby script/generate scaffold locations. Build the model for a single location with ruby script/generate model location. While you're at it, just to get a better look at what's going on behind the curtains, do the same for trail with ruby script/generate scaffold trails. Look at what you've done by pointing your browser to http://localhost:3000/locations. Make sure it works, and add a few locations. I'm adding Austin, Texas, and Durango, Colorado.

It's time to write some code ourselves, instead of letting Rails do all the work. You're going to need to update your trails table to point to the right row in the new locations table. You'll do so by adding a new database column that points to location_id, like this:

    alter table trails add location_id int(6);

You also need to tell Rails about the relationship. Modify the trails model and the Location model to reflect the new relationships, like this:

    class Trails < ActiveRecord::Base
      belongs_to :location
    end

    class Locations < ActiveRecord::Base
      has_many :trails
    end

Figure 7-1. This application has less than 10 lines of code and configuration, because Rails inferred the structure from the database

A little description here is interesting. You've created a subclass of the ActiveRecord class in the Base module. You've then fired a method called belongs_to and passed it a symbol for the Locations class. This method will fire more metaprogramming code that actually adds the properties and methods to your code that will manage the relationships for you.

Next, you're going to have to edit the trails view and controller to edit a location. The scaffolding created the new controllers and views under trails and locations, respectively.

It's time to modify some of the view code. The view code consists of HTML, with Ruby scripting mixed in, between the <% and %> tags. First, you'll need to make sure the view has all the information it needs. You'll do this in the edit method, in the controller. Change the edit method in trails_controller.rb to create a property called @locations that has all the locations:

    class TrailsController < ApplicationController
    ...
      def edit
        @trail = Trail.find(@params[:id])
        @locations = Location.find_all
      end
    ...
    end

It's also time to take over the full view that lets you edit a trail. You'll want the user to pick a location from a pick list with all possible locations. Change app/views/trails/edit.rhtml to look like this:

    <html>
     <head><title>Edit a Trail</title></head>
     <body>
       <h1>Edit Trail</h1>

     <form action="../update" method="POST">
      <input id="trial_id" name="trail[id]" size="20"
             type="hidden" value="<%= @recipe.id %>" />
      <p><b>Name</b><br>
      <input id="trail_name" name="trail[name]" size="20"
             type="text" value="<%= @trail.name %>" />
      </p>
      <p><b>Location:</b><br>

      <%= collection_select("trail", "location_id", @locations, "id","city") %>

      <p><b>Description</b><br>
      <textarea cols="40" id="trail_description"
                name="trail[description]"
                rows="20" wrap="virtual">
        <%= @trail.description %>
      </textarea> </p>
      <input type="submit" value="Update" />
     </form>

     <a href="/trail/show/<%= @trail.id %>">
       Show
     </a> |
      <a href="/trail/list">
      Back
     </a>

     </body>
    </html>

As with most applications, your scaffolding won't hold up infinitely. Often you'll want to replace most of the view code. Rails lets you build toward the goal, instead of creating all of a model, view, and controller right off the bat.

Notice the code in bold. It adds an option value for all the locations (which you specified in the edit method of the controller), and selects the one that matches the one that reflects the model, shown in the variable trails.location.city.

Finally, you'll need to show the new data in the trail list, and in the show method. The idea is exactly the same. Add a line to the show.rhtml view right above the links on the bottom of the page:

    <p>
      <b>Location:</b> <%=h @trail.location.city %>
    </p>

That's pretty simple. You're just getting the location from the model passed in by the controller. The list view uses the same technique. You can edit the table from the app/views/trails/list view:

    <table>
      <tr>
        <th>Name</th>
        <th>Location</th>
      </tr>

    <% for trail in @trails %>
      <tr>
        <td><%= trail.name %></td>
        <td><%= trail.location.city %></td>
        <td><%= link_to 'Show', :action => 'show', :id => trail %></td>
        <td><%= link_to 'Edit', :action => 'edit', :id => trail %></td>
        <td><%= link_to 'Destroy', {:action => 'destroy', :id => trail}, :
    confirm => "Are you sure?" %></td>
      </tr>
    <% end %>
    </table>

Figure 7-2 shows you the result, with the location of each trail in the main list. Keep in mind that all trails have to have locations. If one of yours doesn't, you will get an error here.

This tutorial has already gone on long enough, but I hope you can appreciate the power and flow of Rails development. You can quickly get your application rolling, because Rails discovers your application structure from

Figure 7-2. This list comes from an application that allows you to view and update a database, with trails in one table and locations in another

the database design. You then turn changes around quickly, because the feedback cycle requires you only to code/reload. You're building quality beyond what PHP can give you, because you're building with a proven model/view/controller design pattern, with built-in features for logging, caching, and automated testing. Now that you've seen what Rails can do, take a look under the hood to see some of this magician's secrets.

by BrainBellupdated
Advertisement: