This blog is part of our Rails 5 series.
Let’s look at a validation example in Rails 4.x.
class User < ActiveRecord::Base
validates :email, presence: true
end
>> user = User.new
>> user.valid?
=> false
>> user.errors.messages
=> {:email=>["can't be blank"]}
In this case,
we do not get
any information
about the type of failed validation
as
ActiveModel#Errors
only gives
the attribute name and the translated error message.
This works out well for normal apps.
But in case of API only applications,
sometimes
we want to allow
the client consuming the API
to generate customized error message as per their needs.
We don’t want to send the final translated messages in such cases.
Instead
if we could just send details
that presence
validation failed for :name
attribute,
the client app would be able to customize the error message
based on that information.
In Rails 5, it is now possible to get such details about which validations failed for a given attribute.
We can check this by calling
details method
on the ActiveModel#Errors
instance.
class User < ApplicationRecord
validates :email, presence: true
end
>> user = User.new
>> user.valid?
=> false
>> user.errors.details
=> {:email=>[{:error=>:blank}]}
We can also add custom validator types as per our need.
# Custom validator type
>> user = User.new
>> user.errors.add(:name, :not_valid, message: "The name appears invalid")
>> user.errors.details
=> {:name=>[{:error=>:not_valid}]}
# Custom error with default validator type :invalid
>> user = User.new
>> user.errors.add(:name)
>> user.errors.details
=> {:name=>[{:error=>:invalid}]}
# More than one error on one attribute
>> user = User.new
>> user.errors.add(:password, :invalid_format, message: "Password must start with an alphabet")
>> user.errors.add(:password, :invalid_length, message: "Password must have atleast 8 characters")
>> user.errors.details
=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]}
Passing contextual information about the errors
We can also send contextual data for the validation to the Errors#add
method.
This data can be later accessed
via Errors#details
method
because Errors#add
method
forwards all options except :message
, :if
, :unless
, and :on
to details
.
For eg. we can say that the password
is invalid because !
is not allowed, as follows.
class User < ApplicationRecord
validate :password_cannot_have_invalid_character
def password_cannot_have_invalid_character
if password.scan("!").present?
errors.add(:password, :invalid_character, not_allowed: "!")
end
end
end
>> user = User.create(name: 'Mark', password: 'Ra!ls')
>> user.errors.details
=> {:password=>[{:error=>:invalid_character, :not_allowed=>"!"}]}
We can also use this feature in our Rails 4.x apps by simply installing gem active_model-errors_details.