module ErrorsWithTypesExtensions
attr_reader :types
# Pass in the instance of the object that is using the errors object.
#
# class Person
# def initialize
# @errors = ActiveModel::Errors.new(self)
# end
# end
def initialize(base)
@types = {}
super
end
def initialize_dup(other) # :nodoc:
@types = other.types.dup
super
end
# Clear the error messages.
#
# person.errors.full_messages # => ["name cannot be nil"]
# person.errors.clear
# person.errors.full_messages # => []
def clear
types.clear
super
end
def set_type(attribute, message, type)
messsage = full_message(attribute, message)
types[attribute] ||= {}
types[attribute][message] = type
end
# Returns the type of message for a given attribute's message if
# that message was set via a type symbol
#
# person.errors.type(:name, 'is invalid') # => :invalid
def type_for(attribute, message)
types[attribute].try(:[], message)
end
def messages_data
return nil if empty?
keys.inject([]) do | hashes, attribute |
messages[attribute].each do | message |
hashes << {
attribute: attribute,
full_message: full_message(attribute, message),
type: type_for(attribute, message)
}.with_indifferent_access
end
hashes
end
end
# Translates an error message in its default scope
# (<tt>activemodel.errors.messages</tt>).
#
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
# that is not there also, it returns the translation of the default message
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
# name, translated attribute name and the value are available for
# interpolation.
#
# When using inheritance in your models, it will check all the inherited
# models too, but only if the model itself hasn't been found. Say you have
# <tt>class Admin < User; end</tt> and you wanted the translation for
# the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
# it looks for these translations:
#
# * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
# * <tt>activemodel.errors.models.admin.blank</tt>
# * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
# * <tt>activemodel.errors.models.user.blank</tt>
# * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
# * <tt>activemodel.errors.messages.blank</tt>
# * <tt>errors.attributes.title.blank</tt>
# * <tt>errors.messages.blank</tt>
def generate_message(attribute, type = :invalid, options = {})
# generate_message is called any time an error message
# is added to an attribute. This seemed a cleaner place
# than `add` to monkey patch.
type = options[:message] if options[:message].is_a?(Symbol)
generated_message = super
set_type(attribute, generated_message, type)
generated_message
end
end
module ActiveModel
class Errors
prepend ErrorsWithTypesExtensions
# mix in our types extensions
# 'below' the base (ActiveModel) class definition
# so we _override_ the extensions instead of
# _overwriting_ them (e.g. so we can use `super`)
#
# http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i#answer-4471202
end
end