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.