Chapter 3. Active Model
In Rails, your models are the backbone of your application. The model layer in most Rails projects is traditionally comprised mostly of Active Record classes. Active Resource is also often used to access data persisted on RESTful web services, and it's easy to make in-memory models out of plain old ruby classes. Active Record and Active Resource have a lot in common: they are both backed by a persistent storage layer from which business objects are instantiated; those business objects have attributes which can be updated and stored back into the persistence layer; and the attributes can have constraints which are used to validate the model.
The features described in this chapter are all shared by both Active Record and Active Resource, sometimes with their own additions or alterations which are outlined in their respective chapters. If you are making your own models and want to take advantage of these features, you will need to mix in the appropriate module to your model classes.
Attributes
Tracking Changes
provided by ActiveModel::Dirty
Sometimes you want to know what kind of changes have
been made to a model object since it was instantiated. When a model object's
attributes have been altered, we say that it's a dirty object. If a dirty object is saved to the database, then it
becomes clean again. You can use the changed? method to find out if an object is dirty.
student.changed? # => false student.middle_name = 'Ambrose' student.changed? # => true student.save student.changed? # => false
You can also get more specific and ask about particular attributes and what their values were the last time the object was saved (or instantiated, if it hasn't been saved yet).
student.gpa = 3.22
student.gpa_changed? # => true
student.gpa_was # => 3.14
student.gpa_change # => [3.22, 3.14]
If you want to get information on all of an object's
changes, you can use the changed and changes methods:
student.middle_name = 'David'
student.changed # => ["gpa", "middle_name"]
student.changes # => {"gpa" => [3.22, 3.14],
# "middle_name" => ["Ambrose", "David"]}When you modify an attribute without using assignment, ActiveRecord will not notice the changes automatically. If you need the changes to be picked up, you can be explicit about it:
student.save student.middle_name_will_change! student.middle_name_changed? # => false student.middle_name << 'son' student.middle_name_change # => ["David", "Davidson"]
Validations
provided by ActiveModel::Validations
Whether through typo or through deeper misunderstanding, the people who use your application will invariably throw all kinds of data at it that just doesn't fit. They will try to pick a username that's already taken. They will enter their phone number in place of their email address. They will leave that "confirm password" field blank.
In Rails, we prepare for these situations by defining validations on our models. Validations are bits of code that take a look at one or more of the model's attributes to make sure that everything is how it should be. If something's amiss, then an error is added to the model object with a helpful message that can be displayed to the user.
Built-in macros
validates_acceptance_of
This validation macro solves the problem of making sure that a user has accepted some agreement or terms of service. The typical way of confirming an agreement like this is with a check box in a form. When a controller passes the parameters from such a form to update the attributes of a model, the value of the checkbox is passed in as well. The value of the checkbox doesn't need to be saved to the database, though, so this validation is not performed on a column attribute but rather on an in-memory attribute which is automatically added to the model.
class User < ActiveRecord::Base validates_acceptance_of :terms_of_service end
For this example, the check box could be constructed in its form like this:
<% form_for @user do |user| %> ... <%= user.check_box :terms_of_service %> <% end %>
The validates_acceptance_of macro takes all the
standard options for validations like :message and :on, plus these:
- :accept
Specifies the positive value expected for the agreement attribute. By default, it is "1", which is the default value submitted by a checked check box in Rails.
validates_confirmation_of
This macro is a handy way to validate that a user can type in the same thing
twice. Most often used for password fields. This is very similar to validates_acceptance_of, in that it validates the value of an
automatically generated attribute.
class User < ActiveRecord::Base validates_confirmation_of :password end
In this example, the generated attribute will be called password_confirmation, so you need to give the form field this name.
validates_each
Validates each named attribute with a given block of code. The block is called with three arguments: the model instance, the symbol of the attribute being validated, and the value of that attribute. The block is responsible for adding errors to the model instance as appropriate. This validator forms the basis of all other validators.
class User < ActiveRecord::Base
validates_each :first_name, :last_name do |record, attribute, value|
if ['Kirk', 'Spock', 'Bones'].include?(value)
record.errors.add(attribute, "must not have served on the Enterprise")
end
end
endvalidates_exclusion_of
Validates that the values of the given attributes are not found in some set of
banned values. The blacklist can be represented by any Enumerable object, passed to the macro with the :in
option:
class User < ActiveRecord::Base
validates_exclusion_of :first_name, :last_name,
:in => ['Kirk', 'Spock', 'Bones'],
:message => "must not have served on the Enterprise"
endvalidates_format_of
Checks the given attributes against a regular expression, adding an error if the value does not match.
class User < ActiveRecord::Base
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
endvalidates_inclusion_of
Inverse of validates_exclusion_of, ensuring that the
values of the given attributes are included in a set of allowed values.
class User < ActiveRecord::Base
validates_inclusion_of :date_of_birth,
:in => (130.years.ago.to_date .. Date.today),
:message => "must be at least plausibly human"
endvalidates_length_of
aliased as validates_size_of
Validates the size of the given attributes' values. This can be used on any
values which respond to a size method call, so it will
mean different things for strings as opposed to arrays or other enumerables. You
can specify a Range of allowed values with the :in option, as in validates_inclusion_of,
or you can just specify a :minimum or :maximum
value.
class User < ActiveRecord::Base
validates_length_of :password,
:in => 6..20
endIn addition to a general :message option, you can specify
:too_short and :too_long messages which
are used in each case.
Warning
This macro is not a good way of validating the number of elements in an
Active Record association, even if the child model has a validates_associated pointing at the parent. This is
because of the order in which validations are run, associations are
reloaded, and models are saved. If you need to limit the size of an
association, a custom validation could go on the child model to check the
count of existing sibling records in the parent's association.
validates_numericality_of
Validates that the values of the given attributes are numbers of some kind, or strings which represent numbers. This macro also lets you ensure that the value follows other constraints like being greater than or less than particular limits.
class Product < ActiveRecord::Base
validates_numericality_of :price,
:greater_than_or_equal_to => 0.00,
:message => "must be a non-negative number"
end- :only_integer
allows only integer values.
- :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to
constrains the value to the specified limits.
- :odd, :even
ensures that the value is either odd or even.
validates_presence_of
Validates that the given attributes are not blank, by calling blank? on their values. This macro is not implicitly included
by any other macros, so if you need a particular attribute to be present, you
should use the macro in addition to any others you have for that attribute.
class User < ActiveRecord::Base validates_presence_of :first_name, :last_name, :email, :password end





Add a comment



Add a comment