activemodel 5.2.5 → 6.0.4.6
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 +185 -71
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/active_model/attribute/user_provided_default.rb +1 -2
- data/lib/active_model/attribute.rb +3 -4
- data/lib/active_model/attribute_assignment.rb +1 -2
- data/lib/active_model/attribute_methods.rb +56 -15
- data/lib/active_model/attribute_mutation_tracker.rb +88 -34
- data/lib/active_model/attribute_set/builder.rb +1 -3
- data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
- data/lib/active_model/attribute_set.rb +2 -12
- data/lib/active_model/attributes.rb +60 -35
- data/lib/active_model/callbacks.rb +10 -8
- data/lib/active_model/conversion.rb +1 -1
- data/lib/active_model/dirty.rb +36 -99
- data/lib/active_model/errors.rb +105 -21
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/naming.rb +20 -5
- data/lib/active_model/railtie.rb +6 -0
- data/lib/active_model/secure_password.rb +47 -48
- data/lib/active_model/serialization.rb +0 -1
- data/lib/active_model/serializers/json.rb +10 -9
- data/lib/active_model/translation.rb +1 -1
- data/lib/active_model/type/big_integer.rb +0 -1
- data/lib/active_model/type/binary.rb +1 -1
- data/lib/active_model/type/boolean.rb +0 -1
- data/lib/active_model/type/date.rb +0 -5
- data/lib/active_model/type/date_time.rb +3 -8
- data/lib/active_model/type/decimal.rb +0 -1
- data/lib/active_model/type/float.rb +0 -3
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +4 -0
- data/lib/active_model/type/helpers/numeric.rb +9 -3
- data/lib/active_model/type/helpers/time_value.rb +17 -5
- data/lib/active_model/type/immutable_string.rb +0 -1
- data/lib/active_model/type/integer.rb +8 -20
- data/lib/active_model/type/registry.rb +11 -16
- data/lib/active_model/type/string.rb +2 -3
- data/lib/active_model/type/time.rb +1 -6
- data/lib/active_model/type/value.rb +0 -1
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +33 -26
- data/lib/active_model/validations/callbacks.rb +0 -1
- data/lib/active_model/validations/clusivity.rb +1 -2
- data/lib/active_model/validations/confirmation.rb +2 -2
- data/lib/active_model/validations/format.rb +1 -2
- data/lib/active_model/validations/inclusion.rb +1 -1
- data/lib/active_model/validations/length.rb +1 -1
- data/lib/active_model/validations/numericality.rb +5 -4
- data/lib/active_model/validations/validates.rb +2 -3
- data/lib/active_model/validations.rb +0 -3
- data/lib/active_model/validator.rb +1 -2
- data/lib/active_model.rb +1 -1
- metadata +15 -12
@@ -26,13 +26,13 @@ module ActiveModel
|
|
26
26
|
# user = User.find(1)
|
27
27
|
# user.as_json
|
28
28
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
29
|
-
# # "created_at" => "2006
|
29
|
+
# # "created_at" => "2006-08-01T17:27:133.000Z", "awesome" => true}
|
30
30
|
#
|
31
31
|
# ActiveRecord::Base.include_root_in_json = true
|
32
32
|
#
|
33
33
|
# user.as_json
|
34
34
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
35
|
-
# # "created_at" => "2006
|
35
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
36
36
|
#
|
37
37
|
# This behavior can also be achieved by setting the <tt>:root</tt> option
|
38
38
|
# to +true+ as in:
|
@@ -40,7 +40,7 @@ module ActiveModel
|
|
40
40
|
# user = User.find(1)
|
41
41
|
# user.as_json(root: true)
|
42
42
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
43
|
-
# # "created_at" => "2006
|
43
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
44
44
|
#
|
45
45
|
# Without any +options+, the returned Hash will include all the model's
|
46
46
|
# attributes.
|
@@ -48,7 +48,7 @@ module ActiveModel
|
|
48
48
|
# user = User.find(1)
|
49
49
|
# user.as_json
|
50
50
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
51
|
-
# # "created_at" => "2006
|
51
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true}
|
52
52
|
#
|
53
53
|
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
|
54
54
|
# the attributes included, and work similar to the +attributes+ method.
|
@@ -63,14 +63,14 @@ module ActiveModel
|
|
63
63
|
#
|
64
64
|
# user.as_json(methods: :permalink)
|
65
65
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
66
|
-
# # "created_at" => "2006
|
66
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
67
67
|
# # "permalink" => "1-konata-izumi" }
|
68
68
|
#
|
69
69
|
# To include associations use <tt>:include</tt>:
|
70
70
|
#
|
71
71
|
# user.as_json(include: :posts)
|
72
72
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
73
|
-
# # "created_at" => "2006
|
73
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
74
74
|
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
|
75
75
|
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
|
76
76
|
#
|
@@ -81,7 +81,7 @@ module ActiveModel
|
|
81
81
|
# only: :body } },
|
82
82
|
# only: :title } })
|
83
83
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
84
|
-
# # "created_at" => "2006
|
84
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
85
85
|
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
|
86
86
|
# # "title" => "Welcome to the weblog" },
|
87
87
|
# # { "comments" => [ { "body" => "Don't think too hard" } ],
|
@@ -93,11 +93,12 @@ module ActiveModel
|
|
93
93
|
include_root_in_json
|
94
94
|
end
|
95
95
|
|
96
|
+
hash = serializable_hash(options).as_json
|
96
97
|
if root
|
97
98
|
root = model_name.element if root == true
|
98
|
-
{ root =>
|
99
|
+
{ root => hash }
|
99
100
|
else
|
100
|
-
|
101
|
+
hash
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
@@ -10,16 +10,11 @@ module ActiveModel
|
|
10
10
|
:date
|
11
11
|
end
|
12
12
|
|
13
|
-
def serialize(value)
|
14
|
-
cast(value)
|
15
|
-
end
|
16
|
-
|
17
13
|
def type_cast_for_schema(value)
|
18
14
|
value.to_s(:db).inspect
|
19
15
|
end
|
20
16
|
|
21
17
|
private
|
22
|
-
|
23
18
|
def cast_value(value)
|
24
19
|
if value.is_a?(::String)
|
25
20
|
return if value.empty?
|
@@ -13,12 +13,7 @@ module ActiveModel
|
|
13
13
|
:datetime
|
14
14
|
end
|
15
15
|
|
16
|
-
def serialize(value)
|
17
|
-
super(cast(value))
|
18
|
-
end
|
19
|
-
|
20
16
|
private
|
21
|
-
|
22
17
|
def cast_value(value)
|
23
18
|
return apply_seconds_precision(value) unless value.is_a?(::String)
|
24
19
|
return if value.empty?
|
@@ -40,9 +35,9 @@ module ActiveModel
|
|
40
35
|
end
|
41
36
|
|
42
37
|
def value_from_multiparameter_assignment(values_hash)
|
43
|
-
|
44
|
-
if
|
45
|
-
raise ArgumentError,
|
38
|
+
missing_parameters = (1..3).select { |key| !values_hash.key?(key) }
|
39
|
+
if missing_parameters.any?
|
40
|
+
raise ArgumentError, "Provided hash #{values_hash} doesn't contain necessary keys: #{missing_parameters}"
|
46
41
|
end
|
47
42
|
super
|
48
43
|
end
|
@@ -5,6 +5,10 @@ module ActiveModel
|
|
5
5
|
module Helpers # :nodoc: all
|
6
6
|
class AcceptsMultiparameterTime < Module
|
7
7
|
def initialize(defaults: {})
|
8
|
+
define_method(:serialize) do |value|
|
9
|
+
super(cast(value))
|
10
|
+
end
|
11
|
+
|
8
12
|
define_method(:cast) do |value|
|
9
13
|
if value.is_a?(Hash)
|
10
14
|
value_from_multiparameter_assignment(value)
|
@@ -4,6 +4,10 @@ module ActiveModel
|
|
4
4
|
module Type
|
5
5
|
module Helpers # :nodoc: all
|
6
6
|
module Numeric
|
7
|
+
def serialize(value)
|
8
|
+
cast(value)
|
9
|
+
end
|
10
|
+
|
7
11
|
def cast(value)
|
8
12
|
value = \
|
9
13
|
case value
|
@@ -20,17 +24,19 @@ module ActiveModel
|
|
20
24
|
end
|
21
25
|
|
22
26
|
private
|
23
|
-
|
24
27
|
def number_to_non_number?(old_value, new_value_before_type_cast)
|
25
|
-
old_value != nil && non_numeric_string?(new_value_before_type_cast)
|
28
|
+
old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
|
26
29
|
end
|
27
30
|
|
28
31
|
def non_numeric_string?(value)
|
29
32
|
# 'wibble'.to_i will give zero, we want to make sure
|
30
33
|
# that we aren't marking int zero to string zero as
|
31
34
|
# changed.
|
32
|
-
|
35
|
+
!NUMERIC_REGEX.match?(value)
|
33
36
|
end
|
37
|
+
|
38
|
+
NUMERIC_REGEX = /\A\s*[+-]?\d/
|
39
|
+
private_constant :NUMERIC_REGEX
|
34
40
|
end
|
35
41
|
end
|
36
42
|
end
|
@@ -22,10 +22,17 @@ module ActiveModel
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def apply_seconds_precision(value)
|
25
|
-
return value unless precision && value.respond_to?(:
|
26
|
-
|
25
|
+
return value unless precision && value.respond_to?(:nsec)
|
26
|
+
|
27
|
+
number_of_insignificant_digits = 9 - precision
|
27
28
|
round_power = 10**number_of_insignificant_digits
|
28
|
-
|
29
|
+
rounded_off_nsec = value.nsec % round_power
|
30
|
+
|
31
|
+
if rounded_off_nsec > 0
|
32
|
+
value.change(nsec: value.nsec - rounded_off_nsec)
|
33
|
+
else
|
34
|
+
value
|
35
|
+
end
|
29
36
|
end
|
30
37
|
|
31
38
|
def type_cast_for_schema(value)
|
@@ -37,7 +44,6 @@ module ActiveModel
|
|
37
44
|
end
|
38
45
|
|
39
46
|
private
|
40
|
-
|
41
47
|
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
|
42
48
|
# Treat 0000-00-00 00:00:00 as nil.
|
43
49
|
return if year.nil? || (year == 0 && mon == 0 && mday == 0)
|
@@ -58,7 +64,13 @@ module ActiveModel
|
|
58
64
|
# Doesn't handle time zones.
|
59
65
|
def fast_string_to_time(string)
|
60
66
|
if string =~ ISO_DATETIME
|
61
|
-
|
67
|
+
microsec_part = $7
|
68
|
+
if microsec_part && microsec_part.start_with?(".") && microsec_part.length == 7
|
69
|
+
microsec_part[0] = ""
|
70
|
+
microsec = microsec_part.to_i
|
71
|
+
else
|
72
|
+
microsec = (microsec_part.to_r * 1_000_000).to_i
|
73
|
+
end
|
62
74
|
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
63
75
|
end
|
64
76
|
end
|
@@ -9,7 +9,7 @@ module ActiveModel
|
|
9
9
|
# 4 bytes means an integer as opposed to smallint etc.
|
10
10
|
DEFAULT_LIMIT = 4
|
11
11
|
|
12
|
-
def initialize(
|
12
|
+
def initialize(*, **)
|
13
13
|
super
|
14
14
|
@range = min_value...max_value
|
15
15
|
end
|
@@ -19,39 +19,27 @@ module ActiveModel
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def deserialize(value)
|
22
|
-
return if value.
|
22
|
+
return if value.blank?
|
23
23
|
value.to_i
|
24
24
|
end
|
25
25
|
|
26
26
|
def serialize(value)
|
27
|
-
|
28
|
-
|
29
|
-
ensure_in_range(result)
|
30
|
-
end
|
31
|
-
result
|
27
|
+
return if value.is_a?(::String) && non_numeric_string?(value)
|
28
|
+
ensure_in_range(super)
|
32
29
|
end
|
33
30
|
|
34
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
35
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
36
|
-
protected
|
37
|
-
|
38
|
-
attr_reader :range
|
39
|
-
|
40
31
|
private
|
32
|
+
attr_reader :range
|
41
33
|
|
42
34
|
def cast_value(value)
|
43
|
-
|
44
|
-
when true then 1
|
45
|
-
when false then 0
|
46
|
-
else
|
47
|
-
value.to_i rescue nil
|
48
|
-
end
|
35
|
+
value.to_i rescue nil
|
49
36
|
end
|
50
37
|
|
51
38
|
def ensure_in_range(value)
|
52
|
-
|
39
|
+
if value && !range.cover?(value)
|
53
40
|
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
54
41
|
end
|
42
|
+
value
|
55
43
|
end
|
56
44
|
|
57
45
|
def max_value
|
@@ -9,34 +9,32 @@ module ActiveModel
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def register(type_name, klass = nil, **options, &block)
|
12
|
-
|
12
|
+
unless block_given?
|
13
|
+
block = proc { |_, *args| klass.new(*args) }
|
14
|
+
block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
|
15
|
+
end
|
13
16
|
registrations << registration_klass.new(type_name, block, **options)
|
14
17
|
end
|
15
18
|
|
16
|
-
def lookup(symbol, *args)
|
17
|
-
registration = find_registration(symbol, *args)
|
19
|
+
def lookup(symbol, *args, **kwargs)
|
20
|
+
registration = find_registration(symbol, *args, **kwargs)
|
18
21
|
|
19
22
|
if registration
|
20
|
-
registration.call(self, symbol, *args)
|
23
|
+
registration.call(self, symbol, *args, **kwargs)
|
21
24
|
else
|
22
25
|
raise ArgumentError, "Unknown type #{symbol.inspect}"
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
27
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
28
|
-
protected
|
29
|
-
|
30
|
-
attr_reader :registrations
|
31
|
-
|
32
29
|
private
|
30
|
+
attr_reader :registrations
|
33
31
|
|
34
32
|
def registration_klass
|
35
33
|
Registration
|
36
34
|
end
|
37
35
|
|
38
|
-
def find_registration(symbol, *args)
|
39
|
-
registrations.find { |r| r.matches?(symbol, *args) }
|
36
|
+
def find_registration(symbol, *args, **kwargs)
|
37
|
+
registrations.find { |r| r.matches?(symbol, *args, **kwargs) }
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
@@ -59,10 +57,7 @@ module ActiveModel
|
|
59
57
|
type_name == name
|
60
58
|
end
|
61
59
|
|
62
|
-
|
63
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
64
|
-
protected
|
65
|
-
|
60
|
+
private
|
66
61
|
attr_reader :name, :block
|
67
62
|
end
|
68
63
|
end
|
@@ -12,12 +12,11 @@ module ActiveModel
|
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
|
-
|
16
15
|
def cast_value(value)
|
17
16
|
case value
|
18
17
|
when ::String then ::String.new(value)
|
19
|
-
when true then "t"
|
20
|
-
when false then "f"
|
18
|
+
when true then "t"
|
19
|
+
when false then "f"
|
21
20
|
else value.to_s
|
22
21
|
end
|
23
22
|
end
|
@@ -6,17 +6,13 @@ module ActiveModel
|
|
6
6
|
include Helpers::Timezone
|
7
7
|
include Helpers::TimeValue
|
8
8
|
include Helpers::AcceptsMultiparameterTime.new(
|
9
|
-
defaults: { 1 =>
|
9
|
+
defaults: { 1 => 2000, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
|
10
10
|
)
|
11
11
|
|
12
12
|
def type
|
13
13
|
:time
|
14
14
|
end
|
15
15
|
|
16
|
-
def serialize(value)
|
17
|
-
super(cast(value))
|
18
|
-
end
|
19
|
-
|
20
16
|
def user_input_in_time_zone(value)
|
21
17
|
return unless value.present?
|
22
18
|
|
@@ -33,7 +29,6 @@ module ActiveModel
|
|
33
29
|
end
|
34
30
|
|
35
31
|
private
|
36
|
-
|
37
32
|
def cast_value(value)
|
38
33
|
return apply_seconds_precision(value) unless value.is_a?(::String)
|
39
34
|
return if value.empty?
|
@@ -11,7 +11,7 @@ module ActiveModel
|
|
11
11
|
|
12
12
|
module HelperMethods
|
13
13
|
# Validates that the specified attributes are blank (as defined by
|
14
|
-
# Object#
|
14
|
+
# Object#present?). Happens by default on save.
|
15
15
|
#
|
16
16
|
# class Person < ActiveRecord::Base
|
17
17
|
# validates_absence_of :first_name
|
@@ -15,9 +15,9 @@ module ActiveModel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
|
-
|
19
18
|
def setup!(klass)
|
20
|
-
|
19
|
+
define_attributes = LazilyDefineAttributes.new(attributes)
|
20
|
+
klass.include(define_attributes) unless klass.included_modules.include?(define_attributes)
|
21
21
|
end
|
22
22
|
|
23
23
|
def acceptable_option?(value)
|
@@ -25,50 +25,57 @@ module ActiveModel
|
|
25
25
|
end
|
26
26
|
|
27
27
|
class LazilyDefineAttributes < Module
|
28
|
-
def initialize(
|
28
|
+
def initialize(attributes)
|
29
|
+
@attributes = attributes.map(&:to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def included(klass)
|
33
|
+
@lock = Mutex.new
|
34
|
+
mod = self
|
35
|
+
|
29
36
|
define_method(:respond_to_missing?) do |method_name, include_private = false|
|
30
|
-
|
37
|
+
mod.define_on(klass)
|
38
|
+
super(method_name, include_private) || mod.matches?(method_name)
|
31
39
|
end
|
32
40
|
|
33
41
|
define_method(:method_missing) do |method_name, *args, &block|
|
34
|
-
|
35
|
-
|
42
|
+
mod.define_on(klass)
|
43
|
+
if mod.matches?(method_name)
|
36
44
|
send(method_name, *args, &block)
|
37
45
|
else
|
38
46
|
super(method_name, *args, &block)
|
39
47
|
end
|
40
48
|
end
|
41
49
|
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class AttributeDefinition
|
45
|
-
def initialize(attributes)
|
46
|
-
@attributes = attributes.map(&:to_s)
|
47
|
-
end
|
48
50
|
|
49
51
|
def matches?(method_name)
|
50
|
-
attr_name =
|
51
|
-
attributes.
|
52
|
+
attr_name = method_name.to_s.chomp("=")
|
53
|
+
attributes.any? { |name| name == attr_name }
|
52
54
|
end
|
53
55
|
|
54
56
|
def define_on(klass)
|
55
|
-
|
56
|
-
|
57
|
-
klass.send(:attr_reader, *attr_readers)
|
58
|
-
klass.send(:attr_writer, *attr_writers)
|
59
|
-
end
|
57
|
+
@lock&.synchronize do
|
58
|
+
return unless @lock
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
protected
|
60
|
+
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
61
|
+
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
64
62
|
|
65
|
-
|
63
|
+
attr_reader(*attr_readers)
|
64
|
+
attr_writer(*attr_writers)
|
66
65
|
|
67
|
-
|
66
|
+
remove_method :respond_to_missing?
|
67
|
+
remove_method :method_missing
|
68
68
|
|
69
|
-
|
70
|
-
method_name.to_s.chomp("=")
|
69
|
+
@lock = nil
|
71
70
|
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def ==(other)
|
74
|
+
self.class == other.class && attributes == other.attributes
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
attr_reader :attributes
|
72
79
|
end
|
73
80
|
end
|
74
81
|
|
@@ -15,7 +15,6 @@ module ActiveModel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
|
-
|
19
18
|
def include?(record, value)
|
20
19
|
members = if delimiter.respond_to?(:call)
|
21
20
|
delimiter.call(record)
|
@@ -32,7 +31,7 @@ module ActiveModel
|
|
32
31
|
@delimiter ||= options[:in] || options[:within]
|
33
32
|
end
|
34
33
|
|
35
|
-
#
|
34
|
+
# After Ruby 2.2, <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
|
36
35
|
# possible values in the range for equality, which is slower but more accurate.
|
37
36
|
# <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
|
38
37
|
# endpoints, which is fast but is only accurate on Numeric, Time, Date,
|
@@ -19,11 +19,11 @@ module ActiveModel
|
|
19
19
|
|
20
20
|
private
|
21
21
|
def setup!(klass)
|
22
|
-
klass.
|
22
|
+
klass.attr_reader(*attributes.map do |attribute|
|
23
23
|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
|
24
24
|
end.compact)
|
25
25
|
|
26
|
-
klass.
|
26
|
+
klass.attr_writer(*attributes.map do |attribute|
|
27
27
|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
|
28
28
|
end.compact)
|
29
29
|
end
|
@@ -6,7 +6,7 @@ module ActiveModel
|
|
6
6
|
def validate_each(record, attribute, value)
|
7
7
|
if options[:with]
|
8
8
|
regexp = option_call(record, :with)
|
9
|
-
record_error(record, attribute, :with, value) if value.to_s
|
9
|
+
record_error(record, attribute, :with, value) if !value.to_s&.match?(regexp)
|
10
10
|
elsif options[:without]
|
11
11
|
regexp = option_call(record, :without)
|
12
12
|
record_error(record, attribute, :without, value) if regexp.match?(value.to_s)
|
@@ -23,7 +23,6 @@ module ActiveModel
|
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
|
-
|
27
26
|
def option_call(record, name)
|
28
27
|
option = options[name]
|
29
28
|
option.respond_to?(:call) ? option.call(record) : option
|
@@ -19,7 +19,7 @@ module ActiveModel
|
|
19
19
|
# particular enumerable object.
|
20
20
|
#
|
21
21
|
# class Person < ActiveRecord::Base
|
22
|
-
# validates_inclusion_of :
|
22
|
+
# validates_inclusion_of :role, in: %w( admin contributor )
|
23
23
|
# validates_inclusion_of :age, in: 0..99
|
24
24
|
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
|
25
25
|
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
|
@@ -32,7 +32,7 @@ module ActiveModel
|
|
32
32
|
value = options[key]
|
33
33
|
|
34
34
|
unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY || value.is_a?(Symbol) || value.is_a?(Proc)
|
35
|
-
raise ArgumentError, ":#{key} must be a
|
35
|
+
raise ArgumentError, ":#{key} must be a non-negative Integer, Infinity, Symbol, or Proc"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -13,6 +13,8 @@ module ActiveModel
|
|
13
13
|
|
14
14
|
INTEGER_REGEX = /\A[+-]?\d+\z/
|
15
15
|
|
16
|
+
HEXADECIMAL_REGEX = /\A[+-]?0[xX]/
|
17
|
+
|
16
18
|
def check_validity!
|
17
19
|
keys = CHECKS.keys - [:odd, :even]
|
18
20
|
options.slice(*keys).each do |option, value|
|
@@ -79,7 +81,6 @@ module ActiveModel
|
|
79
81
|
end
|
80
82
|
|
81
83
|
private
|
82
|
-
|
83
84
|
def is_number?(raw_value)
|
84
85
|
!parse_as_number(raw_value).nil?
|
85
86
|
rescue ArgumentError, TypeError
|
@@ -88,7 +89,7 @@ module ActiveModel
|
|
88
89
|
|
89
90
|
def parse_as_number(raw_value)
|
90
91
|
if raw_value.is_a?(Float)
|
91
|
-
raw_value.to_d
|
92
|
+
raw_value.to_d(Float::DIG)
|
92
93
|
elsif raw_value.is_a?(Numeric)
|
93
94
|
raw_value
|
94
95
|
elsif is_integer?(raw_value)
|
@@ -99,11 +100,11 @@ module ActiveModel
|
|
99
100
|
end
|
100
101
|
|
101
102
|
def is_integer?(raw_value)
|
102
|
-
INTEGER_REGEX
|
103
|
+
INTEGER_REGEX.match?(raw_value.to_s)
|
103
104
|
end
|
104
105
|
|
105
106
|
def is_hexadecimal_literal?(raw_value)
|
106
|
-
|
107
|
+
HEXADECIMAL_REGEX.match?(raw_value.to_s)
|
107
108
|
end
|
108
109
|
|
109
110
|
def filtered_options(value)
|