Contents 

Ruby on Rails:
Table of Contents
Preface
Zero to Sixty: Introducing Rails
1.1. Rails Strengths
1.2. Putting Rails into Action
1.3. Organization
1.4. The Web Server
1.5. Creating a Controller
1.6. Building a View
1.7. Tying the Controller to the View
1.8. Under the Hood
1.9. What's Next?
Active Record Basics
2.1. Active Record Basics
2.2. Introducing Photo Share
2.3. Schema Migrations
2.4. Basic Active Record Classes
2.5. Attributes
2.6. Complex Classes
2.7. Behavior
2.8. Moving Forward
Active Record Relationships
3.1. belongs_to
3.2. has_many
3.3. has_one
3.4. What You Haven't Seen
3.5. Looking Ahead
Scaffolding
4.1. Using the Scaffold Method
4.2. Replacing Scaffolding
4.3. Generating Scaffolding Code
4.4. Moving Forward
Extending Views
5.1. The Big Picture
5.2. Seeing Real Photos
5.3. View Templates
5.4. Setting the Default Root
5.5. Stylesheets
5.6. Hierarchical Categories
5.7. Styling the Slideshows
Ajax
6.1. How Rails Implements Ajax
6.2. Playing a Slideshow
6.3. Using Drag-and-Drop to Reorder Slides
6.4. Drag and Drop Everything (Almost Everything)
6.5. Filtering by Category
Testing
7.1. Background
7.2. Ruby's Test::Unit
7.3. Testing in Rails
7.4. Wrapping Up
Installing Rails
1.1. Windows
2.1. OS X
3.1. Linux
Quick Reference
5.1. General
5.2. Testing
5.3. RJS (Ruby JavaScript)
5.4. Active Record
5.5. Controllers
5.6. Views
5.7. Ajax
5.8. Configuring Your Application
About the Authors
Colophon
Index
A
B
C
D
E
F
G
H
I
J
L
M
N
O
P
R
S
T
U
V
W
X
Y
Z

Ruby on Rails manual

Prev Page Next Page
Previous Page
Next Page

2.5. Attributes

You've now seen metaprogramming in action through the console. Your applications will use your model objects in the same way. One of the drawbacks of Active Record is the terseness of the source codeit won't tell you much. If you know what's going on under the covers, though, you can easily understand what attributes and methods your class supports.

2.5.1. Columns

Let's review what happens when Ruby loads the Photo 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:



name

The name of the database column.



type

The Ruby type of the attribute this column will generate.



number

A Boolean value that's TRue if the column's data is numeric. You'll access it through the accessor method number?.



limit

The maximum size of the data element. For example, for a database column of type varchar(45), the limit would be 45.



null

A Boolean value that's true only if the column can be set to null. You'll access it through the accessor method null?.



text

A Boolean value that's true only if the column can be interpreted as text. You'll access it through the accessor text?.



default

The default value you specified in the table definition.



primary

A Boolean value that's true if the column is the Rails unique identifier. You'll access it through the accessor method primary?.

Your applications can use this column metadata to build dynamic user interfaces. Scaffolding, discussed in "rubyrails-chp-4.html#rubyrails-chp-4">Chapter 4, uses this technique. Normally, you want to get the data only for a column, so you'll use an attribute's accessors.

2.5.2. Accessors

You've seen that you can access the database columns of a photo by simply calling an accessor like photo.filename. The Rails implementation isn't necessarily what you'd expect. You might expect to see the accessor for filename as a method on photo. Strangely, if you type:

photo.methods.include? 'filename'

in the console, you get false, which means that there's no explicit filename accessor for photo. Active Record uses a Ruby metaprogramming trick to attach attributes. It overrides the method_missing method, which gets invoked if you call a nonexistent method of some object. Consider this program:

class Talker
  def method_missing(method)
    if method.to_s =~ /say_/
     puts $'
    end
  end
end

This Talker class responds to any message beginning with say_, even though no method beginning with say_ exists. For example, Talker.new.say_hello prints hello. Active Record uses this trick to implement accessors. As a consequence, include? returns false for accessors because the class doesn't include an explicit accessor method. You'll see later that Active Record also generates custom finders, like find_by_filename, for each class.

2.5.3. Identifiers

The id attribute is special to Active Record because that column serves as the primary key for the database table. Our migration created the id column, and a primary key based on the id, automatically. The underlying table definition, shown in "#rubyrails-chp-2-fig-2">Figure 2-2, identifies the primary key with the primary key(id) statement. You might expect Active Record to recognize the unique identifier by seeing which columns are included in a table's primary key, but this strategy is not always possible. Some database managers don't have simple APIs to discover primary or foreign keys, so Active Record uses the id naming convention instead (see "#rubyrails-chp-2-table-2">Table 2-2).

Table 2-2. Active Record adds these methods and attributes to model objects at runtime

Features

Purpose

Methods

find_by_<column_name>

Active Record adds a class method to the class for each column in the database, including id. For example, Active Record adds find_by_id, find_by_name and find_by_email to a class wrapping a table having id, name, and email columns.

find_by_<column_name>_and_<column_name>

Active Record also adds finders that combine groups of attributes. For example, a Person class wrapping a table with name and email columns would support Person.find_by_name_and_email(name, email).

Attributes

<column_name>

Active Record creates an attribute with getters and setters for each property in the database. For example, photo.filename = "dog.jpg" would be legal for a photo instance of a class wrapping a table with a filename column.


Figure 2-2. The most basic Active Record mapping ties a single table to a model object

Identifiers and Legacy Schemas

In a typical Rails migration script, you will not see the id column. Rails manages the id field for you by default. In the typical case, Active Record maps the id onto a database sequence, so the database creates the initial value of id. You don't have to let Active Record manage your identifiers. For example, you could have a Photo class with a timestamp attribute called created_at:

class Photo < ActiveRecord::Base
  set_primary_key "created_at"
end

There are some restrictions, though. The most prominent restriction (as of Rails 1.1) is that you can't use composite keys, or primary keys using more than one database column. If you need to use composite keys, one way to solve the problem is to introduce a new column to serve as your identifier. It need not be the primary key.

Alternatively, you could create a database view. A view is a logical view of database data. You can access the results of any query as a view. You could use a view to introduce a new column or to combine several existing columns into one. Active Record could then use the view instead of the table.

Rules for updating views vary across database managers, so depending on your database manager, you'd either have to customize Active Record or use views only for read-only tables. Both approaches have been successfully used in production applications.



Previous Page
Next Page