May 3, 2016
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 at least 8 characters")
>> user.errors.details
=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]}
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.
If this blog was helpful, check out our full blog archive.