In This Article:

It has been just over a year since the public debut of Ruby on Rails on July 25, 2004. In this short time, Rails has progressed from an already impressive version 0.5 to an awe-inspiring, soon-to-be-released version 1.0 that managed to retain its ease of use and high productivity while adding a mind-boggling array of new features. This article introduces the components of the upcoming Ruby on Rails 1.0 and shows you what the fuss is all about.

High Productivity and Reduced Development Time

At the feature level, Ruby on Rails doesn't offer anything new. Existing web application frameworks have done it all before. What's the big deal, then? The difference lies in how Ruby on Rails does it. When you can finish simple web apps in days instead of weeks and more complicated web apps in weeks instead of months, people notice!

This newfound attention would be short-lived if the resulting web apps were messy and hard to maintain or extend. Fortunately Ruby on Rails actually facilitates good programming practices, which leads to well-factored and easily maintained code.

The attention would also be short-lived if Ruby on Rails had no depth--that is, if once you tried to use it for anything beyond the simplest of web applications, you suddenly found yourself hitting a wall, unable to proceed due to inherent limitations. Experienced developers who know their way around the Web have repeatedly reported that this is not the case with Rails. For example, Tomcat, Ant, and the Servlet API author James Duncan Davidson recently wrote:

Rails is the most well thought-out web development framework I've ever used. And that's in a decade of doing web applications for a living. I've built my own frameworks, helped develop the Servlet API, and have created more than a few web servers from scratch. Nobody has done it like this before. That's not to say they got it all right. It's by no means "perfect". I've got more than a few nits and picks about how things are put together. But "perfect" isn't the point. The point is that it gets you up and going fast and has plenty of depth to keep you going. And Rails does that very well.

It may be hard to believe that this is possible without a significant downside. Fortunately, you don't have to take my word for it (or anyone else's). You can easily prove it to yourself in a day or less by going through a Ruby on Rails tutorial and then developing a modest web application of your own choosing. After all, seeing is believing! If you don't want see yourself be amazingly productive, you can always watch someone else do it in the new Rails video.

How Does Rails Do It?

Like a good recipe, Rails helps you achieve this new level of productivity by combining the right ingredients in the right amounts. Here are a few of the most important ingredients that make Rails what it is.

Ruby

Much of the power of Rails comes from the Ruby programming language. Ruby's unique design makes it easy to create domain-specific languages and to do metaprogramming. Rails takes full advantage of this.

Full-stack MVC framework

Rails is an MVC (model, view, controller) framework where Rails provides all the layers and they work together seamlessly. Other frameworks often implement only part of the solution, requiring the developer to integrate multiple frameworks into the application and then coerce them into working together. (For example, a Java developer might use Hibernate, Struts, and Tiles to get full MVC support.)

Less code

Following the simple Rails programming conventions does more than just eliminate the need for configuration files. It also means that Rails can automatically handle myriad lower-level details without you having to tell it to do so. This means that you write fewer lines of code to implement your application. Keeping your code small means faster development and fewer bugs, which makes your code easier to understand, maintain, and enhance.

Generators

Rails' use of runtime reflection and metaprogramming eliminates much of the boilerplate code that you would otherwise have to create. You can often avoid what little boilerplate code remains by using the built-in generator scripts to create it for you. This leaves you with more time to concentrate on the code that really matters--your business logic.

Zero turnaround time

The typical development cycle for testing a change to a web app has steps such as configure, compile, deploy, reset, and test. This is very time consuming. The Rails development environment has none of this. You simply make a change and see it work. Don't make the mistake of dismissing this as a minor point. It's hard to overstate how much this improves productivity and helps you maintain a creative flow without interruption.

Scaffolding

Rails can automatically create a full set of CRUD (Create, Retrieve, Update, and Delete) operations and views on any database table. This scaffolding can get you up and running quickly with manipulating your database tables. Over time, you can incrementally replace the generated CRUD operations and views with your own--presumably much prettier and more functional.

Rails Components

Rails itself consists of several components, which you can install and use separately. They are designed to work together seamlessly, though, and developers almost always use them together:

Action Pack is the component that implements both the view and controller portions of the MVC architecture. The controller part handles incoming requests from the user's browser and routes them to the correct method of a controller class. The view part builds the response to send back to the browser using a templating system similar to that of ASP or JSP.

Prototype is the component that implements the Ajax, drag-and-drop, and visual effects within your web pages.

Action Mailer is the component that handles the sending and receiving of email.

Action Web Service is the component that makes it easy to add web service APIs to your web application. Action Web Service supports SOAP, XML-RPC, and WSDL.

General Features

Rails has some general and some specific characteristics.

Web servers

Rails can run on just about any web server that implements CGI. However, the performance of CGI is notoriously bad, so the preferred deployment of a Rails application is to use FastCGI. There have been extensive tests of Rails application deployments with both Apache and LightTPD. There is also a newcomer, SCGI, which rivals the performance of FastCGI without the complicated setup.

During development, it is usually easiest just to use the WEBrick web server that comes built-in to Ruby.

Databases

Rails currently contains support for the following databases:

MySQL

PostgreSQL

SQLite

SQL Server

DB2

Oracle

It takes approximately 100 lines of Ruby code to implement a database adapter, so adding to this list is not particularly onerous.

Debugging

When something goes wrong inside your Rails web app, you normally get a pretty detailed error display in your browser (when running in development mode). Often this is enough to diagnose the problem. When it's not, you have other options:

Custom (pretty) URLs

The default Rails mapping of URLs to controller actions is very simple and easy to understand. Rails tries very hard to present the user with pretty URLs. Rails URLs are simple and straightforward, not long and cryptic.

Even so, you can still customize your URLs by using the Rails routing facility. Rails' URL routing is flexible enough to allow you to create virtually any URL mapping scheme.

The Rails routing facility is pure Ruby code that even allows you to use regular expressions. Because Rails does not use the web server's URL mapping (like mod_rewrite in Apache), your custom URL mapping will work the same on every web server.

Active Record

Active Record is the part of Rails that handles the automatic mapping of your database tables to your runtime model objects. It's the M in MVC, and it is Rails' implementation of an ORM layer.

For all the common uses (and some of the not-so-common ones), you'll never need to see or use SQL when accessing or updating your database. Active Record's goal is specifically to work with relational databases; it does not try to abstract away its SQL usage. Active Record makes it easy to use your own custom SQL for those complicated cases where it is necessary. Even so, it is rarely needed.

Automated mapping

Active Record automatically maps tables to classes, rows to objects (instances of the model classes), and columns to object attributes. For example:

Active Record uses English pluralization rules to map classes to tables. The model class name is singular and capitalized, while the table name is plural and lowercased. Examples include:

An Invoice model class maps to an invoices table.

A Person model class maps to a people table.

A Country model class maps to a countries table.

A SecurityLevel model class maps to a security_levels table.

This singular/plural convention results in code that reads fairly naturally. Notice how this mapping is intelligent in its use of English pluralization rules. Also note that the class names use CamelCase (a Ruby convention), while the table names are all lowercase with underscores between words.

In cases where this does not work (such as interfacing with a legacy database with which you have no control over the names), you can also explicitly tell Active Record what name it should use.

The ActiveRecord::Base documentation explains more about Active Record's automatic mapping.

Associations

No table stands alone. Well, not usually, anyway. Most database applications use multiple tables with specific relationships between those tables. You can tell Active Record about these relationships in your model classes, and Active Record will generate a slew of navigation methods that make it easy for your code to access related data. The following models:

Validation

Because you don't want to store just any old thing in your database, you probably want to validate your data before you store it. Active Record contains a suite of macrolike validators that you can add to your model.

If the validate method exists, Rails will call it just before writing any object to the database. If validation fails, it does not write the object to the database. validate_on_create and validate_on_update are similar, except that the first is called only before Rails creates a new record in the database, while the second is called only when Rails is about to update an existing record.

You can also validate a particular attribute only when some condition is true.

Callbacks

As Active Record creates and destroys model objects and creates and updates them in the database, you can monitor these events in the object's life cycle using callbacks. You can use callbacks to handle complex business logic, modify data before Rails writes it to the database (or after Rails reads it from the database), or just about anything else you like.

For example, the save method that saves a model object's data to the database has eight callbacks defined:

before_validation

before_validation_on_create

after_validation

after_validation_on_create

before_save

before_create

after_create

after_save

This gives you fine-grained control over your model objects when you need it.

Transactions

A transaction is necessary when you have multiple database operations that all must succeed before the data in the database can change. If any one of them fails, the data in database should not change. Use transaction blocks to ensure this.

transaction do
david.withdrawal(100)
mary.deposit(100)
end

The database-level transaction shown above will prevent the withdrawal from David's account if the deposit into Mary's account should fail. It will not, however, protect the david and mary objects from being modified. To do that, you must use object-level transactions.

Account.transaction(david, mary) do
david.withdrawal(100)
mary.deposit(100)
end

Any failure in this code will roll back the value of the objects as well as the database.

Much, much more

There is a lot more to Active Record than I can cover here. To learn more, consult the Active Record API.

Action Pack

Action Pack implements both the view and controller part of Rails.

View templates

View templates specify the HTML to return in response to a browser request. View templates are rhtml files (HTML with embedded Ruby) that are very similar to ASP or JSP files. Text within <% %> is Ruby code to execute, and text within <%= %> is also Ruby code to execute and substitute the results back into the HTML.

By default, Rails will try to find a template whose name matches the currently executing action. If, for example, Rails executes an edit action in your InvoiceController, then it will attempt to find and render the view template .../app/views/invoices/edit.rhtml.

You can also build up XML (or HTML) output programmatically in your controller action. This is useful, for example, for building RSS feeds or responding to XML-RPC requests. In the following example, xm is an XmlMarkup object.

URL routing

An incoming URL always maps to some action within a controller. A controller is simply a Ruby class, and each action implemented by the controller is a public method within the controller class. The default mapping from URL to action method is (in "Rails-speak"):

/:controller/:action/:id

This is easiest to explain by an example. If Rails received the URL http://myapp.com/invoices/show/37, Rails would route this to a controller class named InvoicesController and within that class to a method named show. Rails would also pass the 37 to the method as the id member of the parameter hash that also holds the values of query parameters and form fields. The code might look like this:

class InvoicesController
def show
@invoice = Invoice.find(params[:id])
end
end

Because actions are methods grouped within a controller (instead of separate command objects), they can easily share helper methods.

If the default URL routing does not meet your needs, you can easily specify your own routing rules, even using regular expressions. Because Rails implements its own URL routing, you don't need to mess with Apache's mod_rewrite, and your routing rules will work the same under all web servers.

This rule decomposes a URL containing a date that, perhaps, a blog might use to display the postings for a particular date. A URL that matches this form will map to the BlogController class and the by_date method. The parameter hash will contain values for a four-digit year (/\d{4}/ is a Ruby regular expression), a two-digit month, and a two-digit day. Further, the month and day are optional; if no values are present, the parameter hash will contain the default value of nil.

Filters

Filters allow you to run preprocessing code before Rails executes an action and post-processing code after it completes an action. They are useful for such things as caching or authentication before calling the action, and compression or localization of the response after calling an action. The before_filter processing can either allow the action to be called normally by returning true, or abort the action by returning false (or by performing a render or redirect operation).

For example:

class BankController < ActionController::Base
before_filter :audit
private
def audit
# record the action and parameters in an audit log
end
end
class VaultController < BankController
before_filter :verify_credentials
private
def verify_credentials
# make sure the user is allowed into the vault
end
end

Helpers

Helpers are smart methods (functions) that help your view templates generate HTML. They know to use your model objects and controller classes to create just the right HTML and, in the process, save you a lot of time and effort. Of course, this also means you write fewer lines of code, but I bet you already guessed that.

You can write your own helpers, but as you might expect, several come built into Rails. The link_to helper, for example, generates anchor tags that create links to controllers and actions. For example:

<%= link_to "Help", { :action => "help" } %>

creates a link to the help action (method) in the current controller (whatever controller is handling the current request). The text of the link (what the user sees) is Help.

This creates a link to the delete method in the InvoicesController class, and passes an id parameter (presumably the ID of the invoice to delete). This also uses a special confirm option that creates JavaScript to pop up a confirmation dialog letting the user continue or abort.

There is a substantial set of helpers for creating form fields to display and update values in your Active Record model objects, which effectively means values in your database. Assume that your database has a people table with columns for the name, the password, a description, and a Boolean value to indicate whether the person is single or married (OK, so this is a strange table--just humor me). Here's a partial view template with a sampling of form tags that could be used. (Assume that the variable @person contains a person object read from a row of the people table.)

Ajax & JavaScript helpers

Rails has a simple, consistent model for how it implements Ajax operations. Once the browser has rendered and displayed the initial web page, different user actions cause it to display a new web page (like any traditional web app) or trigger an Ajax operation:

A trigger action occurs. This could be the user clicking on a button or link, the user making changes to the data on a form or in a field, or just a periodic trigger (based on a timer).

The client sends data associated with the trigger (a field or an entire form) asynchronously to an action handler on the server via XMLHttpRequest.

The server-side action handler takes some action based on the data, and returns an HTML fragment as its response.

The client-side JavaScript (created automatically by Rails) receives the HTML fragment and uses it to update a specified part of the current page's HTML, often the content of a <div> tag.

The real beauty is how easy Rails makes it to implement all of this in your web application. The following simple example adds new items to a list:

Layouts

Layouts let you specify a common set of display elements for every page rendered by a controller. This is typically useful for common headers, footers, and sidebars. By default, Rails looks in its layouts directory for an rhtml file whose name matches the controller's name. A layout template might look like this:

Rails will substitute the HTML that an action renders into the above layout where it says @content_for_layout.

The controller can also directly specify the name of the layout template to use for all its actions. This makes it easy to use the same layout for multiple controllers. You can even dynamically choose a layout template at runtime. For example, you could use one layout for logged-in users and a different one for anonymous users.

Components and partials

Components and partials allow you to modularize your view templates.

The simplest are partials, which allow you to extract a common piece of a template into a separate file and then render it from many other templates (or many times within a single template). Partial templates always have a leading underscore on their filenames to distinguish them from full templates.

This renders the partial template _adview.rhtml multiple times (once for each ad in the collection @advertisements). For each rendering, Rails will pass _adview.rhtml a local variable named item that contains the ad object to use.

Components are similar to partials in that they embed the rendering of another template within the current template. The difference is that you specify the name of a controller and action, and its template is the one to render and insert into the current template.

Scaffolding

Scaffolding allows you to get an instant implementation of CRUD (Create, Retrieve, Update, and Delete) operations on any database table. They're not pretty, but they do give you immediate web-based access to your tables. Over time, you can incrementally replace the generated CRUD operations and views with your own.

Rails supports static or dynamic scaffolding. Static scaffolding physically generates model, controller, and template files. This lets you see how it works and start tweaking this existing code. To generate static scaffolding, navigate in the file system to the root of your Rails web application and run a command similar to:

$ ruby script/generate scaffold invoices

This will look for an invoices table in your database and use its schema to generate the model, the controller, all of the view templates, the unit test skeletons, and more. The command displays a list of all the files that it generates.

The advantage of static scaffolding is that you get to see and modify the generated code. The disadvantage is that if you change you database table, the scaffolding will not reflect those changes. However, if you haven't modified the scaffolding, you can just regenerate it.

Dynamic scaffolding does not generate any files. Rails simply creates what it needs dynamically as your web app runs. This means that every time you change the database table, the scaffolding's CRUD views immediately show those changes. To request dynamic scaffolding, place a single line of code in the controller:

Parting Thoughts

You can usually divide web application frameworks and the developers who use them into two distinct categories. At one end of the spectrum, you have the heavy-duty frameworks for the "serious" developers, and at the other end you have the lightweight, easy-to-use frameworks for the "toy" developers. Each of these groups generally regards the other with disdain.

One of the most interesting things is that Rails is attracting developers from both camps. The high-end developers are tired of the repetitive, low-productivity routine that they have been forced to endure, while the low-end developers are tired of battling a mess of unmanageable code when their web apps move beyond the simple. Both of these disparate groups find that Rails provides sustainable relief for their pain. I don't know about you, but I find this quite remarkable!

At the moment, Ruby on Rails barely captures a tiny percentage of web development projects. Yet it is rapidly gaining mind share, and many respected software development leaders have been testing the waters with Rails and publicly singing its praises.

Perhaps it's time that you too checked out Rails to see firsthand what the fuss is all about.

Acknowledgments

Most of the sample source code shown in this article came from the Rails API documentation, with permission.

Resources

Instant Rails, a one-stop
Rails runtime solution containing Ruby, Rails, Apache, and MySQL, all
preconfigured and ready to run. There's no installer; you simply drop it into
the directory of your choice and run it. It does not modify your system
environment.

The Locomotive project is
a quick start to Ruby on Rails development on Mac OS X, providing a sandboxed,
no configuration, one-folder install of Ruby on Rails, SQLite,
Lighttpd/Fastcgi, and much, much more.

Curt Hibbs
has been a consultant to well-known companies like Hewlett Packard, Intuit, Corel, WordStar, Charles Schwab, Vivendi Universal, and more. He now works as a Senior Software Engineer for The Boeing Company in St. Louis.