Rolling with Ruby on Rails, Part 2
Pages: 1, 2, 3

Showing recipes in a category

The final task is to add the ability to display only those recipes in a particular category. I'll take the category displayed with each recipe on the main page and turn it into a link that will display only the recipes in that category.

To do this, I'll change the recipe list view template to accept a URL parameter that specifies what category to display, or all categories if the user has omitted the parameter. First, I need to change the list action method to retrieve this parameter for use by the view template.

Edit c:\rails\cookbook\app\controllers\recipe_controller.rb and modify the list method to look like this:

def list
    @category = @params['category']
    @recipes = Recipe.find_all
end

Then edit c:\rails\cookbook\app\views\recipe\list.rhtml to look like this:

<table border="1">
 <tr>
   <td width="40%"><p align="center"><i><b>Recipe</b></i></td>
   <td width="20%"><p align="center"><i><b>Category</b></i></td>
   <td width="20%"><p align="center"><i><b>Date</b></i></td>
 </tr>

 <% @recipes.each do |recipe| %>
   <% if (@category == nil) || (@category == recipe.category.name)%>
     <tr>
      <td>
        <%= link_to recipe.title, 
                   :action => "show", 
                   :id => recipe.id %>
        <font size=-1>
           
        <%= link_to "(delete)", 
                    {:action => "delete", :id => recipe.id},
                    :confirm => "Really delete #{recipe.title}?" %>
        </font>
      </td>
      <td>
        <%= link_to recipe.category.name, 
                    :action => "list", 
					:category => "#{recipe.category.name}" %>
      </td>
      <td><%= recipe.date %></td>
     </tr>
   <% end %>
 <% end %>
</table>

There are two changes in here that do all the work. First, this line:

<% if (@category == nil) || (@category == recipe.category.name)%>

decides whether to display the current recipe in the loop. If the category is nil (there was no category parameter on the URL), or if the category from the URL parameter matches the current recipe's category, it displays that recipe.

Second, this line:

<%= link_to recipe.category.name, 
    :action => "list", 
    :category => "#{recipe.category.name}" %>

creates a link back to the list action that includes the proper category parameter.

Browse to http://127.0.0.1:3000/recipe/list and click on one of the Snacks links. It should look like Figure 11.

showing only snacks
Figure 11. Showing only snacks

What is it? How long did it take?

That's it! This is a reasonably functional online cookbook application developed in record time. It's a functional skeleton just begging for polish.

Wading through all of the words and screenshots in this article may have obscured (at least somewhat) exactly what this code can do and in what amount of developer time. Let me present some statistics to try to put it all into perspective.

Fortunately, Rails has some built-in facilities to help answer these questions. Open up a command window in the cookbook directory (c:\rails\cookbook) and run the command:

rake stats

Your results should be similar to Figure 12. Note that LOC means "lines of code."

viewing development statistics
Figure 12. Viewing development statistics

I won't give a detailed description of each number produced, but the last line has the main figure I want to point out:

Code LOC: 47

This says that the actual number of lines of code in this application (not counting comments or test code) is 47. It took me about 30 minutes to create this application! I could not have come even close to this level of productivity in any other web app development framework that I have used.

Maybe you're thinking that this is an isolated experience using an admittedly trivial example. Maybe you're thinking that this might be OK for small stuff, but it could never scale. If you harbor any such doubts, the next section should lay those to rest.

Ruby on Rails Success Stories

Rails is a relatively young framework. As of this writing, it's been barely six months since the first public release. Yet it debuted with such a stunning feature set and solid stability that a vibrant developer community quickly sprang up around it. Within this time frame, several production web applications have been deployed that were built with Ruby on Rails.

Basecamp

From the site itself:

Basecamp is a web-based tool that lets you manage projects (or simply ideas) and quickly create client/project extranets. It lets you and your clients (or just your own internal team) keep your conversations, ideas, schedules, to-do lists, and more in a password-protected central location.

Basecamp was the first commercial web site powered by Ruby on Rails. David Heinemeier Hansson, the author of Rails, developed it. At its deployment, it contained 4,000 lines of code with two months of development by a single developer. In fall 2004, Basecamp stated that it had passed the 10,000-user mark. It considers the actual number of registered users to be proprietary information, but the home page currently states that it has "tens of thousands" of users.

43 Things

43 Things is a goal-setting social software web application. It currently has 6,000 registered users and hundreds of thousands of unregistered visitors. 43 Things has 4,500 lines of code that were developed in three months by three full-time developers.

Ta-da Lists

Ta-da Lists is a free online service that implements simple, sharable to-do lists. It features a highly responsive user interface that uses XMLHttpRequest to minimize waiting for the server. Ta-da Lists came from one developer using one week of development time producing 579 lines of code.

Snow Devil

Snow Devil is an e-commerce site specializing in snowboards and related equipment. It opened for business only recently, so there is no usage information available at this time. However, it comprises 6,000 lines of code created by two developers in four months.

CD Baby Rewrite

CD Baby is a very successful e-tailer of independent music. In business since 1998, it lists 82,443 artists that together have sold 1.2 million CDs, paying $12 million back into the artists' pockets.

The CD Baby web site previously involved an increasingly unmanageable 90,000 lines of PHP code. Its authors are in the process of rewriting it in Ruby on Rails. It's too early to find any development information, but the owner of CD Baby is publicly blogging about the process and progress of the conversion.

What does this all mean?

When all is said and done, good design will be more important than the framework in determining how your application performs. Think carefully about your database design and how its tables are indexed. Analyze your data access patterns and consider some strategic denormalization of data. Look for opportunities to cache preprocessed data.

Rails has a lot of powerful features to make it easy to prototype and develop applications quickly, which will leave you with more time to think about your application's features and how to tune it for performance.

A Smattering of Ruby on Rails' Features

Rails has many features that I have not used in this two-part article. I'd like to mention a few of them (with links to more information) to give you a more rounded view of the Rails toolkit.

Caching

Caching is cheap way to speed up your application by saving the results of previous processing (calculations, renderings, database calls, and so on) so as to skip the processing entirely next time. Rails provide three types of caching, in varying levels of granularity:

  • page caching
  • action caching
  • fragment caching

Validation and callbacks

To make sure your data is correct and complete before writing it to the database, you must validate it. Rails has a simple mechanism that allows your web application to validate a data object's data before the object updates or creates the appropriate fields in the database. Read the validation how-to or go straight to the validation API documentation.

ActiveRecord callbacks are hooks into the life cycle of a data object that can trigger logic before or after an operation that alters the state of the data object.

Transactions

ActiveRecord also supports transactions. Quoted straight from the documentation:

Transactions are protective blocks where SQL statements are only permanent if they can all succeed as one atomic action. The classic example is a transfer between two accounts where you can only have a deposit if the withdrawal succeeded and vice versa. Transaction enforce the integrity of the database and guards the data against program errors or database break-downs. So basically you should use transaction blocks whenever you have a number of statements that must be executed together or not at all.

For example, consider the code:

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

Testing

Rails was built with testing in mind and provides support for testing your web application. An extensive online tutorial shows how to test a Rails web application.

Generators

Generators are the helper scripts that you can use to generate code for your application. You have already used generators to create new controllers and models, and at the beginning of this article I showed you how to use a new generator to create scaffolding.

Rails also supports user-created add-on generators. For example, Tobias Luetke has written a Login Generator that creates all the code for easily adding authentication, users, and logins to your Rails app.

Security

Related Reading

Programming Ruby
The Pragmatic Programmer's Guide, Second Edition
By Dave Thomas

By now, everyone should know the importance of good security in web applications. The Ruby on Rails web site has an online security manual that describes common security problems in web applications and how to avoid them with Rails.

Parting Thoughts

Rails is not your run-of-the-mill, proof-of-concept web framework. It is the next level in web programming, and the developers who use it will make web applications faster than those who don't; single developers can be as productive as whole teams. Best of all, it's available right now, under an MIT license.

I believe that there hasn't been an improvement in productivity like this in recent programming history.

Editor's note: Want more Ruby on Rails? See Ajax on Rails.

Resources

Web sites
Mailing lists

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.


Return to O'Reilly Ruby.