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

Boss: I think I'm getting the hang of this, CB (he says, with a grin) We added two more lines. Better check our work again. Right?



CB: Looks like there's hope for you yet, Boss (grinning too). Why don't you do the honors?

Refresh our browser and... (Figure 29).

updated category view in browser
Figure 29. Updated category listing in browser

Boss: I'm really seeing what you meant when you said "One of the things that Rails really enables is an iterative and incremental approach to development." I can see where, in a normal project, you couldn't go very long before you'd need some Customer feedback. You'd run out of things to do pretty quick if they didn't stay close!

CB: You have it.

I just remembered the new.rhtml and edit.rhtml files under both the views\categories and views\recipes subdirectories need to have to have the <h1> heading lines removed from them, too. I'll go ahead and do that real quick.

Say, Boss. My fingers are really sore from all this typing ;-) Now that we have more than one category, we need to make sure the category selection works when we add or update a recipe. Think you could take care of that for us? Click the Show all recipes link. Then click the Create new recipe link. Enter a new recipe and assign it to the beverages category. We need at least one main course and one beverage (Figure 30).

listing all recipes
Figure 30. Listing recipes with different categories

CB: Looks like all we have left to do is the filtering when we click on a recipe's category. I set up the link to use filtering when I was fixing the recipe listing view, but I didn't put the behavior in place yet. It goes in the recipe controller, so I open the cookbook2\app\controllers\recipe_controller.rb file and replace the contents of the list method with this code:

if params[:category_id].nil?
  @recipes = Recipe.find(:all)
else
  @recipes = Recipe.find(:all,
                        :conditions => ["category_id = ?", params[:category_id]])
  params[:category_id] = nil
end

Once I'm done, I'll save the file. OK, Boss. We've added another six lines of code. You know what that means ;-)

Boss: I think I'm getting the rhythm ;-) Let's see. Refresh our browser, then click on the beverages link (Figure 31).

just beverage recipes
Figure 31. Recipe list filtered for a single category

CB: Et voil&aacutel;! Okay, Boss. Now click the Show all recipes link (Figure 32).

showing all recipes
Figure 32. We're almost done!

CB: Shucks, Boss!!! If I didn't know better, I'd think we were done!

Boss: It sure looks like it!

CB: Just to be sure, though, why don't you give 'er a thorough run through. HINT: Click the Show all categories link, then click the (delete) link on the Beverages category.

Boss: Hey! That's not right! (Figure 33)

Error message
Figure 33. A Rails error message

CB: Yep. Remember the first line of Rails code we entered? It was in category.rb, the category model file.

has_many :recipes

The error message is telling us we just tried to delete a record that has children. Rails stopped us from accidentally deleting a bunch of recipes. There are a couple of ways we could handle this but the requirements don't say anything about this situation or how to handle it. That's really a decision the Customer should make, you know. For now, I'll just fix it so the app doesn't crash. Then you can ask your boss tomorrow how he wants it to work. I'll add some code to ignore the request if there are child records. The code goes in the destroy method in the category controller (cookbook2\app\controllers\category_controller.rb) file. First I'll ask for all the recipes that have been assigned to the category I'm about to delete.

recipes = Recipe.find(:all, :conditions => ["category_id = ?", params[:id]])

Then I'll wrap the record deletion in an if statement that won't let the deletion happen if Rails found any records.

if recipes.empty?
  Category.find(params[:id]).destroy
end

And we're done. So I save the controller file... (Figure 34).

fixing the parent deletion problem
Figure 34. Modified destroy method to fix the error

Boss: And we added two more lines! Time to check our work again ;-) Refresh the browser. Go back to the recipes listing and delete the ice tea recipe. Now go back to the categories listing and delete the beverages category. (Figure 35).

the delete worked
Figure 35. Houston, we have a launch!

Boss: It's done!!!

CB: What?!?!?!? That can't be right! The pizza's not even cold yet ;-)

Seriously, though, Boss, you're right. We've just finished our first fully functional, web-based, database-driven web application using Rails. What did it take? Thirty or forty minutes? Keystrokes? By my count it's:

MySQL commands: 5
SQL script:     17 lines
Rails commands: 3
Rails code:     31 lines

31 lines of code? No configuration files? One SQL script and eight command-line entries? And we're done?

Boss: You're right. This is amazing. You say you need help with the Ops guys? I'm in. I'm going to tell my boss about it tomorrow morning, too. Do you have any words you want me to use?

CB: You bet. Tell him this.

Rails is the next level in web programming, and the developers who use it will make web applications faster than those who don't. Ruby on Rails makes software development simple. It makes it fast. It makes it fun. And best of all? Rails is available, for free, right now, under an open source MIT license.

Boss: I will, CB. And I'm gonna go make that call to Ops right now. The next thing I need from you is some info on how we learn more about Rails. And thanks again. This was definitely worth the price of a pizza!.

THE END (of our story, but the BEGINNING, we hope, of your Rails adventure!)

Click to continue your adventure as CB shows Paul how to use migrations to manage their Cookbook app's database schema.

Resources

Web sites

Mailing lists

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


Return to O'Reilly Ruby.