Rolling with Ruby on Rails Revisited, Part 2

by Bill Walton
01/05/2007

Editor's note: this is the second part of Rolling with Ruby on Rails Revisited, an update to Curt Hibbs's influential Rolling with Ruby on Rails and Rolling with Ruby on Rails Part 2.

Also check out Bill Walton's monthly series, Cookin' With Ruby on Rails.

NOTE TO READER: This tutorial is intended for use with Rails 1.2.6. It has not yet been updated to support Rails 2.x.

Paul: Good pizza. Thanks, Boss. Listen, I know you asked us to take the details off on a sidebar, but I'm dying to see the code that Rails generated to produce this. Would you mind if I asked CB to take a minute and show us?

Boss: Actually, Paul, I'm pretty interested in seeing that myself. CB, would you mind? But keep it short for now, OK?

CB: No problem. ("Easy now," CB reminds himself, grinning. "You don't want to pull the hook.") Let's take a quick look at the controller code for recipes (Figure 12). Remember that Rails stores controller code in the controllers directory in a file with the name it picks based on the arguments we supplied when we generated the scaffolding. In our case, we told it to generate a model and a controller; both named recipe. The controller gets named recipe_controller.

Rails-generated recipe controller
Figure 12. Our recipe controller

Boss: That's quite a bit of code, and I get most of it. I mean, you said Rails was going to generate code for create, read, update, and delete. I see a create method, and update and destroy methods. I guess the show and list methods are for the read, but what are the new and edit methods for? They don't seem to do much.

CB: That's one of the differences between a real web-based application and site that's just a bunch of static pages with some scripting, Boss. In a scripted site, we'd probably hand-code the HTML form the visitor uses to enter the data. Then we'd code the create method on the server-side to save the data when the form gets submitted. Remember, Rails generates the pages it serves, so it's going to create the HTML form for us. The new and edit methods just supply an object for the form to use. The create and update methods save the object to the database when the form gets submitted.

Boss: OK. I think I understand the basics now. Paul, you OK if we move on now?

Paul: Sure, Boss. I'll meet with CB later to get him to take me deeper.

CB: OK, then. Let's get back to it. You might have noticed that there's no way to assign a category on the New recipe page. That's because I told MySQL that each recipe will have a category, but I didn't tell Rails the same thing. All I need to do to fix this is to tell Rails how the two are related and then add a couple of lines to the view file for the New recipe page.

I tell Rails about the relationship between recipes and categories in their models. In the cookbook2\app\models\category.rb file (Figure 13), I tell Rails that each category will have many recipes assigned to it by adding one line.

has_many :recipes

adding relationship to category model
Figure 13. The category model file

Then I tell Rails that each recipe gets assigned to one category by adding one line to the recipe model (cookbook2\app\models\recipe.rb, Figure 14).

belongs_to :category

adding relationship to recipe model
Figure 14. The recipe model file

Now Rails knows about the relationship between the two models.

To give the visitor the ability to select a category, I need to add a couple of lines to the New recipe view. The view file for the new method in the recipe controller is in the recipe subdirectory under views. To fix the view, I'll open the cookbook2\app\views\recipe\_form.rhtml file (Figure 15).

original form file
Figure 15. The Rails-generated form partial

Paul: CB, I noticed there were a bunch of files in that directory, and one of them is new.html. How come you're not editing that file?

CB: Keen eye, Paul. Rails generates a view file for each method in the controller, but one of the guiding principles in Rails is "Don't Repeat Yourself." Rails views let us modularize our view code. Because the new method and the edit method both work on the same object, Rails creates a partial, names it _form.rhtml, and uses it in the new and edit views. A view can include as many partials as we need to keep our code DRY. We'll do more work on view files shortly.

CB: To add the category selection, I need to add two lines of code.

<p><label
for="recipe_category_id">Category</label><br/>
<%= select("recipe", "category_id", Category.find(:all).collect
{|c| [c.name, c.id] }) %></p>

The first line is straight HTML. The second is Ruby code. Rails sees the <% ... %> and knows that what's inside is embedded Ruby code that it needs to execute to generate the HTML to send back to the browser. Because this one has the = sign, Rails will generate the HTML we need for a select tag. If it didn't have the = sign, and we'll see some of that too, Rails would execute the code but wouldn't generate any HTML. You might have noticed that the New recipe page lets the visitor select a date, but the requirements say the system must assign the date. While I'm in here, I'm going to remove the lines that let the visitor furnish it. Now we have a form partial that looks like Figure 16.

updated recipe form file
Figure 16. Updated form partial

Pages: 1, 2, 3, 4

Next Pagearrow