Data integrity is an underrated part of proper application architecture. Many of the bugs in production systems are triggered by missing or malformed user data. If a user can possibly screw it up or screw with it, they will. Validations in the model can help!
Before we begin, let’s talk about syntax. There are two primary syntaxes for writing validations in Rails 3:
These have the exact same functionality. The first is the older style and the second a newer "Rails 3 style". The Rails 3 style shines when we add in a second validation on the same field:
1 2 3 4 5 6
The newer syntax allows you to condense multiple validations into a single line of code.
In my opinion, the newer syntax is not good. Clean Ruby reads like English. To read the second set of examples aloud:
Rails 2 Style: “validates presence of price, validates numericality of price”
Rails 3: “validates price presence true numericality true”
The Rails 2 sentence isn’t poetry, but you can understand what it means. The Rails 3 syntax sounds like computer talk. Ruby is about developers not computers, and for that reason I recommend you not use the new syntax.
Aaron Patterson on the Rails core team confirms that there are no plans to deprecate the older syntax.
Most Valuable Validations
There are many validations available to you, check out the full API here: http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html
Let’s take a closer look at the most commonly used methods.
Declare that the field must have some value. Note that an empty string (
"") is not considered a value.
Also, you can declare the validation on multiple fields at once:
Check that the value in the field "looks like" a number. It might be a string, but does it look like a number?
"3.45" does, while
"hello" does not. Neither does
"a928". I’ll tend to apply this to every field that is a number in the database (ex:
price) and ones that look like a number but are stored as a string (ex:
That basic usage will allow anything that’s "number-like" including integers or floats.
We can add a few options to add criteria to our "numbers":
:only_integerwill only accept integers
- Control the range of values with these options:
1 2 3
Check the length of a string with
Usage & Options
validates_length_of obviously needs to know what the length should be. Here are a few examples of the common specifiers:
1 2 3 4
validates_format_of method is the Swiss Army knife of validations. It attempts to match the input against a regular expression, so anything you can write in a regex you can check with this validator.
I always like to share Jamie Zawinski’s quote when talking about this topic: “Some people, when confronted with a problem, think ‘I know, I’ll use regular expressions.’ Now they have two problems.”
But if you’re comfortable and capable with regular expressions, have at it!
The canonical example is email address format validation:
It’s important to note that this regex is not compliant with the grammar described in RFC 822. A regex that is compliant would be considerably more complex.
Or reject based on a regex using the
Check that a value is in a given set with
:in parameter will accept a Ruby range like this example or any other
Enumerable object (like an
For custom validations, call the
validate method with a symbol specifying the instance method to be called. When a record is saved, that method will be called.
1 2 3 4 5 6 7
Rails will not respect the return value of that method, it determines pass/fail based on whether any error messages were added to the object. To make the validation fail by call
errors.add(:base, message). If no errors are added, the validation passes.
Validations in the User Interface
Having expressed validations in the model, let’s see how to make use of them in the user interface.
As an example, assume we have this model:
1 2 3
In the Console
Trigger the validation:
valid? method on an instance will run the validations and return
false. From there, we can dig into the errors:
2.1.1 :001> 2.1.1 :002>
.errors method returns an
ActiveRecord::Errors collection, which looks like a hash, with the attribute name as the key and the message(s) in an array. The convenience method
.full_messages on that object returns an array of nicely formatted sentence fragments.
Save vs. Save!
While looking at validation failures, let’s highlight the difference between
.save!. Both methods run your validations, of course, but the consequences of a failure are different.
A call to
save will return
true if the save succeeds and
false if it fails. In your application code, you should react to this return value to determine next steps. For example:
1 2 3 4 5 6 7 8
else switches based on the success/failure of the
Alternatively, when we use
.save! succeeds it will also return
true, but when it fails it will raise an exception. Well architected Ruby treats exceptions as extremely abnormal cases. For that reason, saving a model should only raise an exception when something very strange has happened, like the database has crashed. Users entering junky input is not unexpected, so we shouldn’t typically raise exceptions on a validation.
So when should you use
save!? When you expect the validations to always pass. For instance, if your application is creating objects with no user input, it’d be very strange to have invalid data. Then use
save! and skip the redirect. In such a scenario, redirecting someplace probably isn’t going to help, so your application should raise an exception.
Typically we react to
.save in the controller and re-render the form if it returned
false. Then in the UI we can display the errors for the user to correct.
Back in Rails 2
In the olden days, this was very easy. All you had to write in the helper was call
You’d get a box saying that there were errors and a bulleted list of the error messages. Assuming your form is using the
form_for helper, the fields with the validation errors will automatically be wrapped with a
But the markup was not customizable, so most production apps would end up rewriting it.
With Rails 3 the
error_messages_for helper was removed.
Writing a Helper Method
But now we can write our own
error_messages_for, imitate Rails 2, and leave the door open for easy customization at the design stage. Here’s an implementation from Ryan Bates of RailsCasts:
1 2 3 4 5 6 7 8 9 10 11 12
It’s CSS-compatible with the original Rails 2 implementation and respects i18n message definitions.
Custom Messages & Internationalization
All the validations support a
:message parameter in the model where you can specify a custom message. But this is the wrong way to do it, and I hope that option is soon deprecated.
Error messages can be specified in our locale file. These files live in
config/locales/ and have names corresponding to the language code, like
en.yml for English or
es.yml for Spanish.
In that translation file you can override the default messages either globally for all uses of a validation or on a per-model basis. Check out the Rails source code for lots of details on implementation: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml
Here’s an example based on
validates_presence_of :title for a
1 2 3 4 5 6 7 8
Web applications generally do a terrible job of coaching their users. You fill out a big form, click submit, then it’ll have an uninformative message at the top of the form like "Form Had Errors".
Check out the project page and readme for details on setup and usage.
For truly bullet-proof data integrity you’ll need to implement validations at the database level, too.
foreignergem gives you the ability to add foreign key constraints to MySQL, PostgreSQL, and SQLite: https://github.com/matthuhiggins/foreigner
validates_uniqueness_ofcould, in theory, run into a race condition if there are two concurrent requests creating the same data. To protect against that, you can create a database index on the field and specify that it must be unique:
Then the database would reject a second submission with an existing title if it got past the Rails model validation
Use the Blogger sample application to complete the exercises in this section. See the Setup Instructions for help.
- Write validations to check that an
Articleobject must have both a title and a body.
- Validate that a
Commenthas a body of less than 250 characters.
- Validate that neither articles nor comments have your name in them.
- Validate that a comment must have an associated article. Test it in your console and make sure it works as expected.
- Build a module of
TextValidationsthat can be shared by
includeit from both models.
- Rails API for
- Rails API for
- Tricks and techniques for Ruby exceptions: http://exceptionalruby.com/
- Client-Side Validations Gem: https://github.com/bcardarella/client_side_validations
- RailsCast on Client-Side Validations: http://railscasts.com/episodes/263-client-side-validations
- ActiveRecord i18n Validation Messages: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml
- Rails Guide on i18n for Models: http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models
- Rails migration methods including