Active Record objects don’t
specify their attributes
directly, but rather infer them from the table definition with which
they’re linked. Adding, removing, and changing attributes and their type is
done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that
binds a given Active Record class to
a certain database table will happen automatically in most common cases,
but can be overwritten for the uncommon ones.

See the mapping rules in table_name and the
full example in files/README.html for more insight.

Creation

Active Records accept constructor
parameters either in a hash or
as a block. The hash method is
especially useful when you’re receiving the data from somewhere else,
like a HTTP request. It works like this:

Conditions

Conditions can either be specified as a string or an array representing the
WHERE-part of an SQL statement. The array form is to be used when the
condition input is tainted and requires sanitization. The string form can
be used for statements that don’t involve tainted data. Examples:

The authenticate_unsafely method inserts the parameters directly
into the query and is thus susceptible to SQL-injection attacks if the
user_name and password parameters come directly from a
HTTP request. The authenticate_safely method, on the other hand,
will sanitize the
user_name and password before inserting them in the
query, which will ensure that an attacker can’t escape the query and
fake the login (or worse).

When using multiple parameters in the conditions, it can easily become hard
to read exactly what the fourth or fifth question mark is supposed to
represent. In those cases, you can resort to named bind variables instead.
That’s done by replacing the question marks with symbols and
supplying a hash with values
for the matching symbol keys:

Overwriting default accessors

All column values are automatically available through basic accessors on
the Active Record object, but some
times you want to specialize this behavior. This can be done by either by
overwriting the default accessors (using the same name as the attribute)
calling read_attribute(attr_name) and write_attribute(attr_name, value) to
actually change things. Example:

Sometimes you want to be able to read the raw attribute data without having
the column-determined typecast run its course first. That can be done by
using the <attribute>_before_type_cast accessors that allattributes have. For
example, if your Account model has a balance attribute, you can call
account.balance_before_type_cast or account.id_before_type_cast.

This is especially useful in validation situations where the user might
supply a string for an integer field and you want to display the original
string back in an error message. Accessing the attribute normally would
typecast the string to 0, which isn’t what you want.

Dynamic attribute-based finders

Dynamic attribute-based finders are a cleaner way of getting (and/or
creating) objects by simple queries without turning to SQL. They work by
appending the name of an attribute to find_by_ or
find_all_by_, so you get finders like Person.find_by_user_name,
Person.find_all_by_last_name, Payment.find_by_transaction_id. So instead of
writing Person.find(:first, ["user_name = ?",
user_name]), you just do Person.find_by_user_name(user_name).
And instead of writing Person.find(:all, ["last_name = ?",
last_name]), you just do
Person.find_all_by_last_name(last_name).

It’s also possible to use multiple attributes in the same find by separating them with
"and", so you get finders like
Person.find_by_user_name_and_password or even
Payment.find_by_purchaser_and_state_and_country. So instead of
writing Person.find(:first, ["user_name = ? AND password =
?", user_name, password]), you just do
Person.find_by_user_name_and_password(user_name, password).

It’s even possible to use all the additional parameters
to find. For example, the
full interface for Payment.find_all_by_amount is actually
Payment.find_all_by_amount(amount, options). And the full interface to
Person.find_by_user_name is actually Person.find_by_user_name(user_name,
options). So you could call Payment.find_all_by_amount(50, :order =>
"created_on").

The same dynamic finder style can be used to create the object if it
doesn’t already exist. This dynamic finder is called with
find_or_create_by_ and will return the object if it already exists
and otherwise creates it, then returns it. Example:

# No 'Summer' tag existsTag.find_or_create_by_name("Summer")# equal to Tag.create(:name => "Summer")# Now the 'Summer' tag does existTag.find_or_create_by_name("Summer")# equal to Tag.find_by_name("Summer")

Active Record can serialize any object in
text columns using YAML. To do so, you must specify this with a call to
the class method serialize. This
makes it possible to store arrays, hashes, and other non-mappable objects
without doing any additional work. Example:

Single table inheritance

Active Record allows inheritance by
storing the name of the class in a column that by default is called
"type" (can be changed by overwriting
Base.inheritance_column). This means that an inheritance looking
like this:

When you do Firm.create(:name => "37signals"), this record
will be saved in the companies table with type = "Firm". You can then fetch this row again using
Company.find(:first, "name = ‘37signals’") and it
will return a Firm object.

If you don’t have a type column defined in your table, single-table
inheritance won’t be triggered. In that case, it’ll work just
like normal subclasses with no
special magic for differentiating between them or reloading the right type
with find.

RecordNotFound
— no record responded to the find* method. Either the row with the
given ID doesn’t exist or the row didn’t meet the additional
restrictions.

StatementInvalid
— the database server rejected the SQL statement. The precise error
is added in the message. Either the record with the given ID doesn’t
exist or the record didn’t meet the additional restrictions.

AttributeAssignmentError
— an error occurred while doing a mass assignment through the attributes= method.
You can inspect the
attribute property of the exception object to determine which
attribute triggered the error.

Note: The attributes listed are
class-level attributes
(accessible from both the class and instance level). So it’s possible
to assign a logger to the class through Base.logger= which will then be
used by all instances in
the current object space.

In newer versions of rails, method_missing defines
find_by_bar when you first use it. By calling
super, you’re triggering method_missing and
overwriting your custom definition! It will work the first time then break!
Manually write the call to find!