class ActiveModel::Errors

def add(attribute, message = :invalid, options = {})

# => {:base=>[{error: :name_or_email_blank}]}
person.errors.details
# => {:base=>["either name or email must be present"]}
person.errors.messages
message: "either name or email must be present")
person.errors.add(:base, :name_or_email_blank,

directly associated with a single attribute.
+attribute+ should be set to :base if the error is not

person.errors.messages # => {}

# => NameIsInvalid: Name is invalid
person.errors.add(:name, :invalid, strict: NameIsInvalid)
# => ActiveModel::StrictValidationFailed: Name is invalid
person.errors.add(:name, :invalid, strict: true)

:strict option can also be set to any other exception.
ActiveModel::StrictValidationFailed instead of adding the error.
If the :strict option is set to +true+, it will raise

Time.now to be used within an error.
If +message+ is a proc, it will be called, allowing for things like

scope (see +generate_message+).
If +message+ is a symbol, it will be translated using the appropriate

# => {:name=>[{error: :not_implemented}, {error: :invalid}]}
person.errors.details

# => {:name=>["is invalid", "must be implemented"]}
person.errors.messages

# => ["is invalid", "must be implemented"]
person.errors.add(:name, :not_implemented, message: "must be implemented")
# => ["is invalid"]
person.errors.add(:name)

If no +message+ is supplied, :invalid is assumed.
More than one error can be added to the same +attribute+.
Adds +message+ to the error messages and used validator type to +details+ on +attribute+.
def add(attribute, message = :invalid, options = {})
  message = message.call if message.respond_to?(:call)
  detail  = normalize_detail(message, options)
  message = normalize_message(attribute, message, options)
  if exception = options[:strict]
    exception = ActiveModel::StrictValidationFailed if exception == true
    raise exception, full_message(attribute, message)
  end
  details[attribute.to_sym]  << detail
  messages[attribute.to_sym] << message
end