Published on O'Reilly (http://oreilly.com/)
 See this if you're having trouble printing code examples

Rolling with Ruby on Rails Revisited

by Bill Walton and Curt Hibbs

Editor's Note: Be sure to check out Rolling with Ruby on Rails Revisited, Part Two, as well as Bill Walton's monthly series, Cookin' With Ruby on Rails. Please note that this tutorial is intended for use with Rails 1.2.6. It has not yet been updated to support Rails 2.x.

Maybe you've heard about Ruby on Rails, the super productive new way to develop web applications, and you'd like to give it a try, but you don't know anything about Ruby or Rails.

That's how the original version of Rolling with Ruby on Rails, published almost two years ago now, began. And it was true. Ruby on Rails (Rails for short) was super productive and it was super fun, too! A community of contributors grew up around it to make it even more productive and more fun! Rails has been growing fast. It has capabilities that you have to see to believe!!! That's what we're here for.

Like the original version, this tutorial will show you how to develop a web-based, database-driven application using Ruby on Rails. Also like the original, it will not teach you how to program in Ruby. On the other hand, if you already know another object-oriented programming language, you should have no problem following along (and at the end you can find links on learning Ruby and lots more).

Before you roll up your sleeves, I want to answer a couple of burning questions.

What is Ruby?

Ruby is a pure object-oriented programming language with a super-clean syntax that makes programming elegant and fun. Ruby successfully combines Smalltalk's conceptual elegance, Python's ease of use and learning, and Perl's pragmatism. Ruby originated in Japan in the early 1990s. It has become popular worldwide in the past few years as more English-language books and documentation have become available (and its popularity has really taken off since the introduction of Rails!).

What is Rails?

Rails is an open source Ruby framework for developing web-based, database-driven applications. What's special about that? There are dozens of frameworks out there, and most of them have been around much longer than Rails. Why should you care about yet another framework?

What would you think if I told you that you can develop a web application at least ten times faster with Rails than you can with a typical Java framework? You can--without making any sacrifices in the quality of your application! How is this possible?

Part of the answer lies in the Ruby programming language. Rails takes full advantage of Ruby. The rest of the answer is in two of Rails' guiding principles: less software and convention over configuration.

Less software means 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. Very shortly, you will see how Rails cuts your code burden.

Convention over configuration means an end to verbose XML configuration files--there aren't any in Rails! Instead of configuration files, a Rails application uses a few simple programming conventions that allow it to figure out everything through reflection and discovery. Your application code and your running database already contain everything that Rails needs to know!

Seeing is Believing

Developers are used to hearing excessive hype accompanying something new. We know. We're developers, too, and we can easily imagine that skeptical look on your face. Super productive. Yeah, right!

We're not asking you to accept it on blind faith. We'll show you how to prove it to yourself.

When you've finished this tutorial, we think you'll be both motivated and ready to learn to "ride the Rails" to similar results yourself. This updated version of the tutorial will produce an application that looks like and has exactly the same features as the original, so we decided to approach it a bit differently. Rather than pretend that we're making this up for the first time, let's pretend that the boss walks into our office just before lunch and says...

Boss: Hey, CB (that's us). I'm in kind of a jam. I really need your help. I'm supposed to do a little dog-and-pony show for my boss tomorrow morning. I've been working up a little app for him on the side. It's kind of a big deal to him and I think he's going to invite some other folks to the demo tomorrow. Maybe his boss. Problem is, I've been using an outside consultant for this, and he must have won the lottery last night. He didn't come in this morning, hasn't responded to my emails, and he's not answering his cell phone. And on top of that, the app stopped working about an hour ago for some unknown reason and I can't even find the source code. CB, this is not the story I want to walk into my boss' office with tomorrow morning. Word around the water cooler is that you've been trying to get some new development tool approved. You say it lets you produce code like ten times faster than the tools we're using now?

CB: Yep. It's called Ruby on Rails, and it's at least 10x. But I haven't had any success with the Ops guys. Can't even get 'em to take a look at it. They're just not enthused about putting anything new into their mix right now.

Boss: CB, I really need to get the situation with this app fixed by tomorrow morning. If you can do that, you can use any tool you want. Just give me a web-based, database-driven app that looks like and acts like the one my boss has already seen. I know that's a lot. I'm probably asking you to work late. Tomorrow morning's really got to be pushing it. I tell you what. If you'll agree to give it a shot, I'll make a phone call, see if I can maybe break the ice for you with the Ops guys.

CB: (sensing an opportunity...) Tell you what, boss. It's almost lunch. If you go get us some pizza, and let me show you how productive Rails can make us, I think we can have you fixed up before lunch is over.

Boss: No way! By the time lunch is over? You are ON, CB.

CB: No sweat, boss. You go get the pizza. I'll get Rails installed. The Ops guys wouldn't let me download the Rails software through the firewall so I brought in a CD this morning. I'll be ready by the time you get back.

Boss: You're going to get a new development environment set up and ready by the time I get back? And then, while we eat, you're going to reproduce an app that I've had a consultant working on for... oh, never mind. I'm not going there. CB, don't get offended but, man, that's pretty tough to believe. I sure hope you're right, though.

So the story begins. The boss has gone to get pizza and we need to get Rails installed in a hurry.

Installing and Configuring Rails

In the original version of this tutorial, Curt showed how to install and configure Rails on a Microsoft Windows platform. In preparation for this update, we discussed how to handle the topic this time. We could do the same thing again, but there've been some complaints about favoring one environment (especially Windows!) over others. Alternately, we could show you how to install and configure Rails on all of them--Windows, Mac, and Linux--but that would take up a bunch of space. Alternately, we could take advantage of the fact that, since the original tutorial, new tools have emerged for each of these environments that make installation and configuration a snap. We could just point you to those resources. Because this last option both gets us off the favoritism hook and requires less effort... well, we're just doing "the simplest thing that could possibly work."

For Windows, use Instant Rails.

For Mac OS X, use Locomotive.

For Linux, try the Rails LiveCD.

Any one of these will have you ready to start developing your first Rails app in less time than it takes to get a pizza delivered.

Pizza's Here!

Boss: OK, CB. Here's the pizza. Now I know I agreed to let you show me how cool your new tool is, but I don't need to understand all the "how's that work?" stuff in detail, so I brought Paul with me, too. I don't mind it if you two dig into the details a little if you don't mind treating it as a sidebar. That OK with you?

CB: Sure, boss. Hi, Paul. Let me just grab a piece of that pie and we'll get started! You said you wanted this to look like the app you've been working on. But you said the app is down now. What do you have for me to work from?

Boss: I found this shot of the main screen (Figure 1) on the consultant's desk.

screen show of original app
Figure 1. Our goal!

Basically, the app's supposed to do three things:

Here's a note I found on his desk with the details of how the app works.

Required features

That's all we have to go on.

Let's Write Code!

CB: Alright, then. Let's get started! One of the things that Rails really enables, Boss, is an iterative and incremental approach to development. As you'll see very shortly, Rails delivers so much for so little effort that it's hard not to fall into that rhythm. In general, we do the same steps every time we develop a Rails application.

  1. Generate an Empty Rails Web Application.
  2. Decide whether to use the default database name that Rails chooses or to tell Rails what database we want the app to use.
  3. If it doesn't already exist, create the database our app will use.
  4. Decide what feature to work on.
  5. If they don't already exist, create the tables our app will need to use to produce that feature.
  6. Generate a code base to use the table(s).
  7. Tweak.
  8. Repeat steps 4 through 7 until done.

Those are the steps I'll be showing you.

Generate an empty Rails web application

CB: Rails is an application development framework. One of the things that means is that every Rails app has the same basic structure. Rails makes it easy to follow that convention by generating that structure for us when we tell it we want to build a new application. All we have to do is...

Open a Command Prompt window and navigate to where we want to put the app. That's called the Rails Root directory. Its name will vary depending on what OS we're using (among other things), but the names and structure of the directories under there will be the same. So from here on, the paths I talk about will just assume the Rails Root portion.

So what do we want to call this thing?

Boss: We called the first one the cookbook app.

CB: OK. Let's call this one cookbook2.

To create the empty app, we just run the command:

rails cookbook2

Rails creates a cookbook2 subdirectory (under the Rails Root directory) that contains a complete directory tree of folders and files for an empty Rails application (Figure 2).

Rails application generated
Figure 2. Rails at work creating the new application directory

Paul: Tell me more about this directory structure, CB. That's a bunch of directories. Are we going to write code to put in all those?

CB: Rails will put code in most of them, but most of our app subdirectories. Rails applications use the Model-View-Controller paradigm to organize Rails application source code.

It's important to remember that a Rails application is an application, not a set of mostly static pages with a bit of script added in. Rails apps generate the web pages they serve. So it's important to understand how controllers work in Rails and how URLs map into (and execute) controller methods.

Controller classes handle web requests. The URL of a visitor's request maps to a controller class and a method within the class; not to a static web page. When a visitor starts the request-response cycle by entering the URL of a Rails app in her browser, the app's controller method gets invoked, typically gets or saves some data using the app's models, and then uses the app's views to produce the HTML to send back to the visitor's browser.

Besides the files in the app subdirectories, there are a couple of files in other directories that we'll also create or edit. We'll get to those shortly, but most of our work will be creating and editing files in the app/ subdirectories.

Decide... or tell Rails what database our app will use

CB: Now that we've built our empty Rails app, we need to let it know what database to use. The Rails convention is that the name of the database we'll use during development is the name of the application (cookbook2, in this case) concatenated with _development. I think that'll work fine for us. We'll name our database cookbook2_development. Also, I've left the password for the development database blank. That's the convention Rails uses when it generates the configuration file for the database. If I had set up the development database to require a password, I would need to tell Rails what password to use to access it. To change either the name of the database we want to use or the password we use to access it, edit ..\cookbook2\config\database.yml. The changes we'd need to make would be pretty self-evident when we looked at the file. Even to you, boss (he says, giving Paul a wink). Because we're OK with using the Rails conventions, we don't have to do even that little bit of configuration.

If it doesn't already exist, create the database our app will use

CB: So now we need to create the cookbook2_development database. I don't know what you've heard about what I've been saying, Boss, but I want to make sure you know I'm really not pushing Rails to replace our existing tools. Rails' sweet-spot is building database-driven, web-based applications. It's not for getting your toaster to talk to your coffee pot. There are better platforms than Rails for that. In fact...

Paul: Maybe we shouldn't go there right now, CB.

CB: Right you are, Paul. Sorry about that.

Our Rails app needs a database. I've made sure the MySQL engine is running. In the Command window we already have open here, I'll log in as the root user.

mysql -u root -p

Because I didn't set one up for this database, I just hit <Enter> at the password prompt.

And then I create our database:

create database cookbook2_development;

(NOTE: The semicolon at the end is required.)

Because I'm working on Windows, I also need to grant access to the database to a user named ODBC. If I don't, I'll get an error from MySQL when I try to access it in a Command window. To grant access, I just enter:

grant all on cookbook2_development.* to 'ODBC'@'localhost';

NOTE: If you're not working in a Windows environment (and in some cases, even if you are) this grant may not be necessary. In general, granting unlimited rights on your database to a generic user is not something you do casually. I've included it here to help ensure that you won't encounter an easily avoidable problem while following along with the tutorial.

That's it. We're done with MySQL for now so I'll...


...and return to the operating system (Figure 3).

Creating the cookbook2 database
Figure 3. CB creates a MySQL database for his Rails app on Windows.

Decide what feature to work on

CB: OK, Boss. The screenshot you gave me shows that we can create, read, update, and delete recipes. The consultant's note you gave me says we can do the same thing with categories, and that each recipe gets assigned to a single category. Overall, it seems pretty straightforward. Where do you think we should start (CB asks with a little grin to himself. Setting the Boss up is such fun.)?

Boss: Well, CB (he says a little condescendingly), I guess we'd better start by creating a recipe, right? Then we move on to reading it. Then editing it. Then deleting it. Then we need to do the same for the categories, and then we need to hook them together. Finally, we'll dig in to the real coding. Isn't that the way we usually do these things?

CB: Yep. That's the way we usually do these things, Boss (Gotcha! ;-) ) Over. And over. And over again. And that's the point of Rails. We're going to do these same things every time we develop a database-driven application. Let's automate it and get to the real work. Rails assumes we're going to need to create, read, update, and delete any data we tell it we're going to use. Once we have the database tables for it to work with, Rails will generate all the basic code we need to do that. That means we can take bigger bites than we usually do. That's where a lot of the 10x productivity comes from.

This app is really pretty simple for Rails. In fact, it's right in the Rails 'sweet spot', so let's take a big bite.

Create the tables your app will need to produce that feature

CB: It's apparent that we'll need one table for recipes and another for categories. The relationship between the two is one-to-many categories to recipes. That means we need to include a foreign key field in the recipes table.

I'll just open up my favorite text editor and write a few lines of SQL script to create them.

drop table if exists recipes;
drop table if exists categories;
create table categories (
    id                     int            not null auto_increment,
    name                   varchar(100)   not null default '',
    primary key(id)
) engine=InnoDB;

create table recipes (
    id                     int            not null auto_increment,
    category_id            int            not null,
    title                  varchar(100)   not null default '',
    description            varchar(255)   null,
    date                   date           null,
    instructions           text           null,
    constraint fk_recipes_categories foreign key (category_id) references categories(id),
    primary key(id)
) engine=InnoDB;

CB: Hey, Paul. Remember I said there were a couple of files outside the app directory we'd work on? The SQL script to set up the tables the app uses is one of them. So, Boss. Now I save the file to ..\cookbook2\db\create.sql, and we're ready to create the tables.

In the Command window I'm keeping open, I change into the cookbook2 directory. After I make sure I'm in the cookbook2 directory, I enter:

mysql cookbook2_development <db\create.sql

MySQL executes the script and returns to the command line without telling us anything. That means everything went OK. Our database now contains two empty tables: recipes and categories.

Paul: CB, that script looks like it's specific to MySQL. That might be a problem for the Ops guys.

CB: I know. Another downside of SQL scripts is losing data when you decide you need to add a field to a table. The upside to scripts is that they're really easy. That's why I'm using one here with the Boss.

Fortunately Rails also gives us a tool called migrations. It's a little too much to get into right now, but migrations give us database platform independence and a mechanism that allows us to do a lot of our work on the models incrementally without losing our data every time we make a change. Maybe we'll get a chance to talk more about that later.

Generate a code base to use the table(s)

CB: Now that we have tables that Rails can look at, we ask it to generate our basic application. In Rails lingo, this is scaffolding. In the Command window, making sure I'm still in the cookbook2 subdirectory, I enter:

ruby script\generate scaffold recipe recipe

This tells Rails to generate the scaffolding: the basic model, controller, and view files for the part of our application that will use the recipes table. You can see that Rails generates quite a few files (Figure 4). In the command line I just entered, I told Rails about two files in particular: the model (the first argument) and the controller (the second/last argument). Rails named the model file recipe.rb and placed it in the ..\cookbook2\app\models\ subdirectory. In like fashion, it named the controller file recipe_controller.rb and placed it in the ..\cookbook2\app\controllers\ subdirectory.

Rails generating recipe scaffolding
Figure 4. Generating the recipe scaffolding code

Now I do the same thing for categories. Again in the Command window, I enter:

ruby script\generate scaffold category category

This tells Rails to generate the basic model, controller, and view files (Figure 5) for the part of our application that will use the Categories table. As before, Rails will name the model file and place it at ..\cookbook2\app\models\category.rb and will name the controller file and place it at ..\cookbook2\app\controllers\category_controller.rb.

Rails generating category scaffolding
Figure 5. Generating the category scaffolding code

CB: OK, Boss. Everything I've done up to this point has been pretty short and painless, but not particularly exciting. This is where that changes.

Before we move forward, though, let's take a look at exactly what I've done. I entered a total of eight command lines: five MySQL commands and three Rails commands. I created a SQL script with 17 non-blank lines. That's not what I'd really call coding yet, but as you're about to see... well... just hold on to your hat!


CB: I'm going to be using a web server named Mongrel to serve our app. To start it up, in the cookbook2 subdirectory in my Command window, I enter:

mongrel_rails start

When Mongrel has finished starting, it's ready to serve our app.

mongrel web server starting
Figure 6. Starting our web server

CB: You ready?

Boss: Ready for what? You just reminded me that you haven't even written any code yet.

CB: That's right, but that doesn't mean we haven't been productive. Here; take the keyboard. Now open a browser window and point it at http://localhost:3000/category (Figure 7).

Paul: I've never heard of Mongrel, CB. Do Rails apps have to use it?

CB: Not at all, Paul. Mongrel is a very lightweight, very Rails-friendly web server. It's great for serving our app locally during development, and it's used a lot in production environments, too. However, it's not a "must-use." There are several alternatives. We can get into that later if you want.

Our first Rails page
Figure 7. Our first Rails app!

Click the link (Figure 8).

page for adding a new category
Figure 8. New category entry page

Enter a category name and click the 'Create' button (Figure 9).

NOTE: The typical, incredibly astute O'Reilly reader has probably just noticed that CB didn't say "Enter a recipe and click the 'Create' button" here. "Why's that?", you might ask. It won't work yet. Remember: we haven't written any code yet. We won't be writing much, but we do need a few lines to pull it all together. Not many. Just a few. Part Two of this article is now available.

category listing with new entry
Figure 9. New category added!

Boss: Now, that is way beyond cool--it's awesome! What about recipes?

CB: Just point your browser to http://localhost:3000/recipe (Figure 10).

The recipe list
Figure 10. Empty list of recipes

Click the link (Figure 11).

new recipe page
Figure 11. New recipe entry page

CB: Where's your hat, Boss? It must have been blown away! I told you to hold on to it!

Boss: That is just amazing, CB. I have to tell you the truth. When I agreed to have pizza with you, I really didn't have much faith that you'd actually be able to do this app, start to finish, before lunch was over. I'm starting to get the feeling that maybe you weren't BS'ing me.

CB: And we haven't even written any code yet! You're probably anxious to do that, but before we do, let's have some of that pizza. 'Cause you know, with Rails, we can have our pie and eat it, too!

Continue to Part Two of this article.

Bill Walton is a software development/project management consultant/contractor.

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.

Copyright © 2009 O'Reilly Media, Inc.