Rolling with Ruby on Rails Revisited
Pages: 1, 2

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.

  • The app/controllers subdirectory is where Rails looks to find controller classes. A controller handles a web request from the user.
  • The app/views subdirectory holds the display templates to fill in with data from our application, convert to HTML, and return to the user's browser.
  • The app/models subdirectory holds the classes that model and wrap the data stored in our application's database. In most frameworks, this part of the application can grow pretty messy, tedious, verbose, and error-prone. Rails makes it dead simple!
  • The app/helpers subdirectory holds any helper classes used to assist the model, view, and controller classes. This helps to keep the model, view, and controller code small, focused, and uncluttered.

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

exit

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

Tweak

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.