Chapter 8. Validations

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.

8.1 Finding Errors

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.

8.2 The Simple Declarative Validations

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.

8.2.1 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.

image

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.

image

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.

8.2.2 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.


Suggestion

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.


Tim says ...

It’s possible to get similar behavior by using a combination of the :autosave and :validate options on a has_many.


8.2.3 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:

image

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.

image

8.2.4 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:

image

8.2.5 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:

image

By the way, that example is totally not an RFC-compliant email address format checker.2


Courtenay says ...

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:


image


Note

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:


image


Lark says ...

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.


8.2.6 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:

image

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.

8.2.7 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.

image

Constraint Options

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:

image

To specify an exact length of an attribute, use the :is option:

image

Error Message Options

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.

image

8.2.8 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.

image

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

Infinity and Other Special Float Values

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.

8.2.9 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 "".

image

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]

Validating the Presence and/or Existence of Associated Objects

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:

image

Without a validation, if your application violates a database foreign key constraint, you will get an Active Record exception.

8.2.10 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.

image

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).

image

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).


Tim says ...

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.


Enforcing Uniqueness of Join Models

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:

image

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:

image

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.”


Tim says ...

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.


8.2.11 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:

image

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:

image

8.2.12 RecordInvalid

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:

image

8.3 Common Validation Options

The following options apply to all of the validation methods.

8.3.1 :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.

8.3.2 :if and :unless

The :if and :unless options is covered in the next section, “Conditional Validation.”

8.3.3 :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.

image

The default English locale file in Active Model defines most of the standard error message templates.

image

The default messages only use the count variable for interpolation, where appropriate, but model, attribute, and value are always available.

image

8.3.4 :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.

image

8.4 Conditional Validation

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:

SymbolThe name of a method to invoke as a symbol. This is probably the most common option, and offers the best performance.

StringA 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.

ProcA 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?
}

8.4.1 Usage and Considerations

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.

image

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.

image

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.

image

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.

8.4.2 Validation Contexts

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.

image

8.5 Short-form Validation

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.

image

The following options are available for use with the validates method.

:acceptance => trueAlias 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 => trueAlias 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 => 1000Alias 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 => trueAlias for validates_numericality_of. Supply additional options by replacing true with a hash.

validates :quantity, :numericality => { :message => 'Supply a number.'
}

:presence => trueAlias for validates_presence_of. Supply additional options by replacing true with a hash.

validates :login, :presence => { :message => 'How do you expect to
login?' }

:uniqueness => trueAlias 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!" }

8.6 Custom Validation Techniques

When the existing declarative validation macros are not enough for your application needs Rails gives you a few custom techniques.

8.6.1 Add Custom Validation Macros to Your Application

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.

image

Now that your custom validator exists, it is available to use with the validates macro in your model.

image

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.

image

Our model code would change to

image

8.6.2 Create a Custom Validator Class

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.

image

Use your new custom validator in a model with the validates_with macro.

image

8.6.3 Add a validate Method to Your Model

A 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:

image

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.

8.7 Skipping Validations

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.

8.8 Working with the Errors Hash

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.

8.8.1 Checking for Errors

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.

image

8.9 Testing Validations with Shoulda

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.

image

8.10 Conclusion

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.189.23