38
The Rails Cookbook A Ruby on Rails Crash Course Brian Hogan

Rails Cookbook

  • Upload
    terget

  • View
    870

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Rails Cookbook

The Rails Cookbook

A Ruby on Rails Crash Course

Brian Hogan

Page 2: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 2 http://www.napcs.com/resources/rails

The Rails Cookbook: A Ruby on Rails Crash CourseBrian Hogan

Published 2008Copyright © 2008 Brian P. Hogan

Page 3: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 3 http://www.napcs.com/resources/rails

Page 4: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan i http://www.napcs.com/resources/rails

DedicationThis book is dedicated to the Web Development students at the University of Wisconsin-Eau Claire, past,present, and future.

Page 5: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan ii http://www.napcs.com/resources/rails

Table of Contents1. Introduction ................................................................................................................... 1

1. Rails ..................................................................................................................... 12. Basic Concepts ....................................................................................................... 1

2.1. MVC Design Pattern ..................................................................................... 12.2. Agile Programming Techniques ...................................................................... 22.3. Other features and components of Rails ............................................................ 2

3. Installing Ruby on Rails ........................................................................................... 32. Your First Rails Project - A Cookbook ............................................................................... 4

1. Configuring your Database ....................................................................................... 41.1. Working with SQLite3 .................................................................................. 5

2. Creating the Recipes interface using Scaffolding ........................................................... 52.1. Migrations ................................................................................................... 72.2. The migration file ........................................................................................ 72.3. Creating the table from the migration ............................................................... 7

3. Test it out ............................................................................................................. 84. How did we get all that? .......................................................................................... 8

3. Validating User Input ...................................................................................................... 91. Validations ............................................................................................................ 92. Unit Tests ............................................................................................................ 10

2.1. How Tests Work ........................................................................................ 112.2. Fixtures ..................................................................................................... 112.3. Running the Test ........................................................................................ 12

3. Providing Feedback to Users ................................................................................... 124. Cleaning Up the Scaffolding ........................................................................................... 14

1. Cleaning up the List page ....................................................................................... 141.1. Helpers ..................................................................................................... 15

2. Cleaning up the Show page ..................................................................................... 153. Using Partials to share common code ........................................................................ 164. Where’s the rest of my HTML? ............................................................................... 17

5. Adding Categories ......................................................................................................... 181. Create a category model and table ............................................................................ 182. Adding some default records with Rake .................................................................... 183. Modifying the Recipes table .................................................................................... 194. Creating an Association Between a Recipe and a Category ............................................ 19

4.1. Lazy vs. Eager Loading ............................................................................... 205. Adding categories to the controllers and views ........................................................... 20

5.1. The New and Edit forms .............................................................................. 205.2. The Show view .......................................................................................... 215.3. The List view ............................................................................................ 22

6. Test it out ............................................................................................................ 226. Other Rails Tidbits ........................................................................................................ 23

1. Writing Documentation with RDoc ........................................................................... 232. Annotating Models ................................................................................................ 243. Debugging and Exploring with Console .................................................................... 244. Logging ............................................................................................................... 255. Writing your own SQL statements ........................................................................... 25

7. Development and Deployment ......................................................................................... 271. Exploring Development Tools ................................................................................. 272. Deploying Rails Applications .................................................................................. 27

8. Where To Go Next? ...................................................................................................... 281. Books .................................................................................................................. 28

Page 6: Rails Cookbook

The Rails Cookbook

Copyright (c) 2008 Brian P. Hogan iii http://www.napcs.com/resources/rails

2. Online Resources .................................................................................................. 289. Homework and Exploration ............................................................................................. 2910. Support This Tutorial ................................................................................................... 30Index .............................................................................................................................. 31

Page 7: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan iv http://www.napcs.com/resources/rails

List of Figures3.1. User Feedback as provided by Rails validations ............................................................... 134.1. The modified Index page ............................................................................................. 154.2. The Show Page .......................................................................................................... 166.1. RDoc output in HTML ................................................................................................ 236.2. Annotations added to a model ....................................................................................... 246.3. Using the Rails Console to work with objects .................................................................. 25

Page 8: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 1 http://www.napcs.com/resources/rails

Chapter 1. IntroductionGet ready to forget everything you know about web developent, because Ruby on Rails will change yourworld. Web development with Ruby on Rails is one of the fastest ways to build quality applications in afraction of the time, and all it takes is a little patience and a little time. If you've ever wanted to get startedwith this technology, this guide is for you.

This simple yet detailed tutorial will guide you through the steps to build a working cookbook applicationusing the Ruby on Rails framework. This guide is meant to expose you to the Rails framework and isnot meant to explain all of the concepts in depth. When you’re finished with this tutorial, you will havea better understanding of the basic concepts behind Rails and you should have a basic understanding ofhow basic Rails applications are structured.

1. RailsRuby on Rails is the hot new framework for building database-driven web applications. The Rails frame-work provides developers with the need to create web applications using Agile programming methodolo-gies such as rapid prototyping, test-driven development, and easy refactoring.

Ruby is a dynamically-typed fully object-oriented scripting language used primarily in Japan until it drewthe attention of a developer from a small company called 37Signals. The developer, David HeinemeierHansson, was working on a new project called Basecamp. David felt very limited by the languages he wasusing to develop the project and so he started working with Ruby, taking advantage of all of the built-infeatures of the language.

In July 2004, David released the Rails framework which he extracted from his work on Basecamp. Severalversions later, Rails has burst onto the scene and attracted the attention of many development shops andindustry leaders including Amazon, Oracle, Boeing, Thoughtworks, and many others.

2. Basic ConceptsLet's take a bit and look at the basic concepts of the Rails framework.

2.1. MVC Design PatternThe MVC or Model-View-Controller pattern explicitly splits up your code and separates business logicfrom presentation and flow control logic. It also provides mechanisms for easy reuse of code. Traditionalweb applications written in ASP, PHP, or ColdFusion tend to have scripts intermingled with business logic.Developers can avoid this but without a design pattern such as MVC, the process tends to be trial and error.

2.1.1. The components of MVC

Models contain all business rules and data interaction. All database-related CRUD (Create, Read, Update,and Delete) code belongs in the model as well. If this pattern is followed correctly, you’ll never write aselect statement anywhere outside of the model. Instead, you will access your data by calling a methodon the model.

Views are what your end users will see. They are the web pages in a web application, or the user screensin a desktop application. They should contain very little presentation logic and should be optimized forreuse. Views should never interact directly with models.

Controllers are the glue of the application. Controllers receive user requests, retrieve data from the models,and then send the appropriate view to the user.

Page 9: Rails Cookbook

Introduction

Copyright (c) 2008 Brian P. Hogan 2 http://www.napcs.com/resources/rails

2.1.2. Rails-specific MVC components

The Rails framework divides the View layer into three separate pieces.

Layouts contain your overall template and can be mapped to a controller or a specific view. Instead ofplacing and repeating your entire layout HTML in each view page, you centralize it in the layout. There’sno need to split a layout into various pieces like a header and footer either. This makes layouts easy todesign.

Helpers provide a mechanism to store presentation-related code. Helpers are very similar to a “code-behind” page. Rails comes with hundreds of pre-written helpers built in, but Rails makes it easy to defineyour own. This way you can avoid lots of messy logic code in your view pages.

Partials are pieces of views that need to be used over and over. The web form that lets a user create a newentry might contain the same fields as the form that the user will use to update or edit that entry. Partialsare used to centralize that code for easy reuse.

2.2. Agile Programming Techniques

2.2.1. Test-driven development

Test-driven development (TDD) is a programming methodology where you write tests to prove that yourcode actually works. In an ideal world, you will write your tests first and then write enough applicationcode to make your tests pass.

For example, a developer will create a unit test to create a new user. The test will fail until the developeractually writes the code that creates the user. The developer writes the code and continues to run the testsuntil they pass.. If they don’t pass, the developer knows that his or her functional code is wrong. If youwrite your tests first, the tests are always guaranteed to be current. New tests are added when new featuresare added, and tests are changed to reflect new requirements.

2.2.2. Refactoring

According to Martin Fowler, a senior consultant at Thoughtworks, the basic idea is that you make smallchanges to your code to improve your design, making it easier to understand and to modify. Refactoringenables you to evolve your code slowly over time, to take an iterative and incremental approach to pro-gramming. Martin's refactoring site, www.refactoring.com, is a good online resource.

Rails makes refactoring easy. Because of the strict adherence to the MVC pattern, it’s trivial for expe-rienced developers to take an entire section of an application and rewrite it without breaking the entireapplication.

2.3. Other features and components of RailsGenerate scripts help you create Rails models, views, and controllers.

Destroy scripts help you remove Rails models, views, and controllers.

Mongrel is a Ruby-based web server which is used in development and deployment so you can test yourapplication without having to jump through deployment hoops.

Runner is a script that allows you to execute your application code from outside of the Rails application.This is useful if you want to use a task scheduling program to periodically invoke some application code.

Unit tests contain the code that you use to test your models.

Page 10: Rails Cookbook

Introduction

Copyright (c) 2008 Brian P. Hogan 3 http://www.napcs.com/resources/rails

Functional tests contain code you use to test your controllers and views

Migrations allow you to define a database-agnostic schema incrementally, with the ability to roll back yourchanges. You can use Migrations to easily develop on SQLite, stage to MySQL, and deploy on Oracle.

Plugins allow developers to add new features to the framework at almost any level. Plugins can be usedto introduce new features, override existing ones, modify the Ruby core classes, or even share commonmodels and controllers across multiple applications. Plugins are easy to write and seamless to use. Theyallow the Rails core team to deny many feature requests, keeping the Rails framework small and agile

Rake is a Ruby program that runs user-defined tasks. Rails includes many tasks that help you manageyour application.

Finally, Rails provides the ability to “freeze” your application to the current version of Rails that you usedfor development. A simple Rake task bundles Rails with your application, ensuring that your applicationremains safe when the Rails framework is updated.

3. Installing Ruby on RailsWarning

These instructions are for Windows users.

Download the One Click Ruby Installer1 for Windows and run the installer, accepting all of the defaults.

Next, open a command prompt and type

gem update --system

gem install rails

gem install mongrel

This will get your environment set up using the current stable release of Rails. The Gem package manage-ment tool helps you install Ruby libraries that you want to use system-wide. You have to have administra-tive privileges though, so if you don’t, you’ll need to have an administrator run those commands for you.

Working with Older Versions of Rails

This document is written with Rails 2.0.2 in mind. It is possible that the version of Rails that'scurrently provided by Rubygems is newer. This may cause some problems as things change rapidlywith Rails and things are deprecated or moved into plugins. However, it's easy to have multipleversions of the Rails framework installed on your machine, thanks to Rubygems.

The command gem list rails will quickly tell you which versions of Rails you have installed.To install a specific version of Rails, you simply issue this command:

gem install rails -v=2.0.2

To install Rails 1.2.3 in order to follow along with some older books and tutorials, install it with

gem install rails -v=1.2.3

1https://rubyforge.org/frs/download.php/29263/ruby186-26.exe

Page 11: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 4 http://www.napcs.com/resources/rails

Chapter 2. Your First Rails Project - ACookbook

We're going to build a simple cookbook that will let us keep track of our favorite recipes. Each recipe willhave ingredients, instructions, and a title. Eventually you'll be able to associate a category to the recipe.

Create a new Rails project by opening a command window and typing

rails cookbook

Generating a project using a specific Rails version

If you have multiple versions of Rails installed via RubyGems, you can choose which version ofRails you wish to use for your application. For example, if you need to use Rails 2.0.2 to followalong with this book, you can install Rails 2.0.2 with

gem install rails -v=2.0.2

and then create the cookbook project with

rails _2.0.2_ cookbook

The _2.0.2_ parameter tells RubyGems which version to use. To use Rails 1.2.3 to follow alongwith the famous Depot application in Agile Web Development with Rails - Second Edition, yousimply create the project with

rails _1.2.3_ depot

That's all there is to it.

This command will create the folder cookbook and place a whole bunch of framework files in that folder.As you move through this tutorial, you’ll notice that Rails has many generators that build files for you.Generators are great for file creation and laying down code that’s generic.

1. Configuring your DatabaseOpen the file config/database.yml and review the contents of the file. It should look somethinglike this:

1 development: 2 adapter: sqlite3 3 database: db/cookbook_dev.db 4 5 test: 6 adapter: sqlite3 7 database: db/cookbook_test.db

This is a YAML file. (rhymes with camel) It’s a structured configuration format that maps directly tonested hashes in Ruby and is very common for configurations in Ruby on Rails. Tabs must not be used inYAML files. Instead, two spaces are used for each indentation.

Page 12: Rails Cookbook

Your First Rails Project - A Cookbook

Copyright (c) 2008 Brian P. Hogan 5 http://www.napcs.com/resources/rails

• Adapter is the database adapter that we want to use. Examples are mysql, sql_server, oracle, postresql,sqlite3, and sqlite. We’re using sqlite3 because it’s easy for beginners, requires no setup, and is thedefault database for a new Rails project.

• Database is the name of the database. In this case, it’s the path to the database file. Other complexadapters would have you specify the database name or Oracle TNSNames entry here, and then youwould have host, username, and password fields as well.

1.1. Working with SQLite3SQLite 3 is the default database in Rails 2.0. With SQLite3, the database does not need to exist beforewe start the project; it will be created for you when you run your first migration (but don’t worry aboutthat just yet!) Other databases like MySQL or Microsoft SQL Server require that the database (or schema)exist and that the appropriate privileges are applied. Since we’re trying to get you excited about Rails, wewant to keep the momentum going. Using SQLite3 as a database makes it really simple to create a workingrapid prototype. You can then move to a different database later, because you’ll define your database tablesusing pure Ruby code instead of database-specific SQL DDL statements.

1.1.1. Installing SQLite3 on Windows

1. Download the files sqlite3.exe and sqlite3.dll and place them in your c:\ruby\bin folder so that they areavailable on your path.

Get sqlite3.exe from http://www.sqlite.org/sqlite-3_5_7.zip

Get sqlite3.dll from http://www.sqlite.org/sqlitedll-3_5_7.zip

Extract these files to c:\ruby\bin

Note

If you're on Mac OSX, you already have SQLite3 installed!

2. Open a command prompt and type

gem install sqlite3-ruby

When it finishes installing, you’re good to go!

2. Creating the Recipes interface using Scaf-folding

Since we’re writing a cookbook, the logical place to start would be with recipes.

Rails uses generators to help you do some common tasks. We’ll use a generator to create a scaffold.

Scaffolding is the much talked about but poorly understood feature of Rails. It’s meant to be a startingpoint… to give you a quick interface where you can do some simple testing and get some of the mundanerepetitive code written for you. However, scaffolding can only take you so far and is not meant for usein production, hence the name “scaffolding”. There are some steps you’ll need to take to clean up thescaffolding.

Page 13: Rails Cookbook

Your First Rails Project - A Cookbook

Copyright (c) 2008 Brian P. Hogan 6 http://www.napcs.com/resources/rails

Let’s create a simple interface that will allow us to manage recipes in the system. The scaffold generatorcreates a model, controller, a set of views, and a migration, or a table definition. At the command prompt,move into your cookbook project folder

cd cookbook

The generate scaffold command takes several parameters. The first parameter is the model name. Modelnames are singular. The generator will use this model name to create a controller and a definition for adatabase table. Both of these, by convention, will be pluralized. The second parameter is a string thatdefines your database table structure. Each field can be specified along with its data type. The scaffoldgenerator uses this information to build the web forms your users will see. They won’t be pretty but theywill work.

Type (all on one line)

ruby script/generate scaffold recipe title:string ingredients:textinstructions:text

The generator runs, creating the following output:

exists app/models/ exists app/controllers/ exists app/helpers/ create app/views/recipes exists app/views/layouts/ exists test/functional/ exists test/unit/ create app/views/recipes/index.html.erb create app/views/recipes/show.html.erb create app/views/recipes/new.html.erb create app/views/recipes/edit.html.erb create app/views/layouts/recipes.html.erb create public/stylesheets/scaffold.css dependency model exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/recipe.rb create test/unit/recipe_test.rb create test/fixtures/recipes.yml create db/migrate create db/migrate/001_create_recipes.rb create app/controllers/recipes_controller.rb create test/functional/recipes_controller_test.rb create app/helpers/recipes_helper.rb route map.resources :recipes

The generator created a recipe model, and it also created a controller calledrecipes_controller.rb. The controller will contain all of the logic that handles user requests andinteracts with the models. In fact, if we look in there, it’s already written it for us! It’s got code to handlecreating, editing, listing, viewing, and deleting of recipes. Because these are all common tasks, the gener-ators can do a pretty solid job of handling this for us.

One major problem with scaffolding is that it’s not dynamic. Now that we’ve generated these, we can’trely on scaffolding any more. Any manual changes we make would be destroyed if we attempted to run thescaffold generator again. That means that if we change the table, we’ll need to modify the views. That’sokay though because we already have a good starting point.

The model we just created requires a database table called “recipes”. Normally, you’d go and create thatdatabase table using some sort of SQL statement or visual tool. In Rails, we use migrations.

Page 14: Rails Cookbook

Your First Rails Project - A Cookbook

Copyright (c) 2008 Brian P. Hogan 7 http://www.napcs.com/resources/rails

2.1. MigrationsMigrations are used to modify your database. You use them to execute DDL statements against yourdatabase system. One of the best things about them is that they allow you to define your database asit changes; you can roll your changes back if they don’t work without worrying about goofing up yourdatabase.

They’re also an invaluable tool when moving to production. Migrations are supported by all of the Railsdatabase adapters. This means that you can change database systems and apply the migration to the newdatabase which will create your structures for you. This eliminates the need to know the various dialectsof data definition languages that may change across database systems. Developers can test with SQLite3,develop with MySQL, and deploy to Oracle.

2.2. The migration fileOpen the file db\migrate\001_create_recipes.rb. It’s contents should resemble this:

1 class CreateRecipes < ActiveRecord::Migration 2 def self.up 3 create_table :recipes do |t| 4 t.string :title 5 t.text :ingredients, :instructions 6 t.timestamps 7 end 8 end 9 10 def self.down 11 drop_table :recipes 12 end 13 end

Rails uses the information in this file to create a ‘recipes’ table in the database. Note that the above defi-nition does not include a primary key field. Unless you specify otherwise, Rails will create an ‘id’ columnautomatically, and will mark it as a primary key.

Why is the table name “recipes” and not “recipe”? Remember that by default, Rails likes table names tobe the plural form of your model. It’s pretty smart too because it can do things like person => people andcategory => categoies. This isn’t mandatory but if we follow these conventions, we can save a few linesof code and skip a few steps. Rails will automatically look for the recipes table when we access the Recipemodel in our code.

2.3. Creating the table from the migrationAt this point, the table doesn’t actually exist in our database—we just have the “blueprint” for it in Rubycode. To execute the migration, we’ll run the command

rake db:migrate

from our command line. Run that command and you’ll see feedback stating that our recipes table wascreated.

(in C:/rails/workspace/cookbook)== 1 CreateRecipes: migrating ================================================-- create_table(:recipes) -> 0.0310s

Page 15: Rails Cookbook

Your First Rails Project - A Cookbook

Copyright (c) 2008 Brian P. Hogan 8 http://www.napcs.com/resources/rails

== 1 CreateRecipes: migrated (0.0310s) =======================================

3. Test it outWould you believe that’s all you have to do to get a simple application written with Rails? Start the internalserver and test out your application. At the command prompt, enter

ruby script/server

and wait a few seconds until you see that the server has in fact started.

Navigate to http://localhost:3000/recipes/ and you should be able to enter a few recipes into the system.Once you’ve entered a few recipes, continue with the tutorial. The application works, but it’s a long wayfrom good.

4. How did we get all that?When you generated the Recipe model, it created a new class that extends a class calledActiveRecord::Base. This parent class contains all of the functionality needed to create, read, update, anddelete records from a database. This parent class makes all kinds of dynamic assumptions about yourdatabase connection. As we discovered before, it uses the class name (recipe) and pluralizes it to figureout what database table to use, and It uses your database.yml to find out what database server to use.

The first time you access a model in a Rails application, the application connects to the associated databaseserver and queries the database for the information about the table. It uses this information to dynamicallybuild methods for data storage and retrieval.

The Scaffold generator you ran uses that technique to build an interface that will let you create, read, update,delete, and list the rows of the recipes table. It uses the information your model has obtained and thenbuilds HTML forms for data entry, using the data types specified by your database table’s configuration.

So instead of having to write code to connect to a database and then build data entry forms, you can usethe scaffolding feature of Rails as a starting point. This is just the beginning though. There's a lot more toRails than just scaffolding an application from a single table.. In fact, most professional Rails developersdon’t use scaffolding at all.

Page 16: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 9 http://www.napcs.com/resources/rails

Chapter 3. Validating User InputYou may notice that if you entered recipes into the system without filling in any fields, the data wasstill placed into the database. You will definitely want to require that people enter certain fields into thedatabase.

We’re going to do this with some simple validations and a simple unit test to ensure that our validationswork.

In Rails, validation is done in the model. This is actually good practice because the models could be usedoutside of the Web. The MVC pattern standard is to place all business logic and business rules in themodels and have only presentation logic in the views and controllers.

A word about models

Our model class has very little code. In fact, it really has no code at all. As you learned earlier, itsparent class does all of the work.

Normally, Ruby classes use methods for accessing instance variables in a class. ActiveRecord, theORM library used by Rails, attempts to build methods dynamically based on the metadata in yourdatabase. The class is mapped to a table and upon first use, ActiveRecord creates an instance ofthe class and builds the accessor methods (and many many other methods). In production mod, thisreflection is only done once until the application is restarted by a server admin.

Each field in our table becomes an accessor (a Ruby term meaning that it can be read and writtento). For example, we said a recipe has a title. Our Recipe class will have a method called titlewhich can be used as a getter and a setter.

1. ValidationsValidations are special methods provided by the Validations feature in Active Record. There are quite a fewbuilt-in methods we can use to validate data. For simplicity, we’ll use only validates_presence_ofon our Recipe model.

Open app/models/recipe.rb and change the contents to

1 class Recipe < ActiveRecord::Base 2 validates_presence_of :title, :ingredients, :instructions 3 end 4

Notice that we use symbols as the parameters to the validates_presence_of method. Symbols are a specialtype of string used in Ruby to denote a value. One of the interesting things about Ruby is that methodscan be defined so they take any number of parameters. In this case, the validates_presence_ofmethod is taking in an array of symbols which represent the database fields that need to be validated.

This simple line of code is all we need to make sure that users enter something for the title, ingredients,and instructions for a recipe. It’s not foolproof, but it’s a good start.

If you want to see what other types of validations are out there, take a look at this pagein the Rails API: http://api.rubyonrails.com/classes/ActiveRecord/Valida-tions/ClassMethods.html

Page 17: Rails Cookbook

Validating User Input

Copyright (c) 2008 Brian P. Hogan 10 http://www.napcs.com/resources/rails

2. Unit TestsWe need to put on the brakes here and write a quick unit test to ensure that our validation works. Unittests are built right into the Rails framework and are designed to test the functionality of your individualmodels. Unit tests become vitally important to your development process because they allow you to testyour business logic and prove that things are working at the model level. This way you don’t have to keepusing a browser to track down bugs that aren’t related to the display.

Rails automatically generated a unit test skeleton for recipe when we generated the recipe model. Openthe file test/unit/recipe_test.rb and change the contents to the following:

1 require File.dirname(__FILE__) + '/../test_helper' 2 3 class RecipeTest < ActiveSupport::TestCase 4 5 def test_should_create_valid_record 6 recipe = Recipe.new 7 recipe.title = "Ice water" 8 recipe.ingredients = ["one glass","water","ice"].join("&lt;br&gt;") 9 recipe.instructions = "Combine all ingredients into the glass and let sit for two minutes. Serve immediately." 10 assert_kind_of Recipe, recipe 11 assert recipe.save 12 end 13 14 def test_should_not_save_unless_title_exists 15 recipe = Recipe.new 16 assert !recipe.save # save should fail because there are errors. 17 assert_equal "can't be blank", recipe.errors.on(:title) 18 end 19 20 def test_should_not_save_unless_ingredients_exists 21 recipe = Recipe.new 22 assert !recipe.save # save should fail because there are errors. 23 assert_equal "can't be blank", recipe.errors.on(:ingredients) 24 end 25 26 def test_should_not_save_unless_instructions_exists 27 recipe = Recipe.new 28 assert !recipe.save # save should fail because there are errors. 29 assert_equal "can't be blank", recipe.errors.on(:instructions) 30 end 31 32 end 33

That might look complicated, but it’s really not. We have four methods there…

test_should_create_valid_record,

test_should_not_save_unless_title_exists,

test_should_not_save_unless_ingredients_exists, and

test_should_not_save_unless_instructions_exists.

test_should_create_valid_record simply creates a new instance of Recipe, sets the values forthe recipe, and then saves the record. Save should return true, so we use assert to evaluate the value ofsave. If it evaluates to true, this test passes. If not, it fails. This one needs to pass all the time, as it’s thebaseline test. If this test starts failing, that’s an indication that the program’s logic has changed.

Page 18: Rails Cookbook

Validating User Input

Copyright (c) 2008 Brian P. Hogan 11 http://www.napcs.com/resources/rails

The other three tests simply attempt to save the record without setting one of the required fields. We expectthese to fail because of our validations—in this test we haven’t actually provided any of the requiredfields, but we are testing for only one error at a time to avoid making our tests too complicated.. We’realso asserting the inverse of true for the save. (assert that Recipe.save is not true. Then we assert thatthe error messages are set for each field. Each validation has its own message format. In this case, thevalidates_presence_of validation stores “can’t be blank” in the errors collection, under a key for eachinvalid attribute. If the title isn’t blank, you’ll find the error message for that in the errors collection, underthe :title key. How Tests Work

2.1. How Tests Work

Tests actually work by using the test database you defined in database.yml earlier. A test file startsby dumping everything in your database and then updating it so it’s consistent with your developmentdatabase. Never use the same database for production, development, and tetsting!!!!

Each test file is independent of the others. You can feel free to delete as many records as you want in atest and they will be recreated when you start the test again.

Each method that starts with ‘test_’ will be run as part of the test suite.

2.2. Fixtures

Tests can get data from fixtures. Fixtures are loaded into each test by the fixtures method. You’ll need afixture for each table in your database, not each model in your system.

Modify the fixture for the recipes table by editing /test/fixtures/recipes.yml and add a fewrecipes.

Note

Be careful not to use tabs and also be sure to leave a space after each colon! YAML is atricky little format.

1 ice_water: 2 id: 1 3 title: Ice Cream 4 ingredients: 3 scoops vanilla ice cream&lt;br/chocolate syrup 5 instructions: Scoop ice cream into the bowl and pour chocolate syrup on top. 6 toast: 7 id: 2 8 title: Toast 9 ingredients: bread, butter, jelly 10 instructions: Place bread in the toaster for 1 minute. Remove from toaster and apply butter to each piece.

When the test is run, this data gets loaded into the recipes table and is available within the test. The testsyou currently have in your test don’t need these fixtures, but you may write future tests that depend onhaving data in the test database. For example, you may want to write a test to make sure that there canonly be one recipe called “Toast”. That test might look something like this:

1 def test_should_only_have_one_recipe_called_toast 2 @recipe = Recipe.new(:title =>"Toast")

Page 19: Rails Cookbook

Validating User Input

Copyright (c) 2008 Brian P. Hogan 12 http://www.napcs.com/resources/rails

3 @recipe.valid? 4 Assert @recipe.errors.on(:title).include?("must be unique") 5 end

2.3. Running the Test

To run the test, we need to first prepare the test database. We do that by running the rake task

rake db:test:clone

This task takes the structures from our development database and creates a new test database that we canuse over and over again.

Our test is actually a standalone Ruby application. We can run the test directly using Ruby.

ruby test\unit\recipe_test.rb

Everything should work well. You should get no errors or failures.

Note

You can run all of the unit tests by running rake test:units

3. Providing Feedback to UsersNow that we know our validation works, we should see what it looks like when users attempt to leavefields blank.

Active Record’s Validations places error messages in an array which can be accessed by a helper methodin a view. The helper method error_messages_for takes in an instance of an Active Record model andattempts to display the error messages in a nice friendly manner. Built in helper methods for text boxes,select boxes, and text areas also are validation-aware. They will become styled automatically, providingadditional visual cues. The styles are applied using CSS, so you can modify the way they look. Take alook at Figure 3.1, “User Feedback as provided by Rails validations” to see the results.

Page 20: Rails Cookbook

Validating User Input

Copyright (c) 2008 Brian P. Hogan 13 http://www.napcs.com/resources/rails

Figure 3.1. User Feedback as provided by Rails validations

This task alone could take a web developer a few hours to get right. We’ve created a working solutionwith a unit test in only a few minutes. These validations work for new entries and existing entries.

There are numerous plugins available for Rails to change how validation works at the web page level.Plugins are available at >http://www.agilewebdevelopment.com/plugins

Page 21: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 14 http://www.napcs.com/resources/rails

Chapter 4. Cleaning Up the ScaffoldingAs stated before, the scaffolding needs some work before it can be used in production. As you becomemore familiar with the way Rails works, you may find yourself relying less and less on scaffolding tocreate your pages.

1. Cleaning up the List pageThe list page is pretty good for starters, but one thing that’s kinda rough about it is that it requires Javascriptto be enabled in order to delete records! Yuck!.

It might be nice to show just the recipe name and when it was last updated instead of the ingredientsand instructions. Remember, the list page is built using whatever fields you specified in your scaffoldcommand.

Replace the contents of the page views/recipes/index.erb.html with the following code:

1 <h1>Listing recipes</h1> 2 3 <table> 4 <tr> 5 <th>Title</th> 6 <th>Last Updated</th> 7 <th colspan="2">&nbsp;</th> 8 </tr> 9 10 <% for recipe in @recipes %> 11 <tr> 12 <td><%= link_to h(recipe.title), recipe %></td> 13 <td><%= time_ago_in_words recipe.updated_at.to_time %> 14 <td><%= link_to 'Edit', edit_recipe_path(recipe) %></td> 15 <td><%= button_to 'Destroy', recipe, :confirm => 'Are you sure?', :method => :delete %></td> 16 </tr> 17 <% end %> 18 </table> 19 20 <br /> 21 22 <%= link_to 'New recipe', new_recipe_path %>

Refresh the index page in your browser. Your new page should resemble something like the one in Fig-ure 4.1, “The modified Index page”.

Page 22: Rails Cookbook

Cleaning Up the Scaffolding

Copyright (c) 2008 Brian P. Hogan 15 http://www.napcs.com/resources/rails

Figure 4.1. The modified Index page

1.1. HelpersRails provides many helper methods to make displaying pages easier. This example shows a few helpers:

• link_to : Used to create a hyperlink. We use this because Rails can manage links for us so we don’tneed to worry about relative or absolute paths. Rails has a routing system that makes linking thingstogether a snap.

• h : This simple helper escapes HTML and Javascript. It helps to sanitize output, in case someone putmalicious JavaScript into one of your input fields.

• time_ago_in_words : This method takes a time and tells you in words how long ago it was. Thisis how we get those neat “posted five minutes ago” messages on blogs.

The Rails documentation 1 has more information on helpers.

Why Button_to instead of Link_to

Notice that the delete feature is a button now instead of links? For safety reasons, we want all linksto destructive actions like deletes to be called via post methods. The button_to tag does this for us.We can then style it using CSS so it looks nicer later on.

2. Cleaning up the Show pageTake a look at app/controller/recipes_controller.rb and find the show section

1 def show 2 @recipe = Recipe.find(params[:id]) 3 end

This code retrieves the recipe from the database. The id sent via the url is sent to the database. This actuallygenerates the sql statement “select * from recipes where id = 1”. The find method is a class methodof Recipe and returns an instance of a recipe object. Models in Active Record handle the retrieval andrepresentation of the data.

1http://api.rubyonrails.com/

Page 23: Rails Cookbook

Cleaning Up the Scaffolding

Copyright (c) 2008 Brian P. Hogan 16 http://www.napcs.com/resources/rails

Here we see that it stores the resulting Recipe instance into an instance variable (The @ means instancevariable in Ruby.) The instance variable is passed on to the show.html.erb file. Knowing that, we can easilydisplay the information about our recipe.

Open app/views/recipe/show.html.erb and replace the contents with

1 <h2><%=h @recipe.title %> [<%= link_to 'Edit', edit_recipe_path(@recipe) %> ]</h2> 2 3 <h3>Ingredients:</h3> 4 <p><%=h @recipe.ingredients %></p> 5 6 <h3>Instructions</h3> 7 <p><%=h @recipe.instructions %></p> 8 9 <%= link_to 'Back', recipes_path %>

Your page should look something like Figure 4.2, “The Show Page”

Figure 4.2. The Show Page

3. Using Partials to share common codeThe New and Edit forms are virtually identical except for their headings. The actual form could be sharedacross both files using a partial which is similar to an include file in PHP.. This way, if you add a field tothe form, you only need to add it to one file instead of two. In Rails 1.x, this was handled by the scaffoldgenerator, but in Rails 2.0, this is no longer the case. Let’s add it back.

Create a new file in app/views/recipes called _form.html.erb. The filename should beginwith an underscore because that’s how Rails distinguishes partials from regular views. Open up thenew.html.erb file and find this code:

1 <p> 2 <b>Title</b><br /> 3 <%= f.text_field :title %> 4 </p> 5 6 <p> 7 <b>Ingredients</b><br /> 8 <%= f.text_area :ingredients %> 9 </p> 10 11 <p> 12 <b>Instructions</b><br /> 13 <%= f.text_area :instructions %> 14 </p>

Page 24: Rails Cookbook

Cleaning Up the Scaffolding

Copyright (c) 2008 Brian P. Hogan 17 http://www.napcs.com/resources/rails

Copy that code to your clipboard and paste it into _form.html.erb. Once it's pasted, remove the codefrom the new.html.erb file.. Open edit.html.erb and locate the same code. Remove it from thefile.

Now add this line to edit.html.erb, in place of the code you just removed:

1 <%= render :partial=>"form", :locals=>{:f => f} %>

The :locals => {:f => f} option allows you to pass variables into the partial. Since the variablef for the form is a local variable, it needs to be passed to the partial so the partial can “see” it and access it.

The edit.html.erb file should now look like this:

1 <h1>Editing recipe</h1> 2 3 <%= error_messages_for :recipe %> 4 5 <% form_for(@recipe) do |f| %> 6 7 <%= render :partial=>"form", :locals=>{:f => f} %> 8 9 <p> 10 <%= f.submit "Update" %> 11 </p> 12 <% end %> 13 14 <%= link_to 'Show', @recipe %> | 15 <%= link_to 'Back', recipes_path %>

Add the same line of code to new.html.erb. in place of the code you previously removed. You’re nowsharing code between two views using a partial.

4. Where’s the rest of my HTML?You’ve probably noticed that our view pages have not included any required HTML elements, nor havethey mentioned anything about a style sheet. Yet we can see styles being rendered and we see a lot ofHTML markup if we view the source. Where’s this coming from?

Rails has a wonderfully complex yet simple template mechanism. Look in the app/vies/layout folderand you’ll see a file called recipes.html.erb. This file was created during your scaffold operationand is automatically linked to the recipes controller.

This file wraps any view files you render from within the recipes controller. If you want one single layoutfor your entire application, you can rename this file to application.html.erb and every controllerwill use it.

This file can use any variables set in the controller, including variables created within the view files them-selves, because it is read last before being sent to the browser. This means you can set the page title in theindividual controller actions or even in the .erb files themselves.

Page 25: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 18 http://www.napcs.com/resources/rails

Chapter 5. Adding CategoriesA recipe should be able to be categorized. For this example, we’ll just say that a recipe can only belongto one category, and a category can have many recipes. We’ll say it that way because that’s how ActiveRecord allows us to express relationships.

1. Create a category model and tableWe don’t need to create a full interface to manage categories at this time, so create the new model bydropping to a command prompt and typing

ruby script/generate model Category

Open the created migration file at db/migrate/002_create_categories.rb and change it to

1 class CreateCategories < ActiveRecord::Migration 2 def self.up 3 create_table :categories do |t| 4 t.string :name 5 t.timestamps 6 end 7 8 end 9 10 def self.down 11 drop_table :categories 12 end 13 end

Run the migration by executing the command rake db:migrate from the command line. It will createthe new table.

2. Adding some default records with Rake Sometimes it’s nice to have your database pre-populated with records. You saw how fixtures can dothat with test data, but that’s not always a good choice. Migrations could be used to insert data into yourdatabase but that can be volatile as well. The best approach is to use rake, which is the same tool you’vebeen using to run your migrations.

Rake is an automation language. To use it, you simply create a file with some tasks and then execute itvia the rake command.

Rails projects look for Rake tasks in files with the .rake extension in the project’s lib/tasks folder.Create a new file in that folder called import.rake. Place this code in the file:

1 namespace :db do 2 3 desc "Puts default categories in the database" 4 task :import_categories => :environment do 5 6 Category.create :name =>"Beverages" 7 Category.create :name =>"Deserts" 8 Category.create :name =>"Appetizers" 9 Category.create :name =>"Entrees" 10 Category.create :name =>"Breakfast"

Page 26: Rails Cookbook

Adding Categories

Copyright (c) 2008 Brian P. Hogan 19 http://www.napcs.com/resources/rails

11 Category.create :name =>"Sandwiches" 12 13 end 14 end

A namespace is just a container for code like in any other language. When you issued the command rakedb:migrate you called the migrate task within the db namespace. We’ll follow that same convention here.

To import the records into your database ,issue the command

rake db:import_categories

3. Modifying the Recipes tableSince we want to have a relationship between categories and recipes, we have to place a foreign key in therecipes table so it can be associated with a recipe. We’ll do this with a migration too.

Create a new migration. From the command line, execute the command

ruby script/generate migration recipe_category

This will create a new migration file db/migrate/003_RecipeCategory.rb. Open the file andreplace it with the following code:

1 class RecipeCategory < ActiveRecord::Migration 2 def self.up 3 add_column :recipes, :category_id, :integer 4 end 5 6 def self.down 7 remove_column :recipes, :category_id 8 end 9 end

Run the migration to alter the database. (rake db:migrate).

At this point you will need to stop and restart your web server. This is only necessary with SQLite3, andonly because you changed a table's structure.. Other databases can be modified without restarting the webserver. Press CTRL+BREAK to stop Mongrel and then restart it by executing the command

ruby script/server

4. Creating an Association Between a Recipeand a Category

Associations allow objects to interact. Associations are methods that map the primary keys of one tableto the foreign keys of another; the relational mapping part of “object-relational mapping”.

Open app/models/recipe.rb and modify its contents with the following code:

1 class Recipe &lt; ActiveRecord::Base 2 belongs_to :category 3 validates_presence_of :title, :ingredients, :instructions 4 end

Page 27: Rails Cookbook

Adding Categories

Copyright (c) 2008 Brian P. Hogan 20 http://www.napcs.com/resources/rails

5

The belongs_to method takes in a symbol name of a class with which we wish to associate. Rails needsno further information because it will assume that :category references a class called Category, that thetable name will be categories, and that this table (recipes) has a foreign key column called category_idthat will reference the id column in the categories table. Of course, we can override these assumptions,but it’s easier just to follow convention.

This association adds some new methods to an instance of Recipe. We can now access the name of therecipe directly.

recipe = Recipe.find(1) # gets recipe with id of 1recipe.category.name # gets the associated category name

4.1. Lazy vs. Eager LoadingThe above code will do the retrieval using lazy loading, meaning that two SQL statements will be called.This could be bad if we were retrieving all recipes and displaying the category for each one. If you have200 recipes, the above code would generate 201 SQL statements!

Thankfully there is a solution for situations like this… eager loading. Rails will generate proper left joinsfor us if we specify the objects to include.

recipe = Recipe.find(1)

becomes

recipe = Recipe.find(1, :include => [:category])

and now only one statement is sent to the database.

5. Adding categories to the controllers andviews

In order to add the category selection to the forms and views, we need to do some work in the controller.

5.1. The New and Edit formsOpen app/controller/recipes_controller.rb and locate the new method. Modify it so itretrieves the categories into an instance variable called @categories. Remember that a controller’sinstance variables are then accessible in the view pages.

1 def new 2 @recipe = Recipe.new 3 @categories = Category.find :all 4 end

Now find the edit method and modify it so it also retrieves the categories into @categories.

1 def edit

Page 28: Rails Cookbook

Adding Categories

Copyright (c) 2008 Brian P. Hogan 21 http://www.napcs.com/resources/rails

2 @recipe = Recipe.find(params[:id]) 3 @categories = Category.find :all 4 end

Open app/views/recipes/_form.html.erb and add the following block at the end of the file:

1 <p> 2 <b>Category</b><br /> 3 <%= f.select :category_id, @categories.collect{|c| [c.name, c.id] }, :include_blank => true %> 4 </p>

This code adds a select box which will contain all of the categories so that your users can place a recipeinto the category chosen by the dropdown. This is an example of the select helper.

The .collect method iterates through all the categories and returns an array. In this case we’re returningan array of arrays which the select helper can use to build the form. This is an example of a block andyou’ll see lots of these in Rails applications.

We’re also including a blank option so that a user doesn’t have a category already selected when theyview the page.

5.2. The Show viewWhen we display a recipe, we want to now show the category for the recipe. We can do that easily thanksto the way the belongs_to association works. When we associated a category to a recipe using thatassociation, it added a method to our Recipe model called category, which returns an instance of theassociated category record.

Locate the show action in recipes_controller and add eager loading for the category using the:include option for find.

1 def show 2 @recipe = Recipe.find(params[:id], :include=>[:category]) 3 4 respond_to do |format| 5 format.html # show.html.erb 6 format.xml { render :xml => @recipe } 7 end 8 end

Open app/views/recipes/show.html.erb and add

<p>Category: <%= h(@recipe.category.name) rescue “No category found” %></p>

somewhere on the page. When you refresh, you'll see the category displayed.

Tip

The rescue statement catches a possible exception that could be thrown if the recipe doesnot yet have an assigned category. Your recipes don't all have the category assigned yet, andwithout this rescue statement, this page would fail to render.

Page 29: Rails Cookbook

Adding Categories

Copyright (c) 2008 Brian P. Hogan 22 http://www.napcs.com/resources/rails

5.3. The List viewFind the index action in recipes_controller.rb and add the :include option to the find toeager-load the category information just like the previous example.

1 def index 2 @recipes = Recipe.find(:all, :include=>[:category]) 3 4 respond_to do |format| 5 format.html # index.html.erb 6 format.xml { render :xml => @recipes } 7 end 8 9 end

Open app/views/recipes/index.html.erb and modify it so you can see the category name in the table. You’llneed to add a column heading as well as the data cell itself. Remember to use the association to retrievethe category name just like you did on the show page!

6. Test it outWith the associations in place and the views fixed up, go ahead and play with your application. Notice howyou can now add categories to your recipes, and when you edit an existing recipe, its associated categoryautomatically shows up in the dropdown list.

Page 30: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 23 http://www.napcs.com/resources/rails

Chapter 6. Other Rails TidbitsThere's a lot more to the Rails framework than what we covered here. Let's explore a few other features.

1. Writing Documentation with RDocDocumenting code is one of the most useful things a developer can do. Unfortunately it’s often done poorlyif it’s even done at all.

Ruby on Rails aims to change how developers write documentation by making use of RDoc. RDoc is aprogram that can parse Ruby files for comments and convert these comments to HTML pages or otherformats. It generates very clean and nice-looking documentation and is so easy to use that developersquickly come to actually enjoying documentation.

Any comments located directly above a class or method declaration will be interpreted by the RDOC parserto be the comments for that given block of code.

Here’s an example of some commented code.

1 #=Recipes 2 # Recipes are added, removed, maintained, and viewed using 3 # the actions in this controller. 4 #==Authentication 5 # There is no authentication on this controller 6 class RecipesController < ApplicationController 7 8 # This action handles the default document (Index) and 9 # simply redirects users to the list action. 10 def index 11 list 12 render :action => 'list' 13 end 14 15 end

When we run the command rake appdoc, our HTML documentation will be created for us. SeeFigure 6.1, “RDoc output in HTML”

Figure 6.1. RDoc output in HTML

Page 31: Rails Cookbook

Other Rails Tidbits

Copyright (c) 2008 Brian P. Hogan 24 http://www.napcs.com/resources/rails

2. Annotating ModelsOne of the things that can get confusing with Rails is the fact that the models don’t have much code.Because the methods are generated for you, you can’t look at a model and tell what database fields yourefer to unless you were to comment them yourself.

Thankfully we can handle this simply by installing a plugin called annotate_models written by the creatorof RDoc, Dave Thomas. Install the plugin by executing this command from the command line

ruby script/plugin install http://repo.pragprog.com/svn/Public/plug-ins/annotate_models/

Note

You’ll need to have the Subversion client tools installed on your machine for this to work andthe svn command needs to be on your path. Visit http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 to get the latest version if youdon't have it..

After installation, be sure to close any open command prompt windows. Then re-open yourcommand prompt window and navigate back to the root of your project.)

Once installed, run the command

rake annotate_models

Your models will now be commented with the schema definition. This will then be placed in your docu-mentation the next time you generate your docs. You can see the output in Figure 6.2, “Annotations addedto a model”

Figure 6.2. Annotations added to a model

3. Debugging and Exploring with ConsoleOne of the ways you can debug a Rails application is with unit tests. However, often times you might notknow what to write. That’s where console comes in. Console lets you load the entire Rails application intothe Interactive Ruby environment so you can execute commands and interact with your application.

From the root of your project, execute the command

Page 32: Rails Cookbook

Other Rails Tidbits

Copyright (c) 2008 Brian P. Hogan 25 http://www.napcs.com/resources/rails

ruby script/console

to enter the console. Once the console is loaded, you can start experimenting with your objects, as shownin Figure 6.3, “Using the Rails Console to work with objects”

Figure 6.3. Using the Rails Console to work with objects

In the above example, I use the console to create a new recipe and save it to the database. I then retrievethe id for the recipe. Then I use the find method on Recipe to locate the recipe again. Then I see if it hasa category. Of course, it doesn’t so I fetch a category from the database and assign it to my instance. Theassociation is not saved until I execute the save method of my instance.

This is just the beginning, but it shows how you can use the console to learn more about how the methodson the classes work without having to write any view pages or controller code.

4. LoggingRails applications automatically log requests and responses to the various logs. One log you should reallykeep an eye on is your development.log file. It contains a lot of useful information such as the parameterssent on each request as well as the SQL statements created by Rails and sent to the database.

This is the place you’ll want to look to tune your application. Not only can you see if you’re executingtoo many SQL statements for the job at hand, but you can also see how long it took Rails to serve therequest to your client.

5. Writing your own SQL statementsAt first glance, Rails may seem limited. We’ve gone through this entire project without writing any SQL.A lot of the time we won’t have to worry about it. However, it is still very possible for us to get into thecode and do what we need to do.

For example, one of the methods in Active Record is called find_by_sql which allows us to look recordsup using our own custom SQL statement.

@results = Recipe.find_by_sql "select r.title, c.name from recipes r join categories c

Page 33: Rails Cookbook

Other Rails Tidbits

Copyright (c) 2008 Brian P. Hogan 26 http://www.napcs.com/resources/rails

on r.category_id = c.id"

=> [#<Recipe:0x3750478 @attributes={"name"=>"Beverages", "title"=>"Test"}>]

You have to understand Ruby to understand what this example returns, so I’ll help out. The square brackets([]) surrounding the result means that you're dealing with an Array. The #<Recipe piece means it’s aRecipe object. So when you use the find_by_sql method, you receive an array of objects which youcan then iterate over.

@results.each do |recipe| puts recipe.name # print to STDOUT puts recipe.title # print to STDOUT end

Note that a new method title has been created in the instance of the class. Active Record inspected thecolumn names that came back from the database and dynamically created accessor methods for us to use.

Warning

Never use puts in your Rails application directly. It can cause problems that you may notfind later on. It’s only to be used in tests and in the console to help you debug. If you’rewondering, it’s equivalent to system.out.println or echo.

There are many other features in Rails that make it extremely flexible. Don’t get fooled by the hype aboutORM and scaffolding. Rails is much more than that!

Page 34: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 27 http://www.napcs.com/resources/rails

Chapter 7. Development andDeployment

1. Exploring Development ToolsThere are several different tools you can use to easily build Rails applications.

• If you’re on a Mac, you have to check out TextMate. While not an IDE, it is built with Rails developmentin mind and is just a dream to work with when you see it. It’s the featured editor in all of the Railsscreencasts. See http://macromates.com/ for more information.

• Those interested in a full-blown IDE for Rails development will love NetBeans Ruby IDE from Sun(yup, the Java guys). Snag your copy at http://download.netbeans.org/netbeans/6.0/final/ and be sure tograb the one for Ruby. You will need the Java SDK as well to run this, but it’s worth it.

• Windows users might like to give E-Texteditor a try. It’s a clone of Textmate, and while not nearly asfeature-rich yet, it’s one of the best editors available on Windows so far.

• Advanced users will definitely want to look at Cream with the rails.vim plugin

Cream: http://cream.sourceforge.net/

Rails.vim plugin: http://www.vim.org/scripts/script.php?script_id=1567

2. Deploying Rails ApplicationsDeploying Rails applications is no trivial task. Rails applications require more setup work than PHP ap-plications and there are lots of things that can go wrong when you’re first starting out. Do not attemptdeployment until you are very comfortable with the Rails framework.

Applications can be deployed using FastCGI and Lighttpd or Apache, or by using the Mongrel web serveralongside a load-balancing mechanism like Apache 2.2, Pound, Pen, or Nginx.

There are many web hosting companies that support Rails Shared hosts such as Dreamhost, Site5,Textdrive, RailsPlayground, and Bluehost keep the cost low by sharing space and memory with otherusers. This is a great low-cost way to launch your application.

If you need high availability, you can look at RailsMachine and EngineYard, two solutions that promiseto host large Rails sites with ease. They are significantly more expensive than shared hosting plans. En-gineYard is incredible, but you can’t afford it unless you are really making money. It’s worth every pennythough.

If you just want to set things up yourself on dedicated virtual servers, you could look at LayeredTech,Linode, and Rimuhosting. Rimuhosting provides good support for Rails on their virtual servers, as doesLinode.

More information on deployment can be found in the book Deploying Rails Applications from the Prag-matic Programmers.

Page 35: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 28 http://www.napcs.com/resources/rails

Chapter 8. Where To Go Next?Hopefully this exercise gave you enough of an overview of the Ruby on Rails framework to see the po-tential impact it has on rapid application development. From here, you should be able to extend this as-signment by doing the homework or explore further by coming up with your own application, using thisas a model.

1. Books• Learn to Program (Chris Pine)

http://www.pragprog.com/titles/fr_ltp

• Agile Web Development with Rails (Dave Thomas)

http://www.pragmaticprogrammer.com/titles/rails/index.html

• Ruby for Rails (David A. Black)

http://www.manning.com/black/

• Programming Ruby (Dave Thomas)

http://www.pragprog.com/titles/ruby3 or older version online for free at http://www.rubycentral.com/book/

• Deploying Rails Applications (Ezra Zygmuntowicz, Bruce Tate, Clinton Begin, Geoffrey Grosenbach,and Brian Hogan)

http://www.pragprog.com/titles/fr_deploy

• Rails for Java Developers (Stuart Halloway and Justin Gehtland)

http://www.pragprog.com/titles/fr_r4j

• Rails for PHP Developers (by Derek DeVries and Mike Naberezny)

http://www.pragprog.com/titles/ndphpr

• The Rails Way (Obie Fernandez)

http://www.amazon.com/Rails-Way-Addison-Wesley-Professional-Ruby/dp/0321445619

2. Online Resources• Ruby on Rails Discussion group (http://groups.google.com/group/rubyonrails-talk)

• #rubyonrails IRC channel (http://wiki.rubyonrails.org/rails/pages/IRC)

• Peepcode (http://peepcode.com/)

• Railscasts (http://railscasts.com/)

• Railsforum (http://www.railsforum.com/)

Page 36: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 29 http://www.napcs.com/resources/rails

Chapter 9. Homework and ExplorationIf you want to take the exercise further, you can try some of the ideas below. The answers are not providedbut the solutions for each problem can be found by looking at similar exercises in the preceding tutorial.

1. Document some other methods in your controllers and models and then regenerate the docs. Here aresome simple formatting symbols:

• # is the comment

• = is a large header

• == is a level 2 header

• --- is a horizontal rule

• * is a bullet

2. Create a has_many association from Category to :recipes

• Write a unit test that tests this relationship. (You’ll need to make fixtures for categories and recipesand you’ll need to load both of these in the test file.)

• See http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html for details onhas_many

3. Create a controller and views to manage the categories. Use the recipe controller and views as an ex-ample. If you choose to scaffold this, you may need to undo some work you’ve done so be careful.It’s easier to write it by hand.

• When you display a category, display the recipes associated with that category, making use of thehas_many association you created.

• On the Show page for a recipe, make a link to the category

<%=link_to @recpie.category, show_category(@recipe.category) %>

4. Extra Credit: When a category is deleted, set all associated recipes to a nil category using abefore_destroy ActiveRecord callback.

Page 37: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 30 http://www.napcs.com/resources/rails

Chapter 10. Support This TutorialThis Rails crash-course has been supported by many wonderful donors just like you. If you foundthis useful, please send a small donation via Paypal by visiting http://www.napcs.com/re-sources/rails/cookbook. The latest version of this book is always available there as well.

Thank you for your support.

Page 38: Rails Cookbook

Copyright (c) 2008 Brian P. Hogan 31 http://www.napcs.com/resources/rails

Index, 15

Aaccessor, 9Array, 26Associations, 19

belongs_to, 20

CConsole, 24Controllers, 1

Ddatabase.yml, 4Databases

Adding default data, 18Eager Loading, 20

DocumentationAnnotating Models with schema info, 24generating documentation with RDOC, 23

EException Handling

rescue, 21

FFixtures, 11

GGenerators, 5

scaffold, 6

HHelper methods

link_to, 15Helpers, 2

LLayouts, 2, 17

default layout, 17

MMigrations, 7

running, 7Model-View-Controller pattern, 1Models, 1

database reflection, 8dynamic method creation, 9

PPartials, 2, 16

passing variables into, 17

RRake

automating tasks with, 18Rake tasks

Cloning the test database from the developmentdatabase, 12

RDoc, 23example, 23Generating documentation, 23

Refactoring, 2Relationships, 19

see Associations, 19Rubygems

Gems, 3multiple versions, 3

SScaffolding, 5

issues with, 6script/server, 8SQL

statements in the Rails logs, 25writing your own, 25

SQLite3automatic creation, 5installation of, 5

Symbols, 9

TTest-driven development

TDD, 2

UUnit Tests, 10, 11

running, 12

VValidations, 9

displaying errors, 12validates_presence_of, 9

Views, 1