activemodel 6.0.5.1 → 6.1.7.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 +95 -160
- data/MIT-LICENSE +1 -2
- data/README.rdoc +1 -1
- data/lib/active_model/attribute.rb +18 -17
- data/lib/active_model/attribute_assignment.rb +3 -4
- data/lib/active_model/attribute_methods.rb +74 -38
- data/lib/active_model/attribute_mutation_tracker.rb +8 -5
- data/lib/active_model/attribute_set/builder.rb +80 -13
- data/lib/active_model/attribute_set.rb +18 -16
- data/lib/active_model/attributes.rb +20 -24
- data/lib/active_model/callbacks.rb +1 -1
- data/lib/active_model/dirty.rb +17 -4
- data/lib/active_model/error.rb +207 -0
- data/lib/active_model/errors.rb +316 -208
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/lint.rb +1 -1
- data/lib/active_model/naming.rb +2 -2
- data/lib/active_model/nested_error.rb +22 -0
- data/lib/active_model/railtie.rb +1 -1
- data/lib/active_model/secure_password.rb +15 -14
- data/lib/active_model/serialization.rb +9 -6
- data/lib/active_model/serializers/json.rb +7 -0
- data/lib/active_model/type/date_time.rb +2 -2
- data/lib/active_model/type/float.rb +2 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +11 -7
- data/lib/active_model/type/helpers/numeric.rb +8 -3
- data/lib/active_model/type/helpers/time_value.rb +27 -17
- data/lib/active_model/type/helpers/timezone.rb +1 -1
- data/lib/active_model/type/immutable_string.rb +14 -10
- data/lib/active_model/type/integer.rb +11 -2
- data/lib/active_model/type/registry.rb +12 -9
- data/lib/active_model/type/string.rb +12 -2
- data/lib/active_model/type/value.rb +9 -1
- data/lib/active_model/type.rb +3 -2
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/callbacks.rb +15 -15
- data/lib/active_model/validations/clusivity.rb +5 -1
- data/lib/active_model/validations/confirmation.rb +2 -2
- data/lib/active_model/validations/exclusion.rb +1 -1
- data/lib/active_model/validations/format.rb +2 -2
- data/lib/active_model/validations/inclusion.rb +1 -1
- data/lib/active_model/validations/length.rb +2 -2
- data/lib/active_model/validations/numericality.rb +54 -41
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/validates.rb +6 -4
- data/lib/active_model/validations.rb +6 -6
- data/lib/active_model/validator.rb +7 -1
- data/lib/active_model.rb +2 -1
- metadata +9 -7
data/lib/active_model/lint.rb
CHANGED
@@ -101,7 +101,7 @@ module ActiveModel
|
|
101
101
|
# locale. If no error is present, the method should return an empty array.
|
102
102
|
def test_errors_aref
|
103
103
|
assert_respond_to model, :errors
|
104
|
-
|
104
|
+
assert_equal [], model.errors[:hello], "errors#[] should return an empty Array"
|
105
105
|
end
|
106
106
|
|
107
107
|
private
|
data/lib/active_model/naming.rb
CHANGED
@@ -8,7 +8,7 @@ module ActiveModel
|
|
8
8
|
class Name
|
9
9
|
include Comparable
|
10
10
|
|
11
|
-
|
11
|
+
attr_accessor :singular, :plural, :element, :collection,
|
12
12
|
:singular_route_key, :route_key, :param_key, :i18n_key,
|
13
13
|
:name
|
14
14
|
|
@@ -166,7 +166,7 @@ module ActiveModel
|
|
166
166
|
|
167
167
|
raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
|
168
168
|
|
169
|
-
@unnamespaced = @name.
|
169
|
+
@unnamespaced = @name.delete_prefix("#{namespace.name}::") if namespace
|
170
170
|
@klass = klass
|
171
171
|
@singular = _singularize(@name)
|
172
172
|
@plural = ActiveSupport::Inflector.pluralize(@singular)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model/error"
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module ActiveModel
|
7
|
+
class NestedError < Error
|
8
|
+
def initialize(base, inner_error, override_options = {})
|
9
|
+
@base = base
|
10
|
+
@inner_error = inner_error
|
11
|
+
@attribute = override_options.fetch(:attribute) { inner_error.attribute }
|
12
|
+
@type = override_options.fetch(:type) { inner_error.type }
|
13
|
+
@raw_type = inner_error.raw_type
|
14
|
+
@options = inner_error.options
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :inner_error
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
def_delegators :@inner_error, :message
|
21
|
+
end
|
22
|
+
end
|
data/lib/active_model/railtie.rb
CHANGED
@@ -14,7 +14,7 @@ module ActiveModel
|
|
14
14
|
end
|
15
15
|
|
16
16
|
initializer "active_model.i18n_customize_full_message" do
|
17
|
-
ActiveModel::
|
17
|
+
ActiveModel::Error.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -45,19 +45,19 @@ module ActiveModel
|
|
45
45
|
# end
|
46
46
|
#
|
47
47
|
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
|
48
|
-
# user.save
|
48
|
+
# user.save # => false, password required
|
49
49
|
# user.password = 'mUc3m00RsqyRe'
|
50
|
-
# user.save
|
50
|
+
# user.save # => false, confirmation doesn't match
|
51
51
|
# user.password_confirmation = 'mUc3m00RsqyRe'
|
52
|
-
# user.save
|
52
|
+
# user.save # => true
|
53
53
|
# user.recovery_password = "42password"
|
54
|
-
# user.recovery_password_digest
|
55
|
-
# user.save
|
56
|
-
# user.authenticate('notright')
|
57
|
-
# user.authenticate('mUc3m00RsqyRe')
|
58
|
-
# user.authenticate_recovery_password('42password')
|
59
|
-
# User.find_by(name: 'david')
|
60
|
-
# User.find_by(name: 'david')
|
54
|
+
# user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
|
55
|
+
# user.save # => true
|
56
|
+
# user.authenticate('notright') # => false
|
57
|
+
# user.authenticate('mUc3m00RsqyRe') # => user
|
58
|
+
# user.authenticate_recovery_password('42password') # => user
|
59
|
+
# User.find_by(name: 'david')&.authenticate('notright') # => false
|
60
|
+
# User.find_by(name: 'david')&.authenticate('mUc3m00RsqyRe') # => user
|
61
61
|
def has_secure_password(attribute = :password, validations: true)
|
62
62
|
# Load bcrypt gem only when has_secure_password is used.
|
63
63
|
# This is to avoid ActiveModel (and by extension the entire framework)
|
@@ -79,7 +79,7 @@ module ActiveModel
|
|
79
79
|
# when there is an error, the message is added to the password attribute instead
|
80
80
|
# so that the error message will make sense to the end-user.
|
81
81
|
validate do |record|
|
82
|
-
record.errors.add(attribute, :blank) unless record.
|
82
|
+
record.errors.add(attribute, :blank) unless record.public_send("#{attribute}_digest").present?
|
83
83
|
end
|
84
84
|
|
85
85
|
validates_length_of attribute, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
|
@@ -94,11 +94,12 @@ module ActiveModel
|
|
94
94
|
|
95
95
|
define_method("#{attribute}=") do |unencrypted_password|
|
96
96
|
if unencrypted_password.nil?
|
97
|
-
|
97
|
+
instance_variable_set("@#{attribute}", nil)
|
98
|
+
self.public_send("#{attribute}_digest=", nil)
|
98
99
|
elsif !unencrypted_password.empty?
|
99
100
|
instance_variable_set("@#{attribute}", unencrypted_password)
|
100
101
|
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
101
|
-
self.
|
102
|
+
self.public_send("#{attribute}_digest=", BCrypt::Password.create(unencrypted_password, cost: cost))
|
102
103
|
end
|
103
104
|
end
|
104
105
|
|
@@ -117,7 +118,7 @@ module ActiveModel
|
|
117
118
|
# user.authenticate_password('notright') # => false
|
118
119
|
# user.authenticate_password('mUc3m00RsqyRe') # => user
|
119
120
|
define_method("authenticate_#{attribute}") do |unencrypted_password|
|
120
|
-
attribute_digest =
|
121
|
+
attribute_digest = public_send("#{attribute}_digest")
|
121
122
|
BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
|
122
123
|
end
|
123
124
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/
|
4
|
-
require "active_support/core_ext/hash/slice"
|
3
|
+
require "active_support/core_ext/enumerable"
|
5
4
|
|
6
5
|
module ActiveModel
|
7
6
|
# == Active \Model \Serialization
|
@@ -124,17 +123,17 @@ module ActiveModel
|
|
124
123
|
# user.serializable_hash(include: { notes: { only: 'title' }})
|
125
124
|
# # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
|
126
125
|
def serializable_hash(options = nil)
|
127
|
-
options ||= {}
|
128
|
-
|
129
126
|
attribute_names = attributes.keys
|
127
|
+
|
128
|
+
return serializable_attributes(attribute_names) if options.blank?
|
129
|
+
|
130
130
|
if only = options[:only]
|
131
131
|
attribute_names &= Array(only).map(&:to_s)
|
132
132
|
elsif except = options[:except]
|
133
133
|
attribute_names -= Array(except).map(&:to_s)
|
134
134
|
end
|
135
135
|
|
136
|
-
hash =
|
137
|
-
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
|
136
|
+
hash = serializable_attributes(attribute_names)
|
138
137
|
|
139
138
|
Array(options[:methods]).each { |m| hash[m.to_s] = send(m) }
|
140
139
|
|
@@ -168,6 +167,10 @@ module ActiveModel
|
|
168
167
|
# end
|
169
168
|
alias :read_attribute_for_serialization :send
|
170
169
|
|
170
|
+
def serializable_attributes(attribute_names)
|
171
|
+
attribute_names.index_with { |n| read_attribute_for_serialization(n) }
|
172
|
+
end
|
173
|
+
|
171
174
|
# Add associations specified via the <tt>:include</tt> option.
|
172
175
|
#
|
173
176
|
# Expects a block that takes as arguments:
|
@@ -42,6 +42,13 @@ module ActiveModel
|
|
42
42
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
43
43
|
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
44
44
|
#
|
45
|
+
# If you prefer, <tt>:root</tt> may also be set to a custom string key instead as in:
|
46
|
+
#
|
47
|
+
# user = User.find(1)
|
48
|
+
# user.as_json(root: "author")
|
49
|
+
# # => { "author" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
50
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
51
|
+
#
|
45
52
|
# Without any +options+, the returned Hash will include all the model's
|
46
53
|
# attributes.
|
47
54
|
#
|
@@ -35,8 +35,8 @@ module ActiveModel
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def value_from_multiparameter_assignment(values_hash)
|
38
|
-
missing_parameters =
|
39
|
-
|
38
|
+
missing_parameters = [1, 2, 3].delete_if { |key| values_hash.key?(key) }
|
39
|
+
unless missing_parameters.empty?
|
40
40
|
raise ArgumentError, "Provided hash #{values_hash} doesn't contain necessary keys: #{missing_parameters}"
|
41
41
|
end
|
42
42
|
super
|
@@ -4,12 +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
9
|
super(cast(value))
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
def cast(value)
|
13
13
|
if value.is_a?(Hash)
|
14
14
|
value_from_multiparameter_assignment(value)
|
15
15
|
else
|
@@ -17,7 +17,7 @@ module ActiveModel
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
def assert_valid_value(value)
|
21
21
|
if value.is_a?(Hash)
|
22
22
|
value_from_multiparameter_assignment(value)
|
23
23
|
else
|
@@ -25,17 +25,21 @@ module ActiveModel
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
def value_constructed_by_mass_assignment?(value)
|
29
29
|
value.is_a?(Hash)
|
30
30
|
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(defaults: {})
|
34
|
+
include InstanceMethods
|
31
35
|
|
32
36
|
define_method(:value_from_multiparameter_assignment) do |values_hash|
|
33
37
|
defaults.each do |k, v|
|
34
38
|
values_hash[k] ||= v
|
35
39
|
end
|
36
40
|
return unless values_hash[1] && values_hash[2] && values_hash[3]
|
37
|
-
values = values_hash.sort.map(&:last)
|
38
|
-
::Time.
|
41
|
+
values = values_hash.sort.map!(&:last)
|
42
|
+
::Time.public_send(default_timezone, *values)
|
39
43
|
end
|
40
44
|
private :value_from_multiparameter_assignment
|
41
45
|
end
|
@@ -9,13 +9,18 @@ module ActiveModel
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def cast(value)
|
12
|
-
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
|
13
17
|
case value
|
14
18
|
when true then 1
|
15
19
|
when false then 0
|
16
|
-
|
17
|
-
else value
|
20
|
+
else value.presence
|
18
21
|
end
|
22
|
+
end
|
23
|
+
|
19
24
|
super(value)
|
20
25
|
end
|
21
26
|
|
@@ -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
|
|
@@ -52,27 +52,37 @@ module ActiveModel
|
|
52
52
|
time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
53
53
|
return unless time
|
54
54
|
|
55
|
-
time -= offset
|
55
|
+
time -= offset unless offset == 0
|
56
56
|
is_utc? ? time : time.getlocal
|
57
|
+
elsif is_utc?
|
58
|
+
::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
57
59
|
else
|
58
|
-
::Time.
|
60
|
+
::Time.local(year, mon, mday, hour, min, sec, microsec) rescue nil
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
62
|
-
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
|
63
71
|
|
64
|
-
# Doesn't handle time zones.
|
65
72
|
def fast_string_to_time(string)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
microsec = (microsec_part.to_r * 1_000_000).to_i
|
73
|
-
end
|
74
|
-
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
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)
|
75
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)
|
76
86
|
end
|
77
87
|
end
|
78
88
|
end
|
@@ -3,28 +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
26
|
def cast_value(value)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
result.freeze
|
27
|
+
case value
|
28
|
+
when true then @true
|
29
|
+
when false then @false
|
30
|
+
else value.to_s.freeze
|
31
|
+
end
|
28
32
|
end
|
29
33
|
end
|
30
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
|
@@ -28,15 +28,24 @@ module ActiveModel
|
|
28
28
|
ensure_in_range(super)
|
29
29
|
end
|
30
30
|
|
31
|
+
def serializable?(value)
|
32
|
+
cast_value = cast(value)
|
33
|
+
in_range?(cast_value) && super
|
34
|
+
end
|
35
|
+
|
31
36
|
private
|
32
37
|
attr_reader :range
|
33
38
|
|
39
|
+
def in_range?(value)
|
40
|
+
!value || range.member?(value)
|
41
|
+
end
|
42
|
+
|
34
43
|
def cast_value(value)
|
35
44
|
value.to_i rescue nil
|
36
45
|
end
|
37
46
|
|
38
47
|
def ensure_in_range(value)
|
39
|
-
|
48
|
+
unless in_range?(value)
|
40
49
|
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
41
50
|
end
|
42
51
|
value
|
@@ -8,6 +8,11 @@ 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?
|
13
18
|
block = proc { |_, *args| klass.new(*args) }
|
@@ -16,15 +21,16 @@ module ActiveModel
|
|
16
21
|
registrations << registration_klass.new(type_name, block, **options)
|
17
22
|
end
|
18
23
|
|
19
|
-
def lookup(symbol, *args
|
20
|
-
registration = find_registration(symbol, *args
|
24
|
+
def lookup(symbol, *args)
|
25
|
+
registration = find_registration(symbol, *args)
|
21
26
|
|
22
27
|
if registration
|
23
|
-
registration.call(self, symbol, *args
|
28
|
+
registration.call(self, symbol, *args)
|
24
29
|
else
|
25
30
|
raise ArgumentError, "Unknown type #{symbol.inspect}"
|
26
31
|
end
|
27
32
|
end
|
33
|
+
ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
|
28
34
|
|
29
35
|
private
|
30
36
|
attr_reader :registrations
|
@@ -45,13 +51,10 @@ module ActiveModel
|
|
45
51
|
@block = block
|
46
52
|
end
|
47
53
|
|
48
|
-
def call(_registry, *args
|
49
|
-
|
50
|
-
block.call(*args, **kwargs)
|
51
|
-
else
|
52
|
-
block.call(*args)
|
53
|
-
end
|
54
|
+
def call(_registry, *args)
|
55
|
+
block.call(*args)
|
54
56
|
end
|
57
|
+
ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
|
55
58
|
|
56
59
|
def matches?(type_name, *args, **kwargs)
|
57
60
|
type_name == name
|
@@ -11,12 +11,22 @@ module ActiveModel
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
def to_immutable_string
|
15
|
+
ImmutableString.new(
|
16
|
+
true: @true,
|
17
|
+
false: @false,
|
18
|
+
limit: limit,
|
19
|
+
precision: precision,
|
20
|
+
scale: scale,
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
14
24
|
private
|
15
25
|
def cast_value(value)
|
16
26
|
case value
|
17
27
|
when ::String then ::String.new(value)
|
18
|
-
when true then
|
19
|
-
when false then
|
28
|
+
when true then @true
|
29
|
+
when false then @false
|
20
30
|
else value.to_s
|
21
31
|
end
|
22
32
|
end
|
@@ -11,6 +11,14 @@ module ActiveModel
|
|
11
11
|
@limit = limit
|
12
12
|
end
|
13
13
|
|
14
|
+
# Returns true if this type can convert +value+ to a type that is usable
|
15
|
+
# by the database. For example a boolean type can return +true+ if the
|
16
|
+
# value parameter is a Ruby boolean, but may return +false+ if the value
|
17
|
+
# parameter is some other object.
|
18
|
+
def serializable?(value)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
14
22
|
def type # :nodoc:
|
15
23
|
end
|
16
24
|
|
@@ -110,7 +118,7 @@ module ActiveModel
|
|
110
118
|
[self.class, precision, scale, limit].hash
|
111
119
|
end
|
112
120
|
|
113
|
-
def assert_valid_value(
|
121
|
+
def assert_valid_value(_)
|
114
122
|
end
|
115
123
|
|
116
124
|
private
|
data/lib/active_model/type.rb
CHANGED
@@ -29,9 +29,10 @@ module ActiveModel
|
|
29
29
|
registry.register(type_name, klass, **options, &block)
|
30
30
|
end
|
31
31
|
|
32
|
-
def lookup(*args
|
33
|
-
registry.lookup(*args
|
32
|
+
def lookup(*args) # :nodoc:
|
33
|
+
registry.lookup(*args)
|
34
34
|
end
|
35
|
+
ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
|
35
36
|
|
36
37
|
def default_value # :nodoc:
|
37
38
|
@default_value ||= Value.new
|
@@ -5,7 +5,7 @@ module ActiveModel
|
|
5
5
|
# == \Active \Model Absence Validator
|
6
6
|
class AbsenceValidator < EachValidator #:nodoc:
|
7
7
|
def validate_each(record, attr_name, value)
|
8
|
-
record.errors.add(attr_name, :present, options) if value.present?
|
8
|
+
record.errors.add(attr_name, :present, **options) if value.present?
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
10
10
|
|
11
11
|
def validate_each(record, attribute, value)
|
12
12
|
unless acceptable_option?(value)
|
13
|
-
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
|
13
|
+
record.errors.add(attribute, :accepted, **options.except(:accept, :allow_nil))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -56,14 +56,7 @@ module ActiveModel
|
|
56
56
|
def before_validation(*args, &block)
|
57
57
|
options = args.extract_options!
|
58
58
|
|
59
|
-
|
60
|
-
options = options.dup
|
61
|
-
options[:on] = Array(options[:on])
|
62
|
-
options[:if] = Array(options[:if])
|
63
|
-
options[:if].unshift ->(o) {
|
64
|
-
!(options[:on] & Array(o.validation_context)).empty?
|
65
|
-
}
|
66
|
-
end
|
59
|
+
set_options_for_callback(options)
|
67
60
|
|
68
61
|
set_callback(:validation, :before, *args, options, &block)
|
69
62
|
end
|
@@ -99,16 +92,23 @@ module ActiveModel
|
|
99
92
|
options = options.dup
|
100
93
|
options[:prepend] = true
|
101
94
|
|
102
|
-
|
103
|
-
options[:on] = Array(options[:on])
|
104
|
-
options[:if] = Array(options[:if])
|
105
|
-
options[:if].unshift ->(o) {
|
106
|
-
!(options[:on] & Array(o.validation_context)).empty?
|
107
|
-
}
|
108
|
-
end
|
95
|
+
set_options_for_callback(options)
|
109
96
|
|
110
97
|
set_callback(:validation, :after, *args, options, &block)
|
111
98
|
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def set_options_for_callback(options)
|
102
|
+
if options.key?(:on)
|
103
|
+
options[:on] = Array(options[:on])
|
104
|
+
options[:if] = [
|
105
|
+
->(o) {
|
106
|
+
!(options[:on] & Array(o.validation_context)).empty?
|
107
|
+
},
|
108
|
+
*options[:if]
|
109
|
+
]
|
110
|
+
end
|
111
|
+
end
|
112
112
|
end
|
113
113
|
|
114
114
|
private
|
@@ -24,7 +24,11 @@ module ActiveModel
|
|
24
24
|
delimiter
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
if value.is_a?(Array)
|
28
|
+
value.all? { |v| members.public_send(inclusion_method(members), v) }
|
29
|
+
else
|
30
|
+
members.public_send(inclusion_method(members), value)
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
def delimiter
|
@@ -9,10 +9,10 @@ module ActiveModel
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def validate_each(record, attribute, value)
|
12
|
-
unless (confirmed = record.
|
12
|
+
unless (confirmed = record.public_send("#{attribute}_confirmation")).nil?
|
13
13
|
unless confirmation_value_equal?(record, attribute, value, confirmed)
|
14
14
|
human_attribute_name = record.class.human_attribute_name(attribute)
|
15
|
-
record.errors.add(:"#{attribute}_confirmation", :confirmation, options.except(:case_sensitive).merge!(attribute: human_attribute_name))
|
15
|
+
record.errors.add(:"#{attribute}_confirmation", :confirmation, **options.except(:case_sensitive).merge!(attribute: human_attribute_name))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|