activemodel 6.1.7.10 → 7.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -224
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_model/api.rb +99 -0
  6. data/lib/active_model/attribute.rb +4 -0
  7. data/lib/active_model/attribute_methods.rb +99 -52
  8. data/lib/active_model/attribute_set.rb +4 -1
  9. data/lib/active_model/attributes.rb +15 -12
  10. data/lib/active_model/callbacks.rb +1 -1
  11. data/lib/active_model/conversion.rb +2 -2
  12. data/lib/active_model/dirty.rb +5 -4
  13. data/lib/active_model/errors.rb +17 -3
  14. data/lib/active_model/gem_version.rb +4 -4
  15. data/lib/active_model/locale/en.yml +1 -0
  16. data/lib/active_model/model.rb +6 -59
  17. data/lib/active_model/naming.rb +15 -8
  18. data/lib/active_model/secure_password.rb +0 -1
  19. data/lib/active_model/serialization.rb +7 -2
  20. data/lib/active_model/translation.rb +1 -1
  21. data/lib/active_model/type/helpers/numeric.rb +9 -1
  22. data/lib/active_model/type/helpers/time_value.rb +2 -2
  23. data/lib/active_model/type/integer.rb +4 -1
  24. data/lib/active_model/type/registry.rb +8 -38
  25. data/lib/active_model/type/time.rb +1 -1
  26. data/lib/active_model/type.rb +6 -6
  27. data/lib/active_model/validations/absence.rb +1 -1
  28. data/lib/active_model/validations/clusivity.rb +1 -1
  29. data/lib/active_model/validations/comparability.rb +29 -0
  30. data/lib/active_model/validations/comparison.rb +82 -0
  31. data/lib/active_model/validations/confirmation.rb +4 -4
  32. data/lib/active_model/validations/numericality.rb +27 -20
  33. data/lib/active_model/validations.rb +4 -4
  34. data/lib/active_model/validator.rb +2 -2
  35. data/lib/active_model.rb +2 -1
  36. metadata +14 -12
@@ -25,6 +25,10 @@ module ActiveModel
25
25
  attributes.transform_values(&:value_before_type_cast)
26
26
  end
27
27
 
28
+ def values_for_database
29
+ attributes.transform_values(&:value_for_database)
30
+ end
31
+
28
32
  def to_hash
29
33
  keys.index_with { |name| self[name].value }
30
34
  end
@@ -54,7 +58,6 @@ module ActiveModel
54
58
 
55
59
  def write_cast_value(name, value)
56
60
  @attributes[name] = self[name].with_cast_value(value)
57
- value
58
61
  end
59
62
 
60
63
  def freeze
@@ -4,25 +4,26 @@ require "active_model/attribute_set"
4
4
  require "active_model/attribute/user_provided_default"
5
5
 
6
6
  module ActiveModel
7
- module Attributes #:nodoc:
7
+ module Attributes # :nodoc:
8
8
  extend ActiveSupport::Concern
9
9
  include ActiveModel::AttributeMethods
10
10
 
11
11
  included do
12
- attribute_method_suffix "="
12
+ attribute_method_suffix "=", parameters: "value"
13
13
  class_attribute :attribute_types, :_default_attributes, instance_accessor: false
14
14
  self.attribute_types = Hash.new(Type.default_value)
15
15
  self._default_attributes = AttributeSet.new({})
16
16
  end
17
17
 
18
18
  module ClassMethods
19
- def attribute(name, type = Type::Value.new, **options)
19
+ def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
20
20
  name = name.to_s
21
- if type.is_a?(Symbol)
22
- type = ActiveModel::Type.lookup(type, **options.except(:default))
23
- end
24
- self.attribute_types = attribute_types.merge(name => type)
25
- define_default_attribute(name, options.fetch(:default, NO_DEFAULT_PROVIDED), type)
21
+
22
+ cast_type = Type.lookup(cast_type, **options) if Symbol === cast_type
23
+ cast_type ||= attribute_types[name]
24
+
25
+ self.attribute_types = attribute_types.merge(name => cast_type)
26
+ define_default_attribute(name, default, cast_type)
26
27
  define_attribute_method(name)
27
28
  end
28
29
 
@@ -46,10 +47,12 @@ module ActiveModel
46
47
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
47
48
  owner, name, writer: true,
48
49
  ) do |temp_method_name, attr_name_expr|
49
- owner <<
50
- "def #{temp_method_name}(value)" <<
51
- " _write_attribute(#{attr_name_expr}, value)" <<
52
- "end"
50
+ owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_model) do |batch|
51
+ batch <<
52
+ "def #{temp_method_name}(value)" <<
53
+ " _write_attribute(#{attr_name_expr}, value)" <<
54
+ "end"
55
+ end
53
56
  end
54
57
  end
55
58
 
@@ -63,7 +63,7 @@ module ActiveModel
63
63
  # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
64
64
  #
65
65
  module Callbacks
66
- def self.extended(base) #:nodoc:
66
+ def self.extended(base) # :nodoc:
67
67
  base.class_eval do
68
68
  include ActiveSupport::Callbacks
69
69
  end
@@ -96,10 +96,10 @@ module ActiveModel
96
96
  self.class._to_partial_path
97
97
  end
98
98
 
99
- module ClassMethods #:nodoc:
99
+ module ClassMethods # :nodoc:
100
100
  # Provide a class level cache for #to_partial_path. This is an
101
101
  # internal method and should not be accessed directly.
102
- def _to_partial_path #:nodoc:
102
+ def _to_partial_path # :nodoc:
103
103
  @_to_partial_path ||= begin
104
104
  element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
105
105
  collection = ActiveSupport::Inflector.tableize(name)
@@ -123,10 +123,11 @@ module ActiveModel
123
123
  include ActiveModel::AttributeMethods
124
124
 
125
125
  included do
126
- attribute_method_suffix "_changed?", "_change", "_will_change!", "_was"
127
- attribute_method_suffix "_previously_changed?", "_previous_change", "_previously_was"
128
- attribute_method_affix prefix: "restore_", suffix: "!"
129
- attribute_method_affix prefix: "clear_", suffix: "_change"
126
+ attribute_method_suffix "_previously_changed?", "_changed?", parameters: "**options"
127
+ attribute_method_suffix "_change", "_will_change!", "_was", parameters: false
128
+ attribute_method_suffix "_previous_change", "_previously_was", parameters: false
129
+ attribute_method_affix prefix: "restore_", suffix: "!", parameters: false
130
+ attribute_method_affix prefix: "clear_", suffix: "_change", parameters: false
130
131
  end
131
132
 
132
133
  def initialize_dup(other) # :nodoc:
@@ -378,6 +378,14 @@ module ActiveModel
378
378
  # If +type+ is a symbol, it will be translated using the appropriate
379
379
  # scope (see +generate_message+).
380
380
  #
381
+ # person.errors.add(:name, :blank)
382
+ # person.errors.messages
383
+ # # => {:name=>["can't be blank"]}
384
+ #
385
+ # person.errors.add(:name, :too_long, { count: 25 })
386
+ # person.errors.messages
387
+ # # => ["is too long (maximum is 25 characters)"]
388
+ #
381
389
  # If +type+ is a proc, it will be called, allowing for things like
382
390
  # <tt>Time.now</tt> to be used within an error.
383
391
  #
@@ -563,6 +571,12 @@ module ActiveModel
563
571
  add_from_legacy_details_hash(data["details"]) if data.key?("details")
564
572
  end
565
573
 
574
+ def inspect # :nodoc:
575
+ inspection = @errors.inspect
576
+
577
+ "#<#{self.class.name} #{inspection}>"
578
+ end
579
+
566
580
  private
567
581
  def normalize_arguments(attribute, type, **options)
568
582
  # Evaluate proc first
@@ -596,7 +610,7 @@ module ActiveModel
596
610
  end
597
611
  end
598
612
 
599
- class DeprecationHandlingMessageHash < SimpleDelegator
613
+ class DeprecationHandlingMessageHash < SimpleDelegator # :nodoc:
600
614
  def initialize(errors)
601
615
  @errors = errors
602
616
  super(prepare_content)
@@ -635,7 +649,7 @@ module ActiveModel
635
649
  end
636
650
  end
637
651
 
638
- class DeprecationHandlingMessageArray < SimpleDelegator
652
+ class DeprecationHandlingMessageArray < SimpleDelegator # :nodoc:
639
653
  def initialize(content, errors, attribute)
640
654
  @errors = errors
641
655
  @attribute = attribute
@@ -657,7 +671,7 @@ module ActiveModel
657
671
  end
658
672
  end
659
673
 
660
- class DeprecationHandlingDetailsHash < SimpleDelegator
674
+ class DeprecationHandlingDetailsHash < SimpleDelegator # :nodoc:
661
675
  def initialize(details)
662
676
  details.default = []
663
677
  details.freeze
@@ -7,10 +7,10 @@ module ActiveModel
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
11
- MINOR = 1
12
- TINY = 7
13
- PRE = "10"
10
+ MAJOR = 7
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "alpha1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -32,5 +32,6 @@ en:
32
32
  less_than: "must be less than %{count}"
33
33
  less_than_or_equal_to: "must be less than or equal to %{count}"
34
34
  other_than: "must be other than %{count}"
35
+ in: "must be in %{count}"
35
36
  odd: "must be odd"
36
37
  even: "must be even"
@@ -3,11 +3,10 @@
3
3
  module ActiveModel
4
4
  # == Active \Model \Basic \Model
5
5
  #
6
- # Includes the required interface for an object to interact with
7
- # Action Pack and Action View, using different Active Model modules.
8
- # It includes model name introspections, conversions, translations and
9
- # validations. Besides that, it allows you to initialize the object with a
10
- # hash of attributes, pretty much like Active Record does.
6
+ # Allows implementing models similar to <tt>ActiveRecord::Base</tt>.
7
+ # Includes <tt>ActiveModel::API</tt> for the required interface for an
8
+ # object to interact with Action Pack and Action View, but can be
9
+ # extended with other functionalities.
11
10
  #
12
11
  # A minimal implementation could be:
13
12
  #
@@ -20,23 +19,7 @@ module ActiveModel
20
19
  # person.name # => "bob"
21
20
  # person.age # => "18"
22
21
  #
23
- # Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
24
- # to return +false+, which is the most common case. You may want to override
25
- # it in your class to simulate a different scenario:
26
- #
27
- # class Person
28
- # include ActiveModel::Model
29
- # attr_accessor :id, :name
30
- #
31
- # def persisted?
32
- # self.id == 1
33
- # end
34
- # end
35
- #
36
- # person = Person.new(id: 1, name: 'bob')
37
- # person.persisted? # => true
38
- #
39
- # Also, if for some reason you need to run code on <tt>initialize</tt>, make
22
+ # If for some reason you need to run code on <tt>initialize</tt>, make
40
23
  # sure you call +super+ if you want the attributes hash initialization to
41
24
  # happen.
42
25
  #
@@ -58,42 +41,6 @@ module ActiveModel
58
41
  # (see below).
59
42
  module Model
60
43
  extend ActiveSupport::Concern
61
- include ActiveModel::AttributeAssignment
62
- include ActiveModel::Validations
63
- include ActiveModel::Conversion
64
-
65
- included do
66
- extend ActiveModel::Naming
67
- extend ActiveModel::Translation
68
- end
69
-
70
- # Initializes a new model with the given +params+.
71
- #
72
- # class Person
73
- # include ActiveModel::Model
74
- # attr_accessor :name, :age
75
- # end
76
- #
77
- # person = Person.new(name: 'bob', age: '18')
78
- # person.name # => "bob"
79
- # person.age # => "18"
80
- def initialize(attributes = {})
81
- assign_attributes(attributes) if attributes
82
-
83
- super()
84
- end
85
-
86
- # Indicates if the model is persisted. Default is +false+.
87
- #
88
- # class Person
89
- # include ActiveModel::Model
90
- # attr_accessor :id, :name
91
- # end
92
- #
93
- # person = Person.new(id: 1, name: 'bob')
94
- # person.persisted? # => false
95
- def persisted?
96
- false
97
- end
44
+ include ActiveModel::API
98
45
  end
99
46
  end
@@ -3,6 +3,7 @@
3
3
  require "active_support/core_ext/hash/except"
4
4
  require "active_support/core_ext/module/introspection"
5
5
  require "active_support/core_ext/module/redefine_method"
6
+ require "active_support/core_ext/module/delegation"
6
7
 
7
8
  module ActiveModel
8
9
  class Name
@@ -153,6 +154,7 @@ module ActiveModel
153
154
  # Returns a new ActiveModel::Name instance. By default, the +namespace+
154
155
  # and +name+ option will take the namespace and name of the given class
155
156
  # respectively.
157
+ # Use +locale+ argument for singularize and pluralize model name.
156
158
  #
157
159
  # module Foo
158
160
  # class Bar
@@ -161,7 +163,7 @@ module ActiveModel
161
163
  #
162
164
  # ActiveModel::Name.new(Foo::Bar).to_s
163
165
  # # => "Foo::Bar"
164
- def initialize(klass, namespace = nil, name = nil)
166
+ def initialize(klass, namespace = nil, name = nil, locale = :en)
165
167
  @name = name || klass.name
166
168
 
167
169
  raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
@@ -169,16 +171,17 @@ module ActiveModel
169
171
  @unnamespaced = @name.delete_prefix("#{namespace.name}::") if namespace
170
172
  @klass = klass
171
173
  @singular = _singularize(@name)
172
- @plural = ActiveSupport::Inflector.pluralize(@singular)
174
+ @plural = ActiveSupport::Inflector.pluralize(@singular, locale)
175
+ @uncountable = @plural == @singular
173
176
  @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
174
177
  @human = ActiveSupport::Inflector.humanize(@element)
175
178
  @collection = ActiveSupport::Inflector.tableize(@name)
176
179
  @param_key = (namespace ? _singularize(@unnamespaced) : @singular)
177
180
  @i18n_key = @name.underscore.to_sym
178
181
 
179
- @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
180
- @singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
181
- @route_key << "_index" if @plural == @singular
182
+ @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key, locale) : @plural.dup)
183
+ @singular_route_key = ActiveSupport::Inflector.singularize(@route_key, locale)
184
+ @route_key << "_index" if @uncountable
182
185
  end
183
186
 
184
187
  # Transform the model name into a more human format, using I18n. By default,
@@ -206,6 +209,10 @@ module ActiveModel
206
209
  I18n.translate(defaults.shift, **options)
207
210
  end
208
211
 
212
+ def uncountable?
213
+ @uncountable
214
+ end
215
+
209
216
  private
210
217
  def _singularize(string)
211
218
  ActiveSupport::Inflector.underscore(string).tr("/", "_")
@@ -232,7 +239,7 @@ module ActiveModel
232
239
  # is required to pass the \Active \Model Lint test. So either extending the
233
240
  # provided method below, or rolling your own is required.
234
241
  module Naming
235
- def self.extended(base) #:nodoc:
242
+ def self.extended(base) # :nodoc:
236
243
  base.silence_redefinition_of_method :model_name
237
244
  base.delegate :model_name, to: :class
238
245
  end
@@ -279,7 +286,7 @@ module ActiveModel
279
286
  # ActiveModel::Naming.uncountable?(Sheep) # => true
280
287
  # ActiveModel::Naming.uncountable?(Post) # => false
281
288
  def self.uncountable?(record_or_class)
282
- plural(record_or_class) == singular(record_or_class)
289
+ model_name_from_record_or_class(record_or_class).uncountable?
283
290
  end
284
291
 
285
292
  # Returns string to use while generating route names. It differs for
@@ -321,7 +328,7 @@ module ActiveModel
321
328
  model_name_from_record_or_class(record_or_class).param_key
322
329
  end
323
330
 
324
- def self.model_name_from_record_or_class(record_or_class) #:nodoc:
331
+ def self.model_name_from_record_or_class(record_or_class) # :nodoc:
325
332
  if record_or_class.respond_to?(:to_model)
326
333
  record_or_class.to_model.model_name
327
334
  else
@@ -94,7 +94,6 @@ module ActiveModel
94
94
 
95
95
  define_method("#{attribute}=") do |unencrypted_password|
96
96
  if unencrypted_password.nil?
97
- instance_variable_set("@#{attribute}", nil)
98
97
  self.public_send("#{attribute}_digest=", nil)
99
98
  elsif !unencrypted_password.empty?
100
99
  instance_variable_set("@#{attribute}", unencrypted_password)
@@ -123,7 +123,7 @@ module ActiveModel
123
123
  # user.serializable_hash(include: { notes: { only: 'title' }})
124
124
  # # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
125
125
  def serializable_hash(options = nil)
126
- attribute_names = attributes.keys
126
+ attribute_names = self.attribute_names
127
127
 
128
128
  return serializable_attributes(attribute_names) if options.blank?
129
129
 
@@ -148,6 +148,11 @@ module ActiveModel
148
148
  hash
149
149
  end
150
150
 
151
+ # Returns an array of attribute names as strings
152
+ def attribute_names # :nodoc:
153
+ attributes.keys
154
+ end
155
+
151
156
  private
152
157
  # Hook method defining how an attribute value should be retrieved for
153
158
  # serialization. By default this is assumed to be an instance named after
@@ -177,7 +182,7 @@ module ActiveModel
177
182
  # +association+ - name of the association
178
183
  # +records+ - the association record(s) to be serialized
179
184
  # +opts+ - options for the association records
180
- def serializable_add_includes(options = {}) #:nodoc:
185
+ def serializable_add_includes(options = {}) # :nodoc:
181
186
  return unless includes = options[:include]
182
187
 
183
188
  unless includes.is_a?(Hash)
@@ -17,7 +17,7 @@ module ActiveModel
17
17
  #
18
18
  # This also provides the required class methods for hooking into the
19
19
  # Rails internationalization API, including being able to define a
20
- # class based +i18n_scope+ and +lookup_ancestors+ to find translations in
20
+ # class-based +i18n_scope+ and +lookup_ancestors+ to find translations in
21
21
  # parent classes.
22
22
  module Translation
23
23
  include ActiveModel::Naming
@@ -25,10 +25,18 @@ module ActiveModel
25
25
  end
26
26
 
27
27
  def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc:
28
- super || number_to_non_number?(old_value, new_value_before_type_cast)
28
+ (super || number_to_non_number?(old_value, new_value_before_type_cast)) &&
29
+ !equal_nan?(old_value, new_value_before_type_cast)
29
30
  end
30
31
 
31
32
  private
33
+ def equal_nan?(old_value, new_value)
34
+ (old_value.is_a?(::Float) || old_value.is_a?(BigDecimal)) &&
35
+ old_value.nan? &&
36
+ old_value.instance_of?(new_value.class) &&
37
+ new_value.nan?
38
+ end
39
+
32
40
  def number_to_non_number?(old_value, new_value_before_type_cast)
33
41
  old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
34
42
  end
@@ -12,9 +12,9 @@ module ActiveModel
12
12
 
13
13
  if value.acts_like?(:time)
14
14
  if is_utc?
15
- value = value.getutc if value.respond_to?(:getutc) && !value.utc?
15
+ value = value.getutc if !value.utc?
16
16
  else
17
- value = value.getlocal if value.respond_to?(:getlocal)
17
+ value = value.getlocal
18
18
  end
19
19
  end
20
20
 
@@ -30,7 +30,10 @@ module ActiveModel
30
30
 
31
31
  def serializable?(value)
32
32
  cast_value = cast(value)
33
- in_range?(cast_value) && super
33
+ in_range?(cast_value) || begin
34
+ yield cast_value if block_given?
35
+ false
36
+ end
34
37
  end
35
38
 
36
39
  private
@@ -1,68 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
- # :stopdoc:
5
4
  module Type
6
- class Registry
5
+ class Registry # :nodoc:
7
6
  def initialize
8
- @registrations = []
7
+ @registrations = {}
9
8
  end
10
9
 
11
- def initialize_dup(other)
10
+ def initialize_copy(other)
12
11
  @registrations = @registrations.dup
13
12
  super
14
13
  end
15
14
 
16
- def register(type_name, klass = nil, **options, &block)
15
+ def register(type_name, klass = nil, &block)
17
16
  unless block_given?
18
17
  block = proc { |_, *args| klass.new(*args) }
19
18
  block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
20
19
  end
21
- registrations << registration_klass.new(type_name, block, **options)
20
+ registrations[type_name] = block
22
21
  end
23
22
 
24
23
  def lookup(symbol, *args)
25
- registration = find_registration(symbol, *args)
24
+ registration = registrations[symbol]
26
25
 
27
26
  if registration
28
- registration.call(self, symbol, *args)
27
+ registration.call(symbol, *args)
29
28
  else
30
29
  raise ArgumentError, "Unknown type #{symbol.inspect}"
31
30
  end
32
31
  end
33
- ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
32
+ ruby2_keywords(:lookup)
34
33
 
35
34
  private
36
35
  attr_reader :registrations
37
-
38
- def registration_klass
39
- Registration
40
- end
41
-
42
- def find_registration(symbol, *args, **kwargs)
43
- registrations.find { |r| r.matches?(symbol, *args, **kwargs) }
44
- end
45
- end
46
-
47
- class Registration
48
- # Options must be taken because of https://bugs.ruby-lang.org/issues/10856
49
- def initialize(name, block, **)
50
- @name = name
51
- @block = block
52
- end
53
-
54
- def call(_registry, *args)
55
- block.call(*args)
56
- end
57
- ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
58
-
59
- def matches?(type_name, *args, **kwargs)
60
- type_name == name
61
- end
62
-
63
- private
64
- attr_reader :name, :block
65
36
  end
66
37
  end
67
- # :startdoc:
68
38
  end
@@ -33,7 +33,7 @@ module ActiveModel
33
33
  return apply_seconds_precision(value) unless value.is_a?(::String)
34
34
  return if value.empty?
35
35
 
36
- dummy_time_value = value.sub(/\A(\d\d\d\d-\d\d-\d\d |)/, "2000-01-01 ")
36
+ dummy_time_value = value.sub(/\A\d{4}-\d\d-\d\d(?:T|\s)|/, "2000-01-01 ")
37
37
 
38
38
  fast_string_to_time(dummy_time_value) || begin
39
39
  time_hash = ::Date._parse(dummy_time_value)
@@ -24,15 +24,15 @@ module ActiveModel
24
24
  class << self
25
25
  attr_accessor :registry # :nodoc:
26
26
 
27
- # Add a new type to the registry, allowing it to be gotten through ActiveModel::Type#lookup
28
- def register(type_name, klass = nil, **options, &block)
29
- registry.register(type_name, klass, **options, &block)
27
+ # Add a new type to the registry, allowing it to be referenced as a
28
+ # symbol by {attribute}[rdoc-ref:Attributes::ClassMethods#attribute].
29
+ def register(type_name, klass = nil, &block)
30
+ registry.register(type_name, klass, &block)
30
31
  end
31
32
 
32
- def lookup(*args) # :nodoc:
33
- registry.lookup(*args)
33
+ def lookup(...) # :nodoc:
34
+ registry.lookup(...)
34
35
  end
35
- ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
36
36
 
37
37
  def default_value # :nodoc:
38
38
  @default_value ||= Value.new
@@ -3,7 +3,7 @@
3
3
  module ActiveModel
4
4
  module Validations
5
5
  # == \Active \Model Absence Validator
6
- class AbsenceValidator < EachValidator #:nodoc:
6
+ class AbsenceValidator < EachValidator # :nodoc:
7
7
  def validate_each(record, attr_name, value)
8
8
  record.errors.add(attr_name, :present, **options) if value.present?
9
9
  end
@@ -4,7 +4,7 @@ require "active_support/core_ext/range"
4
4
 
5
5
  module ActiveModel
6
6
  module Validations
7
- module Clusivity #:nodoc:
7
+ module Clusivity # :nodoc:
8
8
  ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
9
9
  "and must be supplied as the :in (or :within) option of the configuration hash"
10
10
 
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Validations
5
+ module Comparability # :nodoc:
6
+ COMPARE_CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
7
+ equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
8
+ other_than: :!= }.freeze
9
+
10
+ def option_value(record, option_value)
11
+ case option_value
12
+ when Proc
13
+ option_value.call(record)
14
+ when Symbol
15
+ record.send(option_value)
16
+ else
17
+ option_value
18
+ end
19
+ end
20
+
21
+ def error_options(value, option_value)
22
+ options.except(*COMPARE_CHECKS.keys).merge!(
23
+ count: option_value,
24
+ value: value
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end