activemodel 7.0.4 → 6.1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +101 -103
- 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 +5 -5
- 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 +2 -25
- 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
|
@@ -36,9 +36,7 @@ module ActiveModel
|
|
36
36
|
#
|
37
37
|
# gem 'bcrypt', '~> 3.1.7'
|
38
38
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# ===== Using Active Record (which automatically includes ActiveModel::SecurePassword)
|
39
|
+
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
|
42
40
|
#
|
43
41
|
# # Schema: User(name:string, password_digest:string, recovery_password_digest:string)
|
44
42
|
# class User < ActiveRecord::Base
|
@@ -60,27 +58,6 @@ module ActiveModel
|
|
60
58
|
# user.authenticate_recovery_password('42password') # => user
|
61
59
|
# User.find_by(name: 'david')&.authenticate('notright') # => false
|
62
60
|
# User.find_by(name: 'david')&.authenticate('mUc3m00RsqyRe') # => user
|
63
|
-
#
|
64
|
-
# ===== Conditionally requiring a password
|
65
|
-
#
|
66
|
-
# class Account
|
67
|
-
# include ActiveModel::SecurePassword
|
68
|
-
#
|
69
|
-
# attr_accessor :is_guest, :password_digest
|
70
|
-
#
|
71
|
-
# has_secure_password
|
72
|
-
#
|
73
|
-
# def errors
|
74
|
-
# super.tap { |errors| errors.delete(:password, :blank) if is_guest }
|
75
|
-
# end
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# account = Account.new
|
79
|
-
# account.valid? # => false, password required
|
80
|
-
#
|
81
|
-
# account.is_guest = true
|
82
|
-
# account.valid? # => true
|
83
|
-
#
|
84
61
|
def has_secure_password(attribute = :password, validations: true)
|
85
62
|
# Load bcrypt gem only when has_secure_password is used.
|
86
63
|
# This is to avoid ActiveModel (and by extension the entire framework)
|
@@ -142,7 +119,7 @@ module ActiveModel
|
|
142
119
|
# user.authenticate_password('mUc3m00RsqyRe') # => user
|
143
120
|
define_method("authenticate_#{attribute}") do |unencrypted_password|
|
144
121
|
attribute_digest = public_send("#{attribute}_digest")
|
145
|
-
|
122
|
+
BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
|
146
123
|
end
|
147
124
|
|
148
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.
|