activemodel 7.0.3.1 → 6.1.7.1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -84
  3. data/MIT-LICENSE +0 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_model/attribute.rb +0 -4
  6. data/lib/active_model/attribute_methods.rb +82 -67
  7. data/lib/active_model/attribute_set/builder.rb +10 -1
  8. data/lib/active_model/attribute_set.rb +1 -4
  9. data/lib/active_model/attributes.rb +12 -15
  10. data/lib/active_model/callbacks.rb +3 -3
  11. data/lib/active_model/conversion.rb +2 -2
  12. data/lib/active_model/dirty.rb +4 -5
  13. data/lib/active_model/error.rb +2 -2
  14. data/lib/active_model/errors.rb +248 -55
  15. data/lib/active_model/gem_version.rb +4 -4
  16. data/lib/active_model/locale/en.yml +0 -1
  17. data/lib/active_model/model.rb +59 -6
  18. data/lib/active_model/naming.rb +8 -15
  19. data/lib/active_model/secure_password.rb +1 -1
  20. data/lib/active_model/serialization.rb +2 -6
  21. data/lib/active_model/translation.rb +2 -2
  22. data/lib/active_model/type/date.rb +1 -1
  23. data/lib/active_model/type/helpers/numeric.rb +1 -9
  24. data/lib/active_model/type/helpers/time_value.rb +3 -3
  25. data/lib/active_model/type/integer.rb +1 -4
  26. data/lib/active_model/type/registry.rb +38 -8
  27. data/lib/active_model/type/time.rb +1 -1
  28. data/lib/active_model/type.rb +6 -6
  29. data/lib/active_model/validations/absence.rb +2 -2
  30. data/lib/active_model/validations/acceptance.rb +1 -1
  31. data/lib/active_model/validations/callbacks.rb +1 -1
  32. data/lib/active_model/validations/clusivity.rb +1 -1
  33. data/lib/active_model/validations/confirmation.rb +5 -5
  34. data/lib/active_model/validations/exclusion.rb +3 -3
  35. data/lib/active_model/validations/format.rb +1 -1
  36. data/lib/active_model/validations/inclusion.rb +3 -3
  37. data/lib/active_model/validations/length.rb +2 -2
  38. data/lib/active_model/validations/numericality.rb +22 -29
  39. data/lib/active_model/validations/presence.rb +1 -1
  40. data/lib/active_model/validations/validates.rb +3 -3
  41. data/lib/active_model/validations/with.rb +4 -4
  42. data/lib/active_model/validations.rb +12 -12
  43. data/lib/active_model/validator.rb +5 -5
  44. data/lib/active_model/version.rb +1 -1
  45. data/lib/active_model.rb +0 -1
  46. metadata +9 -12
  47. data/lib/active_model/api.rb +0 -99
  48. data/lib/active_model/validations/comparability.rb +0 -29
  49. data/lib/active_model/validations/comparison.rb +0 -82
@@ -3,7 +3,6 @@
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"
7
6
 
8
7
  module ActiveModel
9
8
  class Name
@@ -154,7 +153,6 @@ module ActiveModel
154
153
  # Returns a new ActiveModel::Name instance. By default, the +namespace+
155
154
  # and +name+ option will take the namespace and name of the given class
156
155
  # respectively.
157
- # Use +locale+ argument for singularize and pluralize model name.
158
156
  #
159
157
  # module Foo
160
158
  # class Bar
@@ -163,7 +161,7 @@ module ActiveModel
163
161
  #
164
162
  # ActiveModel::Name.new(Foo::Bar).to_s
165
163
  # # => "Foo::Bar"
166
- def initialize(klass, namespace = nil, name = nil, locale = :en)
164
+ def initialize(klass, namespace = nil, name = nil)
167
165
  @name = name || klass.name
168
166
 
169
167
  raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
@@ -171,17 +169,16 @@ module ActiveModel
171
169
  @unnamespaced = @name.delete_prefix("#{namespace.name}::") if namespace
172
170
  @klass = klass
173
171
  @singular = _singularize(@name)
174
- @plural = ActiveSupport::Inflector.pluralize(@singular, locale)
175
- @uncountable = @plural == @singular
172
+ @plural = ActiveSupport::Inflector.pluralize(@singular)
176
173
  @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
177
174
  @human = ActiveSupport::Inflector.humanize(@element)
178
175
  @collection = ActiveSupport::Inflector.tableize(@name)
179
176
  @param_key = (namespace ? _singularize(@unnamespaced) : @singular)
180
177
  @i18n_key = @name.underscore.to_sym
181
178
 
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
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
185
182
  end
186
183
 
187
184
  # Transform the model name into a more human format, using I18n. By default,
@@ -209,10 +206,6 @@ module ActiveModel
209
206
  I18n.translate(defaults.shift, **options)
210
207
  end
211
208
 
212
- def uncountable?
213
- @uncountable
214
- end
215
-
216
209
  private
217
210
  def _singularize(string)
218
211
  ActiveSupport::Inflector.underscore(string).tr("/", "_")
@@ -239,7 +232,7 @@ module ActiveModel
239
232
  # is required to pass the \Active \Model Lint test. So either extending the
240
233
  # provided method below, or rolling your own is required.
241
234
  module Naming
242
- def self.extended(base) # :nodoc:
235
+ def self.extended(base) #:nodoc:
243
236
  base.silence_redefinition_of_method :model_name
244
237
  base.delegate :model_name, to: :class
245
238
  end
@@ -286,7 +279,7 @@ module ActiveModel
286
279
  # ActiveModel::Naming.uncountable?(Sheep) # => true
287
280
  # ActiveModel::Naming.uncountable?(Post) # => false
288
281
  def self.uncountable?(record_or_class)
289
- model_name_from_record_or_class(record_or_class).uncountable?
282
+ plural(record_or_class) == singular(record_or_class)
290
283
  end
291
284
 
292
285
  # Returns string to use while generating route names. It differs for
@@ -328,7 +321,7 @@ module ActiveModel
328
321
  model_name_from_record_or_class(record_or_class).param_key
329
322
  end
330
323
 
331
- def self.model_name_from_record_or_class(record_or_class) # :nodoc:
324
+ def self.model_name_from_record_or_class(record_or_class) #:nodoc:
332
325
  if record_or_class.respond_to?(:to_model)
333
326
  record_or_class.to_model.model_name
334
327
  else
@@ -119,7 +119,7 @@ module ActiveModel
119
119
  # user.authenticate_password('mUc3m00RsqyRe') # => user
120
120
  define_method("authenticate_#{attribute}") do |unencrypted_password|
121
121
  attribute_digest = public_send("#{attribute}_digest")
122
- attribute_digest.present? && BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
122
+ BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
123
123
  end
124
124
 
125
125
  alias_method :authenticate, :authenticate_password if attribute == :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 = attribute_names_for_serialization
126
+ attribute_names = attributes.keys
127
127
 
128
128
  return serializable_attributes(attribute_names) if options.blank?
129
129
 
@@ -149,10 +149,6 @@ module ActiveModel
149
149
  end
150
150
 
151
151
  private
152
- def attribute_names_for_serialization
153
- attributes.keys
154
- end
155
-
156
152
  # Hook method defining how an attribute value should be retrieved for
157
153
  # serialization. By default this is assumed to be an instance named after
158
154
  # the attribute. Override this method in subclasses should you need to
@@ -181,7 +177,7 @@ module ActiveModel
181
177
  # +association+ - name of the association
182
178
  # +records+ - the association record(s) to be serialized
183
179
  # +opts+ - options for the association records
184
- def serializable_add_includes(options = {}) # :nodoc:
180
+ def serializable_add_includes(options = {}) #:nodoc:
185
181
  return unless includes = options[:include]
186
182
 
187
183
  unless includes.is_a?(Hash)
@@ -17,12 +17,12 @@ 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
24
24
 
25
- # Returns the +i18n_scope+ for the class. Override if you want custom lookup.
25
+ # Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
26
26
  def i18n_scope
27
27
  :activemodel
28
28
  end
@@ -11,7 +11,7 @@ module ActiveModel
11
11
  end
12
12
 
13
13
  def type_cast_for_schema(value)
14
- value.to_fs(:db).inspect
14
+ value.to_s(:db).inspect
15
15
  end
16
16
 
17
17
  private
@@ -25,18 +25,10 @@ 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)) &&
29
- !equal_nan?(old_value, new_value_before_type_cast)
28
+ super || number_to_non_number?(old_value, new_value_before_type_cast)
30
29
  end
31
30
 
32
31
  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
-
40
32
  def number_to_non_number?(old_value, new_value_before_type_cast)
41
33
  old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
42
34
  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.utc?
15
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
16
16
  else
17
- value = value.getlocal
17
+ value = value.getlocal if value.respond_to?(:getlocal)
18
18
  end
19
19
  end
20
20
 
@@ -36,7 +36,7 @@ module ActiveModel
36
36
  end
37
37
 
38
38
  def type_cast_for_schema(value)
39
- value.to_fs(:db).inspect
39
+ value.to_s(:db).inspect
40
40
  end
41
41
 
42
42
  def user_input_in_time_zone(value)
@@ -30,10 +30,7 @@ module ActiveModel
30
30
 
31
31
  def serializable?(value)
32
32
  cast_value = cast(value)
33
- in_range?(cast_value) || begin
34
- yield cast_value if block_given?
35
- false
36
- end
33
+ in_range?(cast_value) && super
37
34
  end
38
35
 
39
36
  private
@@ -1,38 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
+ # :stopdoc:
4
5
  module Type
5
- class Registry # :nodoc:
6
+ class Registry
6
7
  def initialize
7
- @registrations = {}
8
+ @registrations = []
8
9
  end
9
10
 
10
- def initialize_copy(other)
11
+ def initialize_dup(other)
11
12
  @registrations = @registrations.dup
12
13
  super
13
14
  end
14
15
 
15
- def register(type_name, klass = nil, &block)
16
+ def register(type_name, klass = nil, **options, &block)
16
17
  unless block_given?
17
18
  block = proc { |_, *args| klass.new(*args) }
18
19
  block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
19
20
  end
20
- registrations[type_name] = block
21
+ registrations << registration_klass.new(type_name, block, **options)
21
22
  end
22
23
 
23
24
  def lookup(symbol, *args)
24
- registration = registrations[symbol]
25
+ registration = find_registration(symbol, *args)
25
26
 
26
27
  if registration
27
- registration.call(symbol, *args)
28
+ registration.call(self, symbol, *args)
28
29
  else
29
30
  raise ArgumentError, "Unknown type #{symbol.inspect}"
30
31
  end
31
32
  end
32
- ruby2_keywords(:lookup)
33
+ ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
33
34
 
34
35
  private
35
36
  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
36
65
  end
37
66
  end
67
+ # :startdoc:
38
68
  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{4}-\d\d-\d\d(?:T|\s)|/, "2000-01-01 ")
36
+ dummy_time_value = value.sub(/\A(\d\d\d\d-\d\d-\d\d |)/, "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 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)
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)
31
30
  end
32
31
 
33
- def lookup(...) # :nodoc:
34
- registry.lookup(...)
32
+ def lookup(*args) # :nodoc:
33
+ registry.lookup(*args)
35
34
  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
@@ -24,7 +24,7 @@ module ActiveModel
24
24
  #
25
25
  # There is also a list of default options supported by every validator:
26
26
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
27
- # See ActiveModel::Validations::ClassMethods#validates for more information.
27
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
28
28
  def validates_absence_of(*attr_names)
29
29
  validates_with AbsenceValidator, _merge_attributes(attr_names)
30
30
  end
@@ -104,7 +104,7 @@ module ActiveModel
104
104
  #
105
105
  # There is also a list of default options supported by every validator:
106
106
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
107
- # See ActiveModel::Validations::ClassMethods#validates for more information.
107
+ # See <tt>ActiveModel::Validations#validates</tt> for more information.
108
108
  def validates_acceptance_of(*attr_names)
109
109
  validates_with AcceptanceValidator, _merge_attributes(attr_names)
110
110
  end
@@ -112,7 +112,7 @@ module ActiveModel
112
112
  end
113
113
 
114
114
  private
115
- # Override run_validations! to include callbacks.
115
+ # Overwrite run validations to include callbacks.
116
116
  def run_validations!
117
117
  _run_validation_callbacks { super }
118
118
  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
 
@@ -19,13 +19,13 @@ module ActiveModel
19
19
 
20
20
  private
21
21
  def setup!(klass)
22
- klass.attr_reader(*attributes.filter_map do |attribute|
22
+ klass.attr_reader(*attributes.map do |attribute|
23
23
  :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
24
- end)
24
+ end.compact)
25
25
 
26
- klass.attr_writer(*attributes.filter_map do |attribute|
26
+ klass.attr_writer(*attributes.map do |attribute|
27
27
  :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
28
- end)
28
+ end.compact)
29
29
  end
30
30
 
31
31
  def confirmation_value_equal?(record, attribute, value, confirmed)
@@ -71,7 +71,7 @@ module ActiveModel
71
71
  #
72
72
  # There is also a list of default options supported by every validator:
73
73
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
74
- # See ActiveModel::Validations::ClassMethods#validates for more information.
74
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
75
75
  def validates_confirmation_of(*attr_names)
76
76
  validates_with ConfirmationValidator, _merge_attributes(attr_names)
77
77
  end
@@ -29,8 +29,8 @@ module ActiveModel
29
29
  #
30
30
  # Configuration options:
31
31
  # * <tt>:in</tt> - An enumerable object of items that the value shouldn't
32
- # be part of. This can be supplied as a proc, lambda, or symbol which returns an
33
- # enumerable. If the enumerable is a numerical, time, or datetime range the test
32
+ # be part of. This can be supplied as a proc, lambda or symbol which returns an
33
+ # enumerable. If the enumerable is a numerical, time or datetime range the test
34
34
  # is performed with <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>. When
35
35
  # using a proc or lambda the instance under validation is passed as an argument.
36
36
  # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
@@ -40,7 +40,7 @@ module ActiveModel
40
40
  #
41
41
  # There is also a list of default options supported by every validator:
42
42
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
43
- # See ActiveModel::Validations::ClassMethods#validates for more information.
43
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
44
44
  def validates_exclusion_of(*attr_names)
45
45
  validates_with ExclusionValidator, _merge_attributes(attr_names)
46
46
  end
@@ -104,7 +104,7 @@ module ActiveModel
104
104
  #
105
105
  # There is also a list of default options supported by every validator:
106
106
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
107
- # See ActiveModel::Validations::ClassMethods#validates for more information.
107
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
108
108
  def validates_format_of(*attr_names)
109
109
  validates_with FormatValidator, _merge_attributes(attr_names)
110
110
  end
@@ -28,8 +28,8 @@ module ActiveModel
28
28
  #
29
29
  # Configuration options:
30
30
  # * <tt>:in</tt> - An enumerable object of available items. This can be
31
- # supplied as a proc, lambda, or symbol which returns an enumerable. If the
32
- # enumerable is a numerical, time, or datetime range the test is performed
31
+ # supplied as a proc, lambda or symbol which returns an enumerable. If the
32
+ # enumerable is a numerical, time or datetime range the test is performed
33
33
  # with <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>. When using
34
34
  # a proc or lambda the instance under validation is passed as an argument.
35
35
  # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
@@ -38,7 +38,7 @@ module ActiveModel
38
38
  #
39
39
  # There is also a list of default options supported by every validator:
40
40
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
41
- # See ActiveModel::Validations::ClassMethods#validates for more information.
41
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
42
42
  def validates_inclusion_of(*attr_names)
43
43
  validates_with InclusionValidator, _merge_attributes(attr_names)
44
44
  end
@@ -117,8 +117,8 @@ module ActiveModel
117
117
  # <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
118
118
  #
119
119
  # There is also a list of default options supported by every validator:
120
- # +:if+, +:unless+, +:on+, and +:strict+.
121
- # See ActiveModel::Validations::ClassMethods#validates for more information.
120
+ # +:if+, +:unless+, +:on+ and +:strict+.
121
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
122
122
  def validates_length_of(*attr_names)
123
123
  validates_with LengthValidator, _merge_attributes(attr_names)
124
124
  end
@@ -1,34 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_model/validations/comparability"
4
3
  require "bigdecimal/util"
5
4
 
6
5
  module ActiveModel
7
6
  module Validations
8
7
  class NumericalityValidator < EachValidator # :nodoc:
9
- include Comparability
8
+ CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
9
+ equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
10
+ odd: :odd?, even: :even?, other_than: :!= }.freeze
10
11
 
11
- RANGE_CHECKS = { in: :in? }
12
- NUMBER_CHECKS = { odd: :odd?, even: :even? }
13
-
14
- RESERVED_OPTIONS = COMPARE_CHECKS.keys + NUMBER_CHECKS.keys + RANGE_CHECKS.keys + [:only_integer]
12
+ RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
15
13
 
16
14
  INTEGER_REGEX = /\A[+-]?\d+\z/
17
15
 
18
16
  HEXADECIMAL_REGEX = /\A[+-]?0[xX]/
19
17
 
20
18
  def check_validity!
21
- options.slice(*COMPARE_CHECKS.keys).each do |option, value|
19
+ keys = CHECKS.keys - [:odd, :even]
20
+ options.slice(*keys).each do |option, value|
22
21
  unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
23
22
  raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
24
23
  end
25
24
  end
26
-
27
- options.slice(*RANGE_CHECKS.keys).each do |option, value|
28
- unless value.is_a?(Range)
29
- raise ArgumentError, ":#{option} must be a range"
30
- end
31
- end
32
25
  end
33
26
 
34
27
  def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
@@ -44,18 +37,23 @@ module ActiveModel
44
37
 
45
38
  value = parse_as_number(value, precision, scale)
46
39
 
47
- options.slice(*RESERVED_OPTIONS).each do |option, option_value|
48
- if NUMBER_CHECKS.include?(option)
49
- unless value.to_i.public_send(NUMBER_CHECKS[option])
40
+ options.slice(*CHECKS.keys).each do |option, option_value|
41
+ case option
42
+ when :odd, :even
43
+ unless value.to_i.public_send(CHECKS[option])
50
44
  record.errors.add(attr_name, option, **filtered_options(value))
51
45
  end
52
- elsif RANGE_CHECKS.include?(option)
53
- unless value.public_send(RANGE_CHECKS[option], option_value)
54
- record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
46
+ else
47
+ case option_value
48
+ when Proc
49
+ option_value = option_value.call(record)
50
+ when Symbol
51
+ option_value = record.send(option_value)
55
52
  end
56
- elsif COMPARE_CHECKS.include?(option)
57
- option_value = option_as_number(record, option_value, precision, scale)
58
- unless value.public_send(COMPARE_CHECKS[option], option_value)
53
+
54
+ option_value = parse_as_number(option_value, precision, scale)
55
+
56
+ unless value.public_send(CHECKS[option], option_value)
59
57
  record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
60
58
  end
61
59
  end
@@ -63,10 +61,6 @@ module ActiveModel
63
61
  end
64
62
 
65
63
  private
66
- def option_as_number(record, option_value, precision, scale)
67
- parse_as_number(option_value(record, option_value), precision, scale)
68
- end
69
-
70
64
  def parse_as_number(raw_value, precision, scale)
71
65
  if raw_value.is_a?(Float)
72
66
  parse_float(raw_value, precision, scale)
@@ -161,7 +155,7 @@ module ActiveModel
161
155
  # Configuration options:
162
156
  # * <tt>:message</tt> - A custom error message (default is: "is not a number").
163
157
  # * <tt>:only_integer</tt> - Specifies whether the value has to be an
164
- # integer (default is +false+).
158
+ # integer, e.g. an integral value (default is +false+).
165
159
  # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
166
160
  # +false+). Notice that for Integer and Float columns empty strings are
167
161
  # converted to +nil+.
@@ -179,11 +173,10 @@ module ActiveModel
179
173
  # supplied value.
180
174
  # * <tt>:odd</tt> - Specifies the value must be an odd number.
181
175
  # * <tt>:even</tt> - Specifies the value must be an even number.
182
- # * <tt>:in</tt> - Check that the value is within a range.
183
176
  #
184
177
  # There is also a list of default options supported by every validator:
185
178
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
186
- # See ActiveModel::Validations::ClassMethods#validates for more information.
179
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
187
180
  #
188
181
  # The following checks can also be supplied with a proc or a symbol which
189
182
  # corresponds to a method:
@@ -30,7 +30,7 @@ module ActiveModel
30
30
  #
31
31
  # There is also a list of default options supported by every validator:
32
32
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
33
- # See ActiveModel::Validations::ClassMethods#validates for more information.
33
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
34
34
  def validates_presence_of(*attr_names)
35
35
  validates_with PresenceValidator, _merge_attributes(attr_names)
36
36
  end
@@ -78,14 +78,14 @@ module ActiveModel
78
78
  # or an array of symbols. (e.g. <tt>on: :create</tt> or
79
79
  # <tt>on: :custom_validation_context</tt> or
80
80
  # <tt>on: [:create, :custom_validation_context]</tt>)
81
- # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
81
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
82
82
  # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
83
83
  # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
84
84
  # proc or string should return or evaluate to a +true+ or +false+ value.
85
- # * <tt>:unless</tt> - Specifies a method, proc, or string to call to determine
85
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
86
86
  # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
87
87
  # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
88
- # method, proc, or string should return or evaluate to a +true+ or
88
+ # method, proc or string should return or evaluate to a +true+ or
89
89
  # +false+ value.
90
90
  # * <tt>:allow_nil</tt> - Skip validation if the attribute is +nil+.
91
91
  # * <tt>:allow_blank</tt> - Skip validation if the attribute is blank.
@@ -51,16 +51,16 @@ module ActiveModel
51
51
  # or an array of symbols. (e.g. <tt>on: :create</tt> or
52
52
  # <tt>on: :custom_validation_context</tt> or
53
53
  # <tt>on: [:create, :custom_validation_context]</tt>)
54
- # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
54
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
55
55
  # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
56
56
  # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
57
- # The method, proc, or string should return or evaluate to a +true+ or
57
+ # The method, proc or string should return or evaluate to a +true+ or
58
58
  # +false+ value.
59
- # * <tt>:unless</tt> - Specifies a method, proc, or string to call to
59
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
60
60
  # determine if the validation should not occur
61
61
  # (e.g. <tt>unless: :skip_validation</tt>, or
62
62
  # <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>).
63
- # The method, proc, or string should return or evaluate to a +true+ or
63
+ # The method, proc or string should return or evaluate to a +true+ or
64
64
  # +false+ value.
65
65
  # * <tt>:strict</tt> - Specifies whether validation should be strict.
66
66
  # See <tt>ActiveModel::Validations#validates!</tt> for more information.