One of the first things we learn in Rails which are greatly useful are ActiveRecord validations. However, since they are easy to add, it happens frequently that we are burdening our users with validations instead of making forms easier and clearer.
For instance, let’s suppose we are validating the Social Security Number (SSN) of an user on signup. A sample code would be:
1 2 3 4 5 6 7
class User < ActiveRecord::Base validates_presence_of :ssn validates_length_of :ssn, :is => 9 validates_numericality_of :ssn validates_uniqueness_of :ssn validates_as_ssn :ssn # Checks if a reserved or special SSN was sent end
With the configuration above, if the user forgets to fill in the SSN, leaving it blank, four error messages related to the SSN field will be shown:
- SSN can’t be blank
- SSN is the wrong length (should be 9 characters)
- SSN is not a number
- SSN is invalid
The question is: if the user just left the field blank, why we should show all those errors to him? Are they all relevant?
Luckily, the solution is quite simple: we can make use of the :allow_blank option, so other validations won’t be triggered if the field is blank:
1 2 3 4 5 6 7
class User < ActiveRecord::Base validates_presence_of :ssn validates_length_of :ssn, :is => 11, :allow_blank => true validates_numericality_of :ssn, :allow_blank => true validates_uniqueness_of :ssn, :allow_blank => true validates_as_ssn :ssn, :allow_blank => true end
This is also a nice use case for the Object#with_options method added by Rails:
1 2 3 4 5 6 7 8 9 10
class User < ActiveRecord::Base validates_presence_of :ssn with_options :allow_blank => true do |v| v.validates_length_of :ssn, :is => 11 v.validates_numericality_of :ssn v.validates_uniqueness_of :ssn v.validates_as_ssn :ssn end end
SSN is just an example, but we are frequently burdening our users in username, e-mail, password and many other fields.
Do not validate presence of confirmation fields
Another interesting subject about validations are the confirmation fields. We have the following note in the documentation of validates_confirmation_of:
validates_confirmation_of :password # NOTE: This check is performed only if password_confirmation is not nil
And this is a feature. This means that you don’t need to give the :password_confirmation key when creating objects in console or when writing tests:
1 2 3
it "should be valid with valid attributes" do User.new(:password => "123456").should be_valid end
If the test fails, you are validating the presence of :password_confirmation, which is unnecessary. Since the :password_confirmation field will be available in the views, it will always be sent. So if the user leave it blank, it’s sent as blank value to the model and therefore will be checked. Just in the place it needs to be.