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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +103 -84
- data/MIT-LICENSE +0 -1
- data/README.rdoc +3 -3
- data/lib/active_model/attribute.rb +0 -4
- data/lib/active_model/attribute_methods.rb +82 -67
- data/lib/active_model/attribute_set/builder.rb +10 -1
- data/lib/active_model/attribute_set.rb +1 -4
- data/lib/active_model/attributes.rb +12 -15
- data/lib/active_model/callbacks.rb +3 -3
- data/lib/active_model/conversion.rb +2 -2
- data/lib/active_model/dirty.rb +4 -5
- data/lib/active_model/error.rb +2 -2
- data/lib/active_model/errors.rb +248 -55
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/locale/en.yml +0 -1
- data/lib/active_model/model.rb +59 -6
- data/lib/active_model/naming.rb +8 -15
- data/lib/active_model/secure_password.rb +1 -1
- data/lib/active_model/serialization.rb +2 -6
- data/lib/active_model/translation.rb +2 -2
- data/lib/active_model/type/date.rb +1 -1
- data/lib/active_model/type/helpers/numeric.rb +1 -9
- data/lib/active_model/type/helpers/time_value.rb +3 -3
- data/lib/active_model/type/integer.rb +1 -4
- data/lib/active_model/type/registry.rb +38 -8
- data/lib/active_model/type/time.rb +1 -1
- data/lib/active_model/type.rb +6 -6
- data/lib/active_model/validations/absence.rb +2 -2
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/callbacks.rb +1 -1
- data/lib/active_model/validations/clusivity.rb +1 -1
- data/lib/active_model/validations/confirmation.rb +5 -5
- data/lib/active_model/validations/exclusion.rb +3 -3
- data/lib/active_model/validations/format.rb +1 -1
- data/lib/active_model/validations/inclusion.rb +3 -3
- data/lib/active_model/validations/length.rb +2 -2
- data/lib/active_model/validations/numericality.rb +22 -29
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/validates.rb +3 -3
- data/lib/active_model/validations/with.rb +4 -4
- data/lib/active_model/validations.rb +12 -12
- data/lib/active_model/validator.rb +5 -5
- data/lib/active_model/version.rb +1 -1
- data/lib/active_model.rb +0 -1
- metadata +9 -12
- data/lib/active_model/api.rb +0 -99
- data/lib/active_model/validations/comparability.rb +0 -29
- data/lib/active_model/validations/comparison.rb +0 -82
data/lib/active_model/naming.rb
CHANGED
@@ -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
|
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
|
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
|
183
|
-
@singular_route_key = ActiveSupport::Inflector.singularize(@route_key
|
184
|
-
@route_key << "_index" if @
|
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)
|
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
|
-
|
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)
|
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
|
-
|
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 =
|
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 = {})
|
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
|
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.
|
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
|
@@ -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
|
-
|
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.
|
39
|
+
value.to_s(:db).inspect
|
40
40
|
end
|
41
41
|
|
42
42
|
def user_input_in_time_zone(value)
|
@@ -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
|
6
|
+
class Registry
|
6
7
|
def initialize
|
7
|
-
@registrations =
|
8
|
+
@registrations = []
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
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
|
21
|
+
registrations << registration_klass.new(type_name, block, **options)
|
21
22
|
end
|
22
23
|
|
23
24
|
def lookup(symbol, *args)
|
24
|
-
registration =
|
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
|
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)
|
data/lib/active_model/type.rb
CHANGED
@@ -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
|
28
|
-
|
29
|
-
|
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(
|
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
|
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
|
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
|
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
|
@@ -4,7 +4,7 @@ require "active_support/core_ext/range"
|
|
4
4
|
|
5
5
|
module ActiveModel
|
6
6
|
module Validations
|
7
|
-
module Clusivity
|
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.
|
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.
|
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
|
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
|
33
|
-
# enumerable. If the enumerable is a numerical, time
|
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
|
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
|
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
|
32
|
-
# enumerable is a numerical, time
|
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
|
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
|
121
|
-
# See ActiveModel::Validations
|
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
|
-
|
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
|
-
|
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
|
-
|
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(*
|
48
|
-
|
49
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
57
|
-
option_value =
|
58
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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.
|