Introduction to Validations & Validation Error Handling in Rails

Purple Nitraguard Security Keypad

Validations in Ruby on Rails are essentially nothing more than methods that ensure that the data in a model is valid before saving it to the database. Traditionally, we validate data coming in using conditional expressions (for example, if email != NULL or if passwd==passwd_confirmation). This task is essential, but boring and tedious, but Rails’ validations make this mundane part of programming as simple and as easy as possible. The validations provided in Rails (defined in every model) are thorough, likely covering all your needs right out of the box. There are even validations provided for checking whether a user agreed to a Terms of Service or End-User License Agreement (EULA), and for doing automatic field confirmation, useful when you ask the user for an email or password twice to ensure no misspelling.

Let’s get to work with a simple step-by-step example (If you’d prefer a crash course, the HowToValidate wiki page is likely all you need.) We will create an application to help cat shelters keep track of each kitty coming in. It will take information about a cat that has been brought to a shelter, validate it and then save it in a database.

Brand Spankin’ New

Begin by creating a new rails project:

rails felinedb

In Rails 2.0, the default database is now SQLite3. If you’d like to use MySQL, use:

rails -d mysql project_name

If sticking with SQLite3, you’re set to go. If you’re using MySQL, configure config/database.yml appropriately, then create the development database (in Rails 2.0, you can use “rake db:create” instead of creating it by hand) before moving on.

Now generate a model named Cat:

ruby script/generate model Cat

(use :Rgenerate model cat if using rails.vim).

Now let’s add some stuff to this model. This is not intended to be realistic, but merely to illustrate the use of validations.

Add the following to your 001_create_cats.rb migration file (this is what it looks like in rails 2.0):

class CreateCats  1
      t.integer :age, :weight
      t.boolean :fiv, :vaccinated
      t.timestamps
    end
  end

  def self.down
    drop_table :cats
  end
end

and run rake db:migrate. :rescued is the date the cat was rescued. In practice, it’s better off being a date(time) object, but I made it a string to demonstrate validating a field against a regular expression. I also didn’t include :null and :default attributes for simplicity’s sake.

Halt! Who Goes There? – Adding Validations to a Model

We will setup the validations and poke at the model directly using the console, without creating any views.

So what needs to be validated?

  • All fields except comment will be mandatory.
  • Name will be at least 2 characters.
  • Sex will be either M or F.
  • Vaccinated and FIV (Feline Immunodeficiency Virus, for those curious) will be either True or False.
  • Age must be between 1-30 (indoor cats live 10-20 years on average, outdoor, far less)
  • Weight should be no less than 1.
  • Rescued will be a date in the following format: MM/DD/YYYY
  • Comment may be left blank, but otherwise has a limit of 500 characters.

Let’s translate this list into Rails validations. This is what our Cat model (app/models/Cat.rb) will look like

class Cat  2
  validates_inclusion_of :sex, :in => %w(M F), :message => 'must be M or F'
  validates_inclusion_of :vaccinated, :in => [true,false]
  validates_inclusion_of :fiv, :in => [true,false]
  validates_inclusion_of :age, :within => 1..30
  validates_each :weight do |record, attr, value|
      record.errors.add attr, 'should be a minimum of 1 pound' if value and value  /^[01][0-9]\/[0-9]{2}\/[0-9]{4}$/
  validates_length_of :comment, :allow_blank => true, :allow_nil => true, :maximum => 500
end

validates_presence_of :name, :sex, :age, :weight, :rescued Makes sure these fields are not left blank. You cannot validate the presence of a boolean using this method. See this:

“If you want to validate the presence of a boolean field (where the real values are true and false), you will want to use validates_inclusion_of :field_name, :in => [true, false] This is due to the way Object#blank? handles boolean values. false.blank? # => true”

validates_length_of :name, :minimum => 2: Make sure minimum length of name is 2 characters.

validates_inclusion_of :sex, :in => %w(M F): Make sure sex is either the value ‘M’ or ‘F’.

validates_inclusion_of :vaccinated, :in => [true,false]: Make sure vaccinated is either true or false.

validates_inclusion_of :age, :within => 1..30: Make sure age is within 1 – 30.

validates_each block (for :weight): validates_each can be used to create a custom validation. This specific validation will fail, adding the error “weight should be a minimum of 1 pound,” if the attribute (:weight) value (input we received) is less than 1. We have “if value and value < 1″ because value cannot be nil, else “nil < 1″ will give us an error.

validates_format_of :rescued, :with => /^[01][0-9]\/[0-9]{2}\/[0-9]{4}$/: compares the given attribute’s value with a regular expression (regex).

In this case, the regex you see makes sure the format is, in English: “a 0 or 1, followed by anything between 0-9 (this will be the month, MM), followed by a forward slash (we escaped it, hence \/ instead of just /), followed by 2 numbers (that’s what the {2} is for – this will be day, DD) between 0-9, followed by another forward slash, and ending with 4 numbers, each between 0-9, the year, YYYY.

validates_length_of :comment, :allow_blank => true, :allow_nil => true, :maximum => 500: This is used to validate the number of characters in a field. Maximum should not be confused with a maximum numerical value, as this attribute does not treat numbers numerically – 583 is 3 characters.

This particular example keeps the comment optional (allow_nil/allow_blank), but restricts the comment to being at most 500 characters.

For more details about these methods and more, see this page.

Validations are active on a model’s save method. This is the default behavior but can be changed using the “on” attribute (I.e., :on => :update). Model.save returns false if any of the validations fail, and the model’s Errors object can be used to iterate through the automatically generated errors (unless explicitly overridden) to find out what went wrong and prompt the user to edit and re-submit the data. You can check the validity of a model before attempting to save it by using the model’s valid? method.

Usage & Handling Errors

Let’s go into the Rails console (ruby script/console) and inspect the validations and Errors object. Create an instance of the Cat model and then try saving it:

>> c = Cat.new
=> #
>> c.valid?
=> false
>> c.save
=> false

The save method failed because the model is not valid. Without the validations in place, the save would have went through, simply filling a record in the database with NULLs in every field. We would have been manually checking each field (“if name…”), but Rails is doing this for us.

You can bypass the validations by passing false to the save method. For example:

>> c.save false
=> true
>> c
=> #

As you can see, this results in nil values being stored in the database. The usual approach to a failed save is to prompt the user about what happened and wait for proper input. When a validation fails, Rails automagically pushes a detailed (human-friendly) message to the model’s Errors stack.

Let’s observe the Errors object with a new Cat instance:

>> o = Cat.new
>> o.save
=> false
>> o.errors.size
=> 13
>> o.errors.class
=> ActiveRecord::Errors

Attempting to save the new Cat object failed, leaving us with 13 errors. No errors are generated if the model isn’t saved (obviously), but they are generated if the valid? method is envoked.

To see the other methods available in the Errors class, you can use the methods method. Preferably making it more readable:

>> puts o.errors.methods.sort
  ... (omitted) ...
  []
  __id__
  __send__
  add
  add_on_blank
  add_on_boundary_breaking
  blank?
  class
  clear
  clone
  collect
  copy_instance_variables_from
  count
  daemonize
  dclone
... (omitted) ...

For the entire list of errors method and details, see this page.

From the list, we can see that a subscript operator ([]) method is available. This method can be used to retrieve the error message generated for a particular attribute. This method is an alias for the on method; Either can be used. For example, we can pass the :name or :age attribute:

>> o.errors[:name]
=> ["can't be blank", "is too short (minimum is 2 characters)"]

>> o.errors.on :sex
=> "must be M or F"

We can iterate through this list using o.errors.each_full:

>> o.errors.each_full {|msg| p msg}
  "Name can't be blank"
  "Name is too short (minimum is 2 characters)"
  "Weight can't be blank"
  "Gender can't be blank"
  "Gender must be M or F"
  "Vaccinated can't be blank"
  "Vaccinated is not included in the list"
  "Fiv can't be blank"
  "Fiv is not included in the list"
  "Age can't be blank"
  "Age is not included in the list"
  "Rescued can't be blank"
  "Rescued is invalid"

Let’s create a new instance, and purposely pass invalid info for sex, weight, and rescued:

>> y = Cat.new
>> y.valid?
=> false
>> y.update_attributes({:name => 'Thumbelina', :age => 5, :sex => 'E', :weight => 0, :rescued => '28/03/1965', :vaccinated => true, :fiv => false, :comment => 'Kitty attempted to buy cigarettes with a fake ID'})
=> false

The returned false indicates it wasn’t saved, and hence, not valid. Let’s inspect further:

y.errors.each_full { |msg| p msg}
"Weight should be a minimum of 1 pound"
"Sex must be M or F"
"Rescued is invalid"

Let’s correct these errors and finally save the model.

>> y.update_attribute(:sex, 'F')
>> y.weight = 12
>> y.rescued = '03/28/1965'
>>; y.valid?
=> true
>> y.save
=> true

In a practical app, you would want the user to see these errors. This can be done by accessing the errors class in your view. Here’s an example:

  Failed to save new cat information due to the errors:

Take Two – Confirmation Fields in Views

One thing you’ll do often is confirm important fields to make sure the input is flawless. An example is asking the user for his password twice upon registration, to greatly reduce the chance of a typo the first time around.

Rails provides a validation for this called validates_confirmation_of. It does a comparison of the two password fields for you in memory. You no longer have to do “if passwd == passwd_conf …” to confirm passwords or other fields. You can trust that if this validation passes, then both fields were equivalent.

Here’s how it works. All you have to do is create another field (input box) in your view, with the same name as the initial value you want to confirm against, but suffixed with a “_confirmation” in the name.

For confirmation of a field named “email” in our model for example, we would have one text_field called “email” and another called “email_confirmation.” Now in the model we would have a validation like this:

validates_confirmation_of :email

.

You may need to use attr_accessor if you’re using form_for, otherwise you might get errors that email_confirmation doesn’t exist. and it doesn’t, it’s not part of the model. It’s only virtual, used when needed.

That’s basically it! Now this validation will fail if those two email fields aren’t the same.

Note: You need to add a few extra steps to this if you’re using a field that requires being altered right before being saved. You want to encrypt a password before saving it, for example, but if you alter the password, this would cause the validation to fail on save, since password no longer matches password_confirmation. There are ways around this. You can use :on => :create for instance, so that this validation only occurs upon Model.new.

This presents a problem if save fails for another reason (username taken, invalid email format, etc), as the field you altered will be refreshed back as-is, not as it was when the user entered it. So for example, they might see the password hash instead of the password they entered, if the form reloads due to an error. Anyway, you can use the before_create hook, or before_validation, after_validation, or other hooks, but I’m straying from the main topic, so I’ll leave this issue here (for now).

Anyway! there’s really nothing more to it. Validations are powerful, but simple and straightforward to implement.

Thanks to wolfteacher2 for the photo!

External Links

8 thoughts on “Introduction to Validations & Validation Error Handling in Rails”

  1. Pingback: Pantech Matrix Pro
  2. Hi. The code listing that includes the validation rules seems to be parsed wrong on Iceweasel 3.06 (Debian’s stable version). Maybe because of your used “less-than” symbol being misinterpreted as the beginning of an html tag.

    The respective line is the one with “record.errors.add attr.*”

    But nonetheless I agree with Jason: this is a nice overview of the AR validations.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>