By Bruce A. Tate, Curt Hibbs
Book Price: $29.99 USD
£20.99 GBP
PDF Price: $20.99
Cover | Table of Contents | Colophon
gem installer accesses a web site, Ruby Forge, and downloads an application unit, called a gem, and all its dependencies. You can install Rails through gems, requesting all dependencies, with this command:
gem install rails --include-dependencies
http://rubyonrails.org for more details on Rails installation. Next, here's how to create a Rails project:cd chapter-1 to change to your project directory. Use the script/server script to start an instance of the WEBrick server, configured for development. If you're running Windows, preface each call to a script with ruby, and you can use either forward or backward slashes. If you're using a Unix derivative, you can omit the ruby keyword:
> ruby script/server
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2006-05-11 07:32:08] INFO WEBrick 1.3.1
[2006-05-11 07:32:08] INFO ruby 1.8.4 (2005-12-24) [i386-mswin32]
[2006-05-11 07:32:08] INFO WEBrick::HTTPServer#start: pid=94884 port=3000
> ruby script/generate controller Greeting
exists app/controllers
/
exists app/helpers/
create app/views/greeting
exists test/functional/
create app/controllers/greeting_controller.rb
create test/functional/greeting_controller_test.rb
create app/helpers/greeting_helper.rb
generate command to generate a new controller, greeting, with a view, index. (You do this to tie the view and controller together.) When it asks you whether to overwrite the controller, type n for no:
> ruby script/generate controller Greeting index
exists app/controllers/
exists app/helpers/
exists app/views/greeting
exists test/functional/
overwrite app/controllers/greeting_controller.rb? [Ynaq] n
skip app/controllers/greeting_controller.rb
overwrite test/functional/greeting_controller_test.rb? [Ynaq] @welcome_message to the controller:
class GreetingController < ApplicationController
def index
@welcome_message = "Welcome to your first Rails application"
end
end
<%= and %> tags. Rails renders the value of the expression within these tags, just as if the value of the expression had been printed in place. Here's a view that prints your welcome message as a level one heading:<h1><%= @welcome_message %></h1>
<% and %> tags. Scriptlets rely on side effects, or the output of the Ruby code. Expressions are Ruby expressions
placed between <%= and %> tags. The expression presents the value returned by the Ruby code.
class GreetingController < ApplicationController
def index
@age=8
@table={"headings" => ["addend", "addend", "sum"],
"body" => [[1, 1, 2], [1, 2, 3], [ 1, 3, 4]]
}
end
end
<h1>Simple expression</h1> <p>Tommy is <%= @age %> years old.</p>
|
Difference
|
Benefit
|
|---|---|
|
Rails adds attributes automatically, based on the columns in the database.
|
Rails developers do not have to specify attributes in more than one place.
|
|
Rails adds relationship management and validation through a custom internal language. |
|
Difference
|
Benefit
|
|---|---|
|
Rails adds attributes automatically, based on the columns in the database.
|
Rails developers do not have to specify attributes in more than one place.
|
|
Rails adds relationship management and validation through a custom internal language.
|
Rails developers can declare relationships and model-based validation to be managed by the framework without relying on code generation.
|
|
Rails naming conventions let the database discover specific fields.
|
Rails developers do not need to configure primary and foreign keys because Active Record discovers them automatically.
|
photos and the definition of the id column are both significant. (With our migration, Rails created the id column automatically.) Rails uses several naming conventions:id and uses it as a unique identifier. The id column should be an integer type, and the column should be populated by the database. In this case, we'll use an auto-increment sequence. Staying with these conventions saves you some configuration, and also makes your code much easier to understand.<class>_id. For example, our slides table will have a foreign key named photo_id.ChunkyBacon, you'd use the symbol :chunky_baconPhoto class. From the class name Photo, Active Record infers that the database table name is photos. It then queries the database system tables, which have detailed information about the tables in the database, to get the photos table definition. Next, it places information about the definition of each column into the @@columns
class variable. @@columns is an array of Column objects; each column has these attributes:true if the column's data is numeric. You'll access it through the accessor method number?.varchar(45), the limit would be 45.true only if the column can be set to null. You'll access it through the accessor method Person and Photographer go into the same table. Consider this table:CREATE TABLE people ( id INT AUTO_INCREMENT NOT NULL, type VARCHAR(20), name VARCHAR(20), email VARCHAR(30), camera VARCHAR(20), PRIMARY KEY (id) );
Person will return people and photographers. Active Record doesn't need to build any special support to handle a query against a superclass. Subclasses are more difficult. In order to allow a query returning only Photographers, Active Record must have some way to determine the type of an object for an individual row. Active Record uses the type field for this purpose.class Photographer < Person end class Person < ActiveRecord::Base end
Photographer as a subclass of Person. Active Record will manage the type attribute and everything else. You'll be able to access the camera property from Photographer. We don't need these classes for Photo Share, so we'll delete them.ActiveRecord::Base class supplies many of the methods, and missing_method provides most of the rest. You can find the documentation for the latest stable Active Record version online at the following address: http://api.rubyonrails.com.find_by_sql lets you type SQL directly into a finder; find_all returns all records. In addition, Active Record adds a custom finder called find_by_<column_name> to each model class for each column in that model's table.Photo. We'll use a finder and the destroy method to delete an object from the database:
Photo.find_by_filename("balboa_park.jpg").destroy
delete and destroy are slightly different. delete aborts on minor errors, but destroy does not abort unless there's a critical database error. You can also update objects. Let's update the Photo object for cat.jpg:>> cat=Photo.find_by_filename "cat.jpg" ... >> cat.filename="Cat.jpg" ... >> cat.update ... >> puts cat.reload.filename Cat.jpg ...
filename attribute and called update to write the changes to the database.filename property exists with one line of code. Change the Photo class in app/models/photo.rb to look like this:class Photo < ActiveRecord::Base validates_presence_of :filename end
belongs_to
has_one
has_many
has_and_belongs_to_many
acts_as_list
acts_as_tree
ActiveRecord::Base, which defines the behavior of the relationship.
relationship :association :parameter1 => value, :parameter2 => value,...
class Slideshow < ActiveRecord::Base
has_many :photos :order => position
Slide class. We'll then need a many-to-one relationship between slides and slideshows: a slideshow consists of many slides, but each slide (a photo combined with a position) can belong to only one slideshow. To give us the flexibility that we need (we'll also want the ability to reuse photos in different slideshows), we'll need another relationship between photos and slides, but let's leave that for later.
Slide, and another for Slideshow:
ruby script/generate model Slideshow
ruby script/generate model Slide
up step will create the slides and slideshows tables, and the down step will drop them, like this:
class CreateSlideshows < ActiveRecord::Migration
def self.up
create_table "slideshows" do |t|
t.column "name", :string
t.column "created_at", :datetime
end
end
def self.down
drop_table "slideshows"
end
end
class CreateSlides < ActiveRecord::Migration
def self.up
create_table "slides" do |t|
t.column "position", :integer
t.column "photo_id", :integer
t.column "slideshow_id", :integer
end
end
def self.down
drop_table "slides"
end
end
has_many relationships on both Photo and Slideshow. Figure 3-2 shows the mapping between Active Record objects and database tables with has_many.
has_many is the other side of a belongs_to relationship, so you don't need to modify the class or table for Slide. You can merely add the relationship has_many to slideshow.rb:class Slideshow < ActiveRecord::Base has_many :slides end
class Photo < ActiveRecord::Base has_many :slides validates_presence_of :filename end
>> slide = Slide.find 1 ... >> slideshow = slide.slideshow ... >> slideshow.slides.each {|slide| puts slide.photo.filename} balboa_park.jpg camel.jpg cat_and_candles.jpg hut.jpg mosaic.jpg polar_bear.jpg police.jpg sleeping_dog.jpg stairs.jpg
has_many relationship between Slideshow and Slide. You could use photo.slides in the same way. Table 3-2 shows you the metaprogramming for has_many.belongs_to or has_one. You decide whether to use belongs_to or has_one based on where the foreign key resides. The class associated to the table with the primary key uses belongs_to, and the other uses has_one. Figure 3-3 shows a has_one relationship.
photo_id into the files table, you would have this Active Record Photo class:class Photo < ActiveRecord::Base has_one :file ... end
has_one is identical to belongs_to with respect to metaprogramming. For example, adding either has_one :photo or belongs_to :photo to Slide would add the photo attribute to Slide. We really have no need for adding an extra table to manage a file, so let's move on to the next relationship.
Photo and Category. A category can hold many photos, and the same photo can fit into more than one category. As always, you'll start with the database. You'll need to create a table called http://api.rubyonrails.com for details.ActiveRecord::Base.lock_version, if it exists, to manage concurrency using a technique called optimistic locking.
With this technique, a database engine can store multiple versions of each piece of data and maintain database integrity if many applications need the same piece of data.