This blog is part of our Rails 5 series.
Let's look at a validation example in Rails 4.x.
1class User < ActiveRecord::Base 2 validates :email, presence: true 3end 4 5>> user = User.new 6>> user.valid? 7=> false 8 9>> user.errors.messages 10=> {:email=>["can't be blank"]} 11
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.
1 2class User < ApplicationRecord 3 validates :email, presence: true 4end 5 6>> user = User.new 7>> user.valid? 8=> false 9 10>> user.errors.details 11=> {:email=>[{:error=>:blank}]} 12
We can also add custom validator types as per our need.
1 2# Custom validator type 3>> user = User.new 4>> user.errors.add(:name, :not_valid, message: "The name appears invalid") 5 6>> user.errors.details 7=> {:name=>[{:error=>:not_valid}]} 8 9# Custom error with default validator type :invalid 10 11>> user = User.new 12>> user.errors.add(:name) 13 14>> user.errors.details 15=> {:name=>[{:error=>:invalid}]} 16 17# More than one error on one attribute 18 19>> user = User.new 20>> user.errors.add(:password, :invalid_format, message: "Password must start with an alphabet") 21>> user.errors.add(:password, :invalid_length, message: "Password must have at least 8 characters") 22 23>> user.errors.details 24=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]} 25
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.
1 2class User < ApplicationRecord 3 validate :password_cannot_have_invalid_character 4 5 def password_cannot_have_invalid_character 6 if password.scan("!").present? 7 errors.add(:password, :invalid_character, not_allowed: "!") 8 end 9 end 10end 11 12>> user = User.create(name: 'Mark', password: 'Ra!ls') 13>> user.errors.details 14=> {:password=>[{:error=>:invalid_character, :not_allowed=>"!"}]} 15
We can also use this feature in our Rails 4.x apps by simply installing gem active_model-errors_details.