class AdequateErrors::Error

Represents one single error @!attribute [r] base

@return [ActiveModel::Base] the object which the error belongs to

@!attribute [r] attribute

@return [Symbol] attribute of the object which the error belongs to

@!attribute [r] type

@return [Symbol] error's type

@!attribute [r] options

@return [Hash] additional options

Attributes

attribute[R]
base[R]
options[R]
type[R]

Public Class Methods

new(base, attribute, type, options = {}) click to toggle source
# File lib/adequate_errors/error.rb, line 12
def initialize(base, attribute, type, options = {})
  @base = base
  @attribute = attribute
  @type = type
  @options = options
end

Public Instance Methods

match?(params) click to toggle source

@param (see Errors#where) @return [Boolean] whether error matches the params

# File lib/adequate_errors/error.rb, line 107
def match?(params)
  if params.key?(:attribute) && @attribute != params[:attribute]
    return false
  end

  if params.key?(:type) && @type != params[:type]
    return false
  end

  (params.keys - [:attribute, :type]).each do |key|
    if @options[key] != params[key]
      return false
    end
  end

  true
end
message() click to toggle source

Full message of the error.

Key differences to Rails vanilla errors

1. Flexible positioning of attribute name interpolation

In Rails, errors' full messages are always prefixed with attribute name, and if prefix is not wanted, developer often adds error to the `base' attribute instead. This can be unreasonable in different languages or special business requirements.

AdequateErrors leaves the attribute placement to the developer. For each error message in the locale file, the %{attribute} indicates placement of the attribute. The same error can have prefix in English, and be prefix-less in Russian. If no prefix is needed, one should update the locale file accordingly,

2. Message evaluated lazily

In Rails, error message is evaluated during the `add` call. AdequateErrors evaluates message lazily at `message` call instead, so one can change message locale after model has been validated.

Order of I18n lookup:

Error messages are first looked up in activemodel.adequate_errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE, if it's not there, it's looked up in activemodel.adequate_errors.models.MODEL.MESSAGE and if that is not there also, it returns the translation of the default message (e.g. activemodel.errors.messages.MESSAGE). 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 class Admin < User; end and you wanted the translation for the :blank error message for the title attribute, it looks for these translations:

  • activemodel.adequate_errors.models.admin.attributes.title.blank

  • activemodel.adequate_errors.models.admin.blank

  • activemodel.adequate_errors.models.user.attributes.title.blank

  • activemodel.adequate_errors.models.user.blank

  • any default you provided through the options hash (in the activemodel.adequate_errors scope)

  • activemodel.adequate_errors.messages.blank

  • adequate_errors.attributes.title.blank

  • adequate_errors.messages.blank

# File lib/adequate_errors/error.rb, line 65
def message
  if @options[:message].is_a?(Symbol)
    type = @options.delete(:message)
  else
    type = @type
  end

  if @base.class.respond_to?(:i18n_scope)
    i18n_scope = @base.class.i18n_scope.to_s
    defaults = @base.class.lookup_ancestors.flat_map do |klass|
      [ :"#{i18n_scope}.adequate_errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
        :"#{i18n_scope}.adequate_errors.models.#{klass.model_name.i18n_key}.#{type}" ]
    end
    defaults << :"#{i18n_scope}.adequate_errors.messages.#{type}"
  else
    defaults = []
  end

  defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
  defaults << :"adequate_errors.messages.#{type}"

  key = defaults.shift
  defaults = @options.delete(:message) if @options[:message]
  value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)

  i18n_options = {
    default: defaults,
    model: @base.model_name.human,
    attribute: humanized_attribute,
    value: value,
    object: @base,
    exception_handler: ->(exception, locale, key, option) {
      rails_errors = @base.errors
      rails_errors.full_message(@attribute, rails_errors.generate_message(@attribute, @type, @options))
    }
  }.merge!(@options)

  I18n.translate(key, i18n_options)
end

Private Instance Methods

humanized_attribute() click to toggle source
# File lib/adequate_errors/error.rb, line 127
def humanized_attribute
  default = @attribute.to_s.tr(".", "_").humanize
  @base.class.human_attribute_name(@attribute, default: default)
end