I have bought this wonderful machine a computer. Now I am rather an authority on gods, so I identified the machine it seems to me to be an Old Testament god with a lot of rules and no mercy.
—Joseph Campbell
The Validations API in Active Model, along with its supplementary functionality in Active Record allows you to declaratively define valid states for your model objects. The validation methods hook into the life cycle of an Active Record model object and are able to inspect the object to determine whether certain attributes are set, have values in a given range, or pass any other logical hurdles that you specify.
In this chapter, we’ll describe the validation methods available and how to use them effectively. We’ll also explore how those validation methods interact with your model’s attributes and how the built-in error-messaging system messages can be used effectively in your application’s user interface to provide descriptive feedback.
Finally, we’ll cover how to use Active Model’s validation functionality in your own, non-Active Record classes.
Validation problems are also known as (drumroll please...) errors! Every Active Record model object contains a collection of errors, accessible (unsurprisingly) as the errors
attribute. It’s an instance of the class ActiveModel::Errors
that extends ActiveSupport::OrderedHash
.
When a model object is valid, the errors
collection is empty. In fact, when you call valid?
on a model object, a series of steps to find errors is taken as follows (slightly simplified):
1. Clear the errors
collection.
2. Run validations.
3. Return whether the model’s errors
collection is now empty or not.
If the errors
collection ends up empty, the object is valid. In cases where you have to write actual validation logic yourself, you mark an object invalid by adding items to the errors
collection using its add
methods. Simple as that.
We’ll cover the methods of the Errors
class in some more detail later on. It makes more sense to look at the validation methods themselves first.
Whenever possible, you should set validations for your models declaratively by using one or more of the following class methods available to all Active Record classes. Unless otherwise noted, all of the validates
methods accept a variable number of attributes, plus options. There are some options for these validation methods that are common to all of them, and we’ll cover them at the end of the section.
validates_acceptance_of
Many web applications have screens in which the user is prompted to agree to terms of service or some similar concept, usually involving a check box. No actual database column matching the attribute declared in the validation is required. When you call this method, it will create virtual attributes automatically for each named attribute you specify. I see this validation as a type of syntax sugar since it is so specific to web application programming.
You can use this validation with or without a boolean columns on the table backing your model. An attribute will be created if necessary. Choose to store the value in the database only if you need to keep track of whether the user accepted the term, for auditing or other reasons. Mind you, not accepting the term would prevent creation of the record, but it’s good to know what is supported.
When the validates_acceptance_of
validation fails, an error message is stored in the model object reading “attribute must be accepted.”
The :accept
option makes it easy to change the value considered acceptance. The default value is "1"
, which matches the value supplied by check boxes generated using Rails helper methods.
If you use the preceding example in conjunction with a text field connected to the account_cancellation
attribute, the user would have to type the word YES in order for the cancellation object to be valid.
validates_associated
This is used to ensure that all associated objects are valid on save. It works with any kind of association and is specific to Active Record (not Active Model.) We emphasize all because the default behavior of has_many
associations is to ensure the validity of their new child records on save.
You probably don’t need to use this particular validation nowadays since has_many
associations default to :validate => true
. Additionally note that one of the implications of that default is that setting :validate => true
carelessly on a belongs_to
association can cause infinite loop problems.
A validates_associated
on belongs_to
will not fail if the association is nil
. If you want to make sure that the association is populated and valid, you have to use validates_associated
in conjunction with validates_presence_of
.
It’s possible to get similar behavior by using a combination of the :autosave
and :validate
options on a has_many
.
validates_confirmation_of
The validates_confirmation_of
method is another case of syntactic sugar for web applications, since it is so common to include dual-entry text fields to make sure that the user entered critical data such as passwords and e-mail address correctly. This validation will create a virtual attribute for the confirmation value and compare the two attributes to make sure they match in order for the model to be valid.
Here’s an example, using our fictional Account
model again:
The user interface used to set values for the Account
model would need to include extra text fields named with a _confirmation
suffix, and when submitted, the value of those fields would have to match in order for this validation to pass. A simplified example of matching view code is provided.
validates_each
The validates_each
method is a little more free-form than its companions in the validation family in that it doesn’t have a predefined validation function. Instead, you give it an array of attribute names to check, and supply a Ruby block to be used in checking each attribute’s validity. Notice that parameters for the model instance (record
), the name of the attribute as a symbol, and the value to check are passed as block parameters. The block function designates the model object as valid or not by merit of adding to its errors
array or not. The return value of the block is ignored.
There aren’t too many situations where this method is necessary, but one plausible example is when interacting with external services for validation. You might wrap the external validation in a faade specific to your application, and then call it using a validates_each
block:
validates_format_of
To use validates_format_of
, you’ll have to know how to use Ruby regular expressions.1 Pass the method one or more attributes to check, and a regular expression as the (required) :with
option. A good example, as shown in the Rails docs, is checking for a valid e-mail address format:
By the way, that example is totally not an RFC-compliant email address format checker.2
Regular expressions are awesome but can get very complex, particularly when validating domain names or email addresses. You can use #{}
inside regular expressions, so split up your regex into chunks like this:
That expression is pretty straightforward and easy to understand. The constants themselves are not so easy to understand but easier than if they were all jumbled in together:
I’ll take your readability Courtenay, and raise you test isolation. Your regular expression should itself be in a constant so you can test it.
validates_inclusion_of
and validates_exclusion_of
These methods take a variable number of attribute names and an :in
option. When they run, they check to make sure that the value of the attribute is included (or excluded, respectively) in the enumerable object passed as the :in
option.
The examples in the Rails docs are probably some of the best illustrations of their use, so I’ll take inspiration from them:
Notice that in the examples I’ve introduced usage of the :message
option, common to all validation methods, to customize the error message constructed and added to the Errors
collection when the validation fails. We’ll cover the default error messages and how to effectively customize them a little further along in the chapter.
validates_length_of
The validates_length_of
method takes a variety of different options to let you concisely specify length constraints for a given attribute of your model.
The :minimum
and :maximum
options work as expected, but don’t use them together. To specify a range, use the :within
option and pass it a Ruby range, as in the following example:
To specify an exact length of an attribute, use the :is
option:
Rails gives you the ability to generate detailed error messages for validates_length_of
via the :too_long
, :too_short
, and :wrong_length
options. Use {{count}}
in your custom error message as a placeholder for the number corresponding to the constraint.
validates_numericality_of
The somewhat clumsily named validates_numericality_of
method is used to ensure that an attribute can only hold a numeric value.
The :only_integer
option lets you further specify that the value should only be an integral value and defaults to false.
The :even
and :odd
options do what you would expect and are useful for things like, I don’t know, checking electron valences. (Actually, I’m not creative enough to think of what you would use this validation for, but there you go.)
The following comparison options are also available:
• :equal_to
• :greater_than
• :greater_than_or_equal_to
• :less_than
• :less_than_or_equal_to
Interestingly, Ruby has the concept of infinity built-in. If you haven’t seen infinity before, try the following in a console:
>> (1.0/0.0)
=> Infinity
Infinity
is considered a number by validates_numericality_of
. Databases (like PostgreSQL) with support for the IEEE 754 standard should allow special float values like Infinity
to be stored. The other special values are positive infinity (+INF), negative infinity (–INF), and not-a-number (NaN). IEEE 754 also distinguishes between positive zero (+0) and negative zero (–0). NaN is used to represent results of operations that are undefined.
validates_presence_of
One of the more common validation methods, validates_presence_of
, is used to denote mandatory attributes. This method checks whether the attribute is blank using the blank?
method, defined on Object
, which returns true
for values that are nil
or a blank string ""
.
A common mistake is to use validates_presence_of
with a boolean attribute, like the backing field for a checkbox. If you want to make sure that the attribute is true, use validates_acceptance_of
instead. The boolean value false
is considered blank, so if you want to make sure that only true
or false
values are set on your model, use the following pattern:
validates_inclusion_of :protected, :in => [true, false]
When you’re trying to ensure that an association is present, pass validates_presence_of
its foreign key attribute, not the association variable itself. Note that the validation will fail in cases when both the parent and child object are unsaved (since the foreign key will be blank).
Many developers try to use this validation with the intention of ensuring that associated objects actually exist in the database. Personally, I think that would be a valid use case for an actual foreign-key constraint in the database, but if you want to do the check in your Rails code then emulate the following example:
Without a validation, if your application violates a database foreign key constraint, you will get an Active Record exception.
validates_uniqueness_of
The validates_uniqueness_of
method, also exclusive to Active Record, ensures that the value of an attribute is unique for all models of the same type. This validation does not work by adding a uniqueness constraint at the database level. It does work by constructing and executing a query looking for a matching record in the database. If any record is returned when this method does its query, the validation fails.
By specifying a :scope
option, additional attributes can be used to determine uniqueness. You may pass :scope
one or more attribute names as symbols (putting multiple symbols in an array).
It’s also possible to specify whether to make the uniqueness constraint case-sensitive or not, via the :case_sensitive
option (ignored for nontextual attributes).
This validation is not foolproof because of a potential race condition between the SELECT
query that checks for duplicates and the INSERT
or UPDATE
which persists the record. An Active Record exception could be generated as a result, so be prepared to handle that failure in your controller.
I recommend that you use a unique index constraint in the database if you absolutely must make sure that a column value is unique.
In the course of using join models (with has_many :through
), it seems pretty common to need to make the relationship unique. Consider an application that models students, courses, and registrations with the following code:
How do you make sure that a student is not registered more than once for a particular course? The most concise way is to use validates_uniqueness_of
with a :scope
constraint. The important thing to remember with this technique is to reference the foreign keys, not the names of the associations themselves:
Notice that since the default error message generated when this validation fails would not make sense, I’ve provided a custom error message that will result in the expression: “Student can only register once per course.”
Astute readers will notice that the validation was on student_id
but the error message references “Student.” Rails special cases this to do what you mean.
validates_with
All of the validation methods we’ve covered so far are essentially local to the class in which they are used. If you want to develop a suite of custom, reusable validation classes, then you need a way to apply them to your models, and that is what the validates_with
method allows you to do.
To implement a custom validator, extend ActiveRecord::Validator
and implement the validate
method. The record being validated is available as record
and you manipulate its errors
hash to log validation errors.
The following examples, from Ryan Daigle’s excellent post3 on this feature, demonstrate a reusable email field validator:
The example assumes the existence of an email attribute on the record. If you need to make your reusable validator more flexible, you can access validation options at runtime via the options
hash, like this:
Whenever you do so-called bang operations (such as save!
) and a validation fails, you should be prepared to rescue ActiveRecord::RecordInvalid
. Validation failures will cause RecordInvalid
to be raised and its message will contain a description of the failures.
Here’s a quick example from one of my applications that has pretty restrictive validations on its User
model:
The following options apply to all of the validation methods.
:allow_blank
and :allow_nil
In some cases, you only want to trigger a validation if a value is present, in other words the attribute is optional. There are two options that provide this functionality.
The :allow_blank
option skips validation if the value is blank according to the blank?
method. Similarly, the :allow_nil
option skips the validation if the value of the attribute is nil
; it only checks for nil
, and empty strings ""
are not considered nil, but they are considered blank.
:if
and :unless
The :if
and :unless
options is covered in the next section, “Conditional Validation.”
:message
As we’ve discussed earlier in the chapter, the way that the validation process registers failures is by adding items to the Errors
collection of the model object being checked. Part of the error item is a specific message describing the validation failure. All of the validation methods accept a :message
option so that you can override the default error message format.
The default English locale file in Active Model defines most of the standard error message templates.
The default messages only use the count
variable for interpolation, where appropriate, but model
, attribute
, and value
are always available.
:on
By default, validations are run on save (both create and update operations). If you need to do so, you can limit a given validation to just one of those operations by passing the :on
option either :create
or :update
.
Assuming that your application does not support changing login names, one good use for :on => :create
might be in conjunction with validates_uniqueness_of
, since checking uniqueness with a query on large datasets can be time-consuming.
Since all validation methods are implemented via the Active Model Callback API, they also accept :if
and :unless
options, to determine at runtime (and not during the class definition) whether the validation needs to be run or not. The following three types of arguments can be supplied as an :if
and :unless
options:
Symbol
The name of a method to invoke as a symbol. This is probably the most common option, and offers the best performance.
String
A snippet of Ruby code to eval
might be useful when the condition is really short, but keep in mind that eval’ing statements is relatively slow.
Proc
A block of code to be instance_eval
’d, so that self
is the current record. Perhaps the most elegant choice for one-line conditionals.
validates_presence_of :approver, :if => lambda { approved? && !legacy?
}
When does it make sense to use conditional validations? The answer is: whenever an object can be validly persisted in more than one state. A very common example involves the User
(or Person
) model, used for login and authentication.
This code is not DRY (meaning that it is repetitive). You can refactor it to make it a little dryer using the with_options
method that Rails mixes into Object
.
All of the example validations check for the two cases when a (plaintext) password field should be required in order for the model to be valid.
The first case is if the crypted_password
attribute is blank, because that means we are dealing with a new User
instance that has not been given a password yet. The other case is when the password
attribute itself is not blank; perhaps this is happening during an update operation and the user is attempting to reset her password.
Another way to accomplish conditional validation leverages support for validation contexts. Declare a validation and pass the name of an application-specific validation context as the value of the :on
option. That validation will now only be checked when explicitly invoked using record.valid?(
context_name
)
.
Consider the following example involving a report generation app. Saving a report without a name is fine, but publishing one without a name is not.
Rails 3 introduces a validates
method that identifies an attribute and accepts options that correspond to the validators we’ve already covered in the chapter. Using validates
can tighten up your model code nicely.
The following options are available for use with the validates
method.
:acceptance => true
Alias for validates_acceptance_of
, typically used with checkboxes that indicate acceptance of terms. Supply additional options by replacing true
with a hash.
validates :terms, :acceptance => { :message => 'You must accept terms.'
}
:confirmation => true
Alias for validates_confirmation_of
, typically used to ensure that email and password confirmation fields match up correctly. Supply additional options by replacing true
with a hash.
validates :email, :confirmation => { :message => 'Try again.' }
:exclusion => :in => [1,2,3]
Alias for validates_exclusion_of
.
:format => :with => /.*/
Alias for validates_format_of
. If your only option is the regular expression, you can shorten the syntax further by making it the value like:
:format => /[A-Za-z0-9]+/
:inclusion => :in => [1,2,3]
Alias for validates_inclusion_of
.
:length => :minimum => 0, maximum => 1000
Alias for validates_length_of
. If your only options are minimum and maximum lengths, you can shorten the syntax further by supplying a Ruby range as the value.
validates :login, :length => [3..20]
:numericality => true
Alias for validates_numericality_of
. Supply additional options by replacing true
with a hash.
validates :quantity, :numericality => { :message => 'Supply a number.'
}
:presence => true
Alias for validates_presence_of
. Supply additional options by replacing true
with a hash.
validates :login, :presence => { :message => 'How do you expect to
login?' }
:uniqueness => true
Alias for validates_uniqueness_of
. Supply additional options by replacing true
with a hash.
validates :quantity, :uniqueness => { :message => "You're SOL on that
login choice, buddy!" }
When the existing declarative validation macros are not enough for your application needs Rails gives you a few custom techniques.
Rails 3 introduces the ability to add custom validation macros (available to all your model classes) by extending ActiveModel::EachValidator
.
The following example is silly, but demonstrates the functionality nicely.
Now that your custom validator exists, it is available to use with the validates
macro in your model.
The key :report_like
is inferred from the name of the validator class, which in this case was ReportLikeValidator
.
You can receive options via the validates
method by adding an initializer
method to your custom validator class. For example, let’s make ReportLikeValidator
more generic.
Our model code would change to
This technique involves inheriting from ActiveModel::Validator
and implementing a validate
method that takes the record to validate.
I’ll demonstrate with a really wicked example.
Use your new custom validator in a model with the validates_with
macro.
validate
Method to Your ModelA validate
instance method might be the way to go if you want to check the state of your object holistically and keep the code for doing so inside of the model class itself. (This is an older technique that I can’t fully endorse; it adds complexity to your model class unnecessarily given how easy it is to create custom validator classes.)
For example, assume that you are dealing with a model object with a set of three integer attributes (:attr1
, :attr2
, and :attr3
) and a precalculated total attribute (:total
). The total must always equal the sum of the three attributes:
You can alternatively add an error message to the whole object instead of just a particular attribute, using the :base
key, like this:
errors[:base] << "The total doesn't add up!"
Remember: The way to mark an object as invalid is to add to its Errors
object. The return value of a custom validation method is not used.
The method update_attribute
doesn’t invoke validations, yet its companion method update_attributes
does, a question that comes up quite often on the mailing list. Whoever wrote the API docs believes that this behavior is “especially useful for Boolean flags on existing records.”
I don’t know if that is entirely true or not, but I do know that it is the source of ongoing contention in the community. Unfortunately, I don’t have much more to add other than some simple common-sense advice: Be very careful using the update_attribute
method. It can easily persist your model objects in invalid states.
Some methods are provided to allow you to add validation errors to the collection manually and alter the state of the Errors
hash.
errors[:base] = msg
Adds an error message related to the overall object state itself and not the value of any particular attribute. Make your error messages complete sentences, because Rails does not do any additional processing of them to make them readable.
errors[:attribute] = msg
Adds an error message related to a particular attribute. The message should be a sentence fragment that reads naturally when prepended with the capitalized name of the attribute.
clear
As you might expect, the clear
method clears the Errors
collection.
It’s also possible to check the Errors
object for validation failures on specific attributes with a couple of methods, just using square brackets notation. An array is always returned; an empty one when there aren’t any validation errors for the attribute specified.
Even though validations are declarative code, if you’re doing TDD then you’ll want to specify them before writing them. Luckily, Thoughtbot’s Shoulda library4 contains a number of matchers designed to easily test validations.
In this (relatively speaking) short chapter, we covered the Active Record Validations API in-depth. One of the most appealing aspects of Rails is how we can declaratively specify the criteria for determining the validity of model objects.
3.149.29.145