activemodel 5.2.6 → 6.1.4
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 +58 -109
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -4
- data/lib/active_model.rb +2 -1
- data/lib/active_model/attribute.rb +21 -21
- data/lib/active_model/attribute/user_provided_default.rb +1 -2
- data/lib/active_model/attribute_assignment.rb +4 -6
- data/lib/active_model/attribute_methods.rb +117 -40
- data/lib/active_model/attribute_mutation_tracker.rb +90 -33
- data/lib/active_model/attribute_set.rb +20 -28
- data/lib/active_model/attribute_set/builder.rb +81 -16
- data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
- data/lib/active_model/attributes.rb +65 -44
- data/lib/active_model/callbacks.rb +11 -9
- data/lib/active_model/conversion.rb +1 -1
- data/lib/active_model/dirty.rb +51 -101
- data/lib/active_model/error.rb +207 -0
- data/lib/active_model/errors.rb +347 -155
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/lint.rb +1 -1
- data/lib/active_model/naming.rb +22 -7
- data/lib/active_model/nested_error.rb +22 -0
- data/lib/active_model/railtie.rb +6 -0
- data/lib/active_model/secure_password.rb +54 -55
- data/lib/active_model/serialization.rb +9 -7
- data/lib/active_model/serializers/json.rb +17 -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 +2 -3
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +14 -6
- data/lib/active_model/type/helpers/numeric.rb +17 -6
- data/lib/active_model/type/helpers/time_value.rb +37 -15
- data/lib/active_model/type/helpers/timezone.rb +1 -1
- data/lib/active_model/type/immutable_string.rb +14 -11
- data/lib/active_model/type/integer.rb +15 -18
- data/lib/active_model/type/registry.rb +16 -16
- data/lib/active_model/type/string.rb +12 -3
- data/lib/active_model/type/time.rb +1 -6
- data/lib/active_model/type/value.rb +9 -2
- data/lib/active_model/validations.rb +6 -9
- data/lib/active_model/validations/absence.rb +2 -2
- data/lib/active_model/validations/acceptance.rb +34 -27
- data/lib/active_model/validations/callbacks.rb +15 -16
- data/lib/active_model/validations/clusivity.rb +6 -3
- data/lib/active_model/validations/confirmation.rb +4 -4
- data/lib/active_model/validations/exclusion.rb +1 -1
- data/lib/active_model/validations/format.rb +2 -3
- data/lib/active_model/validations/inclusion.rb +2 -2
- data/lib/active_model/validations/length.rb +3 -3
- data/lib/active_model/validations/numericality.rb +58 -44
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/validates.rb +7 -6
- data/lib/active_model/validator.rb +8 -3
- metadata +14 -9
@@ -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
|
-
|
45
|
-
raise ArgumentError,
|
38
|
+
missing_parameters = [1, 2, 3].delete_if { |key| values_hash.key?(key) }
|
39
|
+
unless missing_parameters.empty?
|
40
|
+
raise ArgumentError, "Provided hash #{values_hash} doesn't contain necessary keys: #{missing_parameters}"
|
46
41
|
end
|
47
42
|
super
|
48
43
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
|
3
5
|
module ActiveModel
|
4
6
|
module Type
|
5
7
|
class Float < Value # :nodoc:
|
@@ -18,10 +20,7 @@ module ActiveModel
|
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
alias serialize cast
|
22
|
-
|
23
23
|
private
|
24
|
-
|
25
24
|
def cast_value(value)
|
26
25
|
case value
|
27
26
|
when ::Float then value
|
@@ -4,8 +4,12 @@ module ActiveModel
|
|
4
4
|
module Type
|
5
5
|
module Helpers # :nodoc: all
|
6
6
|
class AcceptsMultiparameterTime < Module
|
7
|
-
|
8
|
-
|
7
|
+
module InstanceMethods
|
8
|
+
def serialize(value)
|
9
|
+
super(cast(value))
|
10
|
+
end
|
11
|
+
|
12
|
+
def cast(value)
|
9
13
|
if value.is_a?(Hash)
|
10
14
|
value_from_multiparameter_assignment(value)
|
11
15
|
else
|
@@ -13,7 +17,7 @@ module ActiveModel
|
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
16
|
-
|
20
|
+
def assert_valid_value(value)
|
17
21
|
if value.is_a?(Hash)
|
18
22
|
value_from_multiparameter_assignment(value)
|
19
23
|
else
|
@@ -21,17 +25,21 @@ module ActiveModel
|
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
24
|
-
|
28
|
+
def value_constructed_by_mass_assignment?(value)
|
25
29
|
value.is_a?(Hash)
|
26
30
|
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(defaults: {})
|
34
|
+
include InstanceMethods
|
27
35
|
|
28
36
|
define_method(:value_from_multiparameter_assignment) do |values_hash|
|
29
37
|
defaults.each do |k, v|
|
30
38
|
values_hash[k] ||= v
|
31
39
|
end
|
32
40
|
return unless values_hash[1] && values_hash[2] && values_hash[3]
|
33
|
-
values = values_hash.sort.map(&:last)
|
34
|
-
::Time.
|
41
|
+
values = values_hash.sort.map!(&:last)
|
42
|
+
::Time.public_send(default_timezone, *values)
|
35
43
|
end
|
36
44
|
private :value_from_multiparameter_assignment
|
37
45
|
end
|
@@ -4,14 +4,23 @@ 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
|
-
value
|
12
|
+
# Checks whether the value is numeric. Spaceship operator
|
13
|
+
# will return nil if value is not numeric.
|
14
|
+
value = if value <=> 0
|
15
|
+
value
|
16
|
+
else
|
9
17
|
case value
|
10
18
|
when true then 1
|
11
19
|
when false then 0
|
12
|
-
|
13
|
-
else value
|
20
|
+
else value.presence
|
14
21
|
end
|
22
|
+
end
|
23
|
+
|
15
24
|
super(value)
|
16
25
|
end
|
17
26
|
|
@@ -20,17 +29,19 @@ module ActiveModel
|
|
20
29
|
end
|
21
30
|
|
22
31
|
private
|
23
|
-
|
24
32
|
def number_to_non_number?(old_value, new_value_before_type_cast)
|
25
|
-
old_value != nil && non_numeric_string?(new_value_before_type_cast)
|
33
|
+
old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
|
26
34
|
end
|
27
35
|
|
28
36
|
def non_numeric_string?(value)
|
29
37
|
# 'wibble'.to_i will give zero, we want to make sure
|
30
38
|
# that we aren't marking int zero to string zero as
|
31
39
|
# changed.
|
32
|
-
|
40
|
+
!NUMERIC_REGEX.match?(value)
|
33
41
|
end
|
42
|
+
|
43
|
+
NUMERIC_REGEX = /\A\s*[+-]?\d/
|
44
|
+
private_constant :NUMERIC_REGEX
|
34
45
|
end
|
35
46
|
end
|
36
47
|
end
|
@@ -11,10 +11,10 @@ module ActiveModel
|
|
11
11
|
value = apply_seconds_precision(value)
|
12
12
|
|
13
13
|
if value.acts_like?(:time)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
value = value.
|
14
|
+
if is_utc?
|
15
|
+
value = value.getutc if value.respond_to?(:getutc) && !value.utc?
|
16
|
+
else
|
17
|
+
value = value.getlocal if value.respond_to?(:getlocal)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -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)
|
@@ -46,21 +52,37 @@ module ActiveModel
|
|
46
52
|
time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
47
53
|
return unless time
|
48
54
|
|
49
|
-
time -= offset
|
55
|
+
time -= offset unless offset == 0
|
50
56
|
is_utc? ? time : time.getlocal
|
57
|
+
elsif is_utc?
|
58
|
+
::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
51
59
|
else
|
52
|
-
::Time.
|
60
|
+
::Time.local(year, mon, mday, hour, min, sec, microsec) rescue nil
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
56
|
-
ISO_DATETIME =
|
64
|
+
ISO_DATETIME = /
|
65
|
+
\A
|
66
|
+
(\d{4})-(\d\d)-(\d\d)(?:T|\s) # 2020-06-20T
|
67
|
+
(\d\d):(\d\d):(\d\d)(?:\.(\d{1,6})\d*)? # 10:20:30.123456
|
68
|
+
(?:(Z(?=\z)|[+-]\d\d)(?::?(\d\d))?)? # +09:00
|
69
|
+
\z
|
70
|
+
/x
|
57
71
|
|
58
|
-
# Doesn't handle time zones.
|
59
72
|
def fast_string_to_time(string)
|
60
|
-
|
61
|
-
|
62
|
-
|
73
|
+
return unless ISO_DATETIME =~ string
|
74
|
+
|
75
|
+
usec = $7.to_i
|
76
|
+
usec_len = $7&.length
|
77
|
+
if usec_len&.< 6
|
78
|
+
usec *= 10**(6 - usec_len)
|
63
79
|
end
|
80
|
+
|
81
|
+
if $8
|
82
|
+
offset = $8 == "Z" ? 0 : $8.to_i * 3600 + $9.to_i * 60
|
83
|
+
end
|
84
|
+
|
85
|
+
new_time($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, usec, offset)
|
64
86
|
end
|
65
87
|
end
|
66
88
|
end
|
@@ -3,29 +3,32 @@
|
|
3
3
|
module ActiveModel
|
4
4
|
module Type
|
5
5
|
class ImmutableString < Value # :nodoc:
|
6
|
+
def initialize(**args)
|
7
|
+
@true = -(args.delete(:true)&.to_s || "t")
|
8
|
+
@false = -(args.delete(:false)&.to_s || "f")
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
6
12
|
def type
|
7
13
|
:string
|
8
14
|
end
|
9
15
|
|
10
16
|
def serialize(value)
|
11
17
|
case value
|
12
|
-
when ::Numeric, ActiveSupport::Duration then value.to_s
|
13
|
-
when true then
|
14
|
-
when false then
|
18
|
+
when ::Numeric, ::Symbol, ActiveSupport::Duration then value.to_s
|
19
|
+
when true then @true
|
20
|
+
when false then @false
|
15
21
|
else super
|
16
22
|
end
|
17
23
|
end
|
18
24
|
|
19
25
|
private
|
20
|
-
|
21
26
|
def cast_value(value)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
result.freeze
|
27
|
+
case value
|
28
|
+
when true then @true
|
29
|
+
when false then @false
|
30
|
+
else value.to_s.freeze
|
31
|
+
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
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,36 @@ 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
|
-
|
35
|
-
|
36
|
-
|
31
|
+
def serializable?(value)
|
32
|
+
cast_value = cast(value)
|
33
|
+
in_range?(cast_value) && super
|
34
|
+
end
|
37
35
|
|
36
|
+
private
|
38
37
|
attr_reader :range
|
39
38
|
|
40
|
-
|
39
|
+
def in_range?(value)
|
40
|
+
!value || range.member?(value)
|
41
|
+
end
|
41
42
|
|
42
43
|
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
|
44
|
+
value.to_i rescue nil
|
49
45
|
end
|
50
46
|
|
51
47
|
def ensure_in_range(value)
|
52
|
-
unless
|
48
|
+
unless in_range?(value)
|
53
49
|
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
54
50
|
end
|
51
|
+
value
|
55
52
|
end
|
56
53
|
|
57
54
|
def max_value
|
@@ -8,35 +8,38 @@ module ActiveModel
|
|
8
8
|
@registrations = []
|
9
9
|
end
|
10
10
|
|
11
|
+
def initialize_dup(other)
|
12
|
+
@registrations = @registrations.dup
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
11
16
|
def register(type_name, klass = nil, **options, &block)
|
12
|
-
|
17
|
+
unless block_given?
|
18
|
+
block = proc { |_, *args| klass.new(*args) }
|
19
|
+
block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
|
20
|
+
end
|
13
21
|
registrations << registration_klass.new(type_name, block, **options)
|
14
22
|
end
|
15
23
|
|
16
|
-
def lookup(symbol, *args)
|
17
|
-
registration = find_registration(symbol, *args)
|
24
|
+
def lookup(symbol, *args, **kwargs)
|
25
|
+
registration = find_registration(symbol, *args, **kwargs)
|
18
26
|
|
19
27
|
if registration
|
20
|
-
registration.call(self, symbol, *args)
|
28
|
+
registration.call(self, symbol, *args, **kwargs)
|
21
29
|
else
|
22
30
|
raise ArgumentError, "Unknown type #{symbol.inspect}"
|
23
31
|
end
|
24
32
|
end
|
25
33
|
|
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
34
|
private
|
35
|
+
attr_reader :registrations
|
33
36
|
|
34
37
|
def registration_klass
|
35
38
|
Registration
|
36
39
|
end
|
37
40
|
|
38
|
-
def find_registration(symbol, *args)
|
39
|
-
registrations.find { |r| r.matches?(symbol, *args) }
|
41
|
+
def find_registration(symbol, *args, **kwargs)
|
42
|
+
registrations.find { |r| r.matches?(symbol, *args, **kwargs) }
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -59,10 +62,7 @@ module ActiveModel
|
|
59
62
|
type_name == name
|
60
63
|
end
|
61
64
|
|
62
|
-
|
63
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
64
|
-
protected
|
65
|
-
|
65
|
+
private
|
66
66
|
attr_reader :name, :block
|
67
67
|
end
|
68
68
|
end
|