Class: AdequateErrors::Error

Inherits:
Object
  • Object
show all
Defined in:
lib/adequate_errors/error.rb

Overview

Represents one single error

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base, attribute, type, options = {}) ⇒ Error


12
13
14
15
16
17
# File 'lib/adequate_errors/error.rb', line 12

def initialize(base, attribute, type, options = {})
  @base = base
  @attribute = attribute
  @type = type
  @options = options
end

Instance Attribute Details

#attributeSymbol (readonly)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/adequate_errors/error.rb', line 11

class Error
  def initialize(base, attribute, type, options = {})
    @base = base
    @attribute = attribute
    @type = type
    @options = options
  end

  attr_reader :base, :attribute, :type, :options

  # 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 <tt>activemodel.adequate_errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
  # if it's not there, it's looked up in <tt>activemodel.adequate_errors.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.adequate_errors.models.admin.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.admin.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.blank</tt>
  # * any default you provided through the +options+ hash (in the <tt>activemodel.adequate_errors</tt> scope)
  # * <tt>activemodel.adequate_errors.messages.blank</tt>
  # * <tt>adequate_errors.attributes.title.blank</tt>
  # * <tt>adequate_errors.messages.blank</tt>
  def message
    if @options[:message].is_a?(Symbol)
      type = @options.delete(:message)
    else
      type = @type
    end

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

    defaults << :"#{@base.class.i18n_scope}.adequate_errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
    defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
    defaults << :"adequate_errors.messages.#{type}"

    defaults.compact!
    defaults.flatten!

    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
    }.merge!(@options)

    I18n.translate(key, i18n_options)
  end

  # @param (see Errors#where)
  # @return [Boolean] whether error matches the params
  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

  private

  def humanized_attribute
    default = @attribute.to_s.tr(".", "_").humanize
    @base.class.human_attribute_name(@attribute, default: default)
  end

end

#baseActiveModel::Base (readonly)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/adequate_errors/error.rb', line 11

class Error
  def initialize(base, attribute, type, options = {})
    @base = base
    @attribute = attribute
    @type = type
    @options = options
  end

  attr_reader :base, :attribute, :type, :options

  # 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 <tt>activemodel.adequate_errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
  # if it's not there, it's looked up in <tt>activemodel.adequate_errors.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.adequate_errors.models.admin.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.admin.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.blank</tt>
  # * any default you provided through the +options+ hash (in the <tt>activemodel.adequate_errors</tt> scope)
  # * <tt>activemodel.adequate_errors.messages.blank</tt>
  # * <tt>adequate_errors.attributes.title.blank</tt>
  # * <tt>adequate_errors.messages.blank</tt>
  def message
    if @options[:message].is_a?(Symbol)
      type = @options.delete(:message)
    else
      type = @type
    end

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

    defaults << :"#{@base.class.i18n_scope}.adequate_errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
    defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
    defaults << :"adequate_errors.messages.#{type}"

    defaults.compact!
    defaults.flatten!

    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
    }.merge!(@options)

    I18n.translate(key, i18n_options)
  end

  # @param (see Errors#where)
  # @return [Boolean] whether error matches the params
  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

  private

  def humanized_attribute
    default = @attribute.to_s.tr(".", "_").humanize
    @base.class.human_attribute_name(@attribute, default: default)
  end

end

#optionsHash (readonly)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/adequate_errors/error.rb', line 11

class Error
  def initialize(base, attribute, type, options = {})
    @base = base
    @attribute = attribute
    @type = type
    @options = options
  end

  attr_reader :base, :attribute, :type, :options

  # 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 <tt>activemodel.adequate_errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
  # if it's not there, it's looked up in <tt>activemodel.adequate_errors.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.adequate_errors.models.admin.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.admin.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.blank</tt>
  # * any default you provided through the +options+ hash (in the <tt>activemodel.adequate_errors</tt> scope)
  # * <tt>activemodel.adequate_errors.messages.blank</tt>
  # * <tt>adequate_errors.attributes.title.blank</tt>
  # * <tt>adequate_errors.messages.blank</tt>
  def message
    if @options[:message].is_a?(Symbol)
      type = @options.delete(:message)
    else
      type = @type
    end

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

    defaults << :"#{@base.class.i18n_scope}.adequate_errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
    defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
    defaults << :"adequate_errors.messages.#{type}"

    defaults.compact!
    defaults.flatten!

    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
    }.merge!(@options)

    I18n.translate(key, i18n_options)
  end

  # @param (see Errors#where)
  # @return [Boolean] whether error matches the params
  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

  private

  def humanized_attribute
    default = @attribute.to_s.tr(".", "_").humanize
    @base.class.human_attribute_name(@attribute, default: default)
  end

end

#typeSymbol (readonly)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/adequate_errors/error.rb', line 11

class Error
  def initialize(base, attribute, type, options = {})
    @base = base
    @attribute = attribute
    @type = type
    @options = options
  end

  attr_reader :base, :attribute, :type, :options

  # 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 <tt>activemodel.adequate_errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
  # if it's not there, it's looked up in <tt>activemodel.adequate_errors.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.adequate_errors.models.admin.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.admin.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.attributes.title.blank</tt>
  # * <tt>activemodel.adequate_errors.models.user.blank</tt>
  # * any default you provided through the +options+ hash (in the <tt>activemodel.adequate_errors</tt> scope)
  # * <tt>activemodel.adequate_errors.messages.blank</tt>
  # * <tt>adequate_errors.attributes.title.blank</tt>
  # * <tt>adequate_errors.messages.blank</tt>
  def message
    if @options[:message].is_a?(Symbol)
      type = @options.delete(:message)
    else
      type = @type
    end

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

    defaults << :"#{@base.class.i18n_scope}.adequate_errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
    defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
    defaults << :"adequate_errors.messages.#{type}"

    defaults.compact!
    defaults.flatten!

    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
    }.merge!(@options)

    I18n.translate(key, i18n_options)
  end

  # @param (see Errors#where)
  # @return [Boolean] whether error matches the params
  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

  private

  def humanized_attribute
    default = @attribute.to_s.tr(".", "_").humanize
    @base.class.human_attribute_name(@attribute, default: default)
  end

end

Instance Method Details

#match?(params) ⇒ Boolean


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/adequate_errors/error.rb', line 105

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

#messageObject

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


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 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)
    defaults = @base.class.lookup_ancestors.map do |klass|
      [ :"#{@base.class.i18n_scope}.adequate_errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
        :"#{@base.class.i18n_scope}.adequate_errors.models.#{klass.model_name.i18n_key}.#{type}" ]
    end
  else
    defaults = []
  end

  defaults << :"#{@base.class.i18n_scope}.adequate_errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
  defaults << :"adequate_errors.attributes.#{attribute}.#{type}"
  defaults << :"adequate_errors.messages.#{type}"

  defaults.compact!
  defaults.flatten!

  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
  }.merge!(@options)

  I18n.translate(key, i18n_options)
end