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
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/hash/indifferent_access"
|
4
|
+
require "active_support/core_ext/object/duplicable"
|
4
5
|
|
5
6
|
module ActiveModel
|
6
7
|
class AttributeMutationTracker # :nodoc:
|
7
8
|
OPTION_NOT_GIVEN = Object.new
|
8
9
|
|
9
|
-
def initialize(attributes)
|
10
|
+
def initialize(attributes, forced_changes = Set.new)
|
10
11
|
@attributes = attributes
|
11
|
-
@forced_changes =
|
12
|
+
@forced_changes = forced_changes
|
12
13
|
end
|
13
14
|
|
14
15
|
def changed_attribute_names
|
@@ -18,24 +19,22 @@ module ActiveModel
|
|
18
19
|
def changed_values
|
19
20
|
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
|
20
21
|
if changed?(attr_name)
|
21
|
-
result[attr_name] =
|
22
|
+
result[attr_name] = original_value(attr_name)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
27
|
def changes
|
27
28
|
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
|
28
|
-
change = change_to_attribute(attr_name)
|
29
|
-
if change
|
29
|
+
if change = change_to_attribute(attr_name)
|
30
30
|
result.merge!(attr_name => change)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def change_to_attribute(attr_name)
|
36
|
-
attr_name = attr_name.to_s
|
37
36
|
if changed?(attr_name)
|
38
|
-
[
|
37
|
+
[original_value(attr_name), fetch_value(attr_name)]
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
@@ -44,81 +43,136 @@ module ActiveModel
|
|
44
43
|
end
|
45
44
|
|
46
45
|
def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
|
47
|
-
attr_name
|
48
|
-
|
49
|
-
|
50
|
-
(OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) &&
|
51
|
-
(OPTION_NOT_GIVEN == to || attributes[attr_name].value == to)
|
46
|
+
attribute_changed?(attr_name) &&
|
47
|
+
(OPTION_NOT_GIVEN == from || original_value(attr_name) == from) &&
|
48
|
+
(OPTION_NOT_GIVEN == to || fetch_value(attr_name) == to)
|
52
49
|
end
|
53
50
|
|
54
51
|
def changed_in_place?(attr_name)
|
55
|
-
attributes[attr_name
|
52
|
+
attributes[attr_name].changed_in_place?
|
56
53
|
end
|
57
54
|
|
58
55
|
def forget_change(attr_name)
|
59
|
-
attr_name = attr_name.to_s
|
60
56
|
attributes[attr_name] = attributes[attr_name].forgetting_assignment
|
61
57
|
forced_changes.delete(attr_name)
|
62
58
|
end
|
63
59
|
|
64
60
|
def original_value(attr_name)
|
65
|
-
attributes[attr_name
|
61
|
+
attributes[attr_name].original_value
|
66
62
|
end
|
67
63
|
|
68
64
|
def force_change(attr_name)
|
69
|
-
forced_changes << attr_name
|
65
|
+
forced_changes << attr_name
|
70
66
|
end
|
71
67
|
|
72
|
-
|
73
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
74
|
-
protected
|
75
|
-
|
68
|
+
private
|
76
69
|
attr_reader :attributes, :forced_changes
|
77
70
|
|
71
|
+
def attr_names
|
72
|
+
attributes.keys
|
73
|
+
end
|
74
|
+
|
75
|
+
def attribute_changed?(attr_name)
|
76
|
+
forced_changes.include?(attr_name) || !!attributes[attr_name].changed?
|
77
|
+
end
|
78
|
+
|
79
|
+
def fetch_value(attr_name)
|
80
|
+
attributes.fetch_value(attr_name)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class ForcedMutationTracker < AttributeMutationTracker # :nodoc:
|
85
|
+
def initialize(attributes, forced_changes = {})
|
86
|
+
super
|
87
|
+
@finalized_changes = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def changed_in_place?(attr_name)
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
def change_to_attribute(attr_name)
|
95
|
+
if finalized_changes&.include?(attr_name)
|
96
|
+
finalized_changes[attr_name].dup
|
97
|
+
else
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def forget_change(attr_name)
|
103
|
+
forced_changes.delete(attr_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def original_value(attr_name)
|
107
|
+
if changed?(attr_name)
|
108
|
+
forced_changes[attr_name]
|
109
|
+
else
|
110
|
+
fetch_value(attr_name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def force_change(attr_name)
|
115
|
+
forced_changes[attr_name] = clone_value(attr_name) unless attribute_changed?(attr_name)
|
116
|
+
end
|
117
|
+
|
118
|
+
def finalize_changes
|
119
|
+
@finalized_changes = changes
|
120
|
+
end
|
121
|
+
|
78
122
|
private
|
123
|
+
attr_reader :finalized_changes
|
79
124
|
|
80
125
|
def attr_names
|
81
|
-
|
126
|
+
forced_changes.keys
|
127
|
+
end
|
128
|
+
|
129
|
+
def attribute_changed?(attr_name)
|
130
|
+
forced_changes.include?(attr_name)
|
131
|
+
end
|
132
|
+
|
133
|
+
def fetch_value(attr_name)
|
134
|
+
attributes.send(:_read_attribute, attr_name)
|
135
|
+
end
|
136
|
+
|
137
|
+
def clone_value(attr_name)
|
138
|
+
value = fetch_value(attr_name)
|
139
|
+
value.duplicable? ? value.clone : value
|
140
|
+
rescue TypeError, NoMethodError
|
141
|
+
value
|
82
142
|
end
|
83
143
|
end
|
84
144
|
|
85
145
|
class NullMutationTracker # :nodoc:
|
86
146
|
include Singleton
|
87
147
|
|
88
|
-
def changed_attribute_names
|
148
|
+
def changed_attribute_names
|
89
149
|
[]
|
90
150
|
end
|
91
151
|
|
92
|
-
def changed_values
|
152
|
+
def changed_values
|
93
153
|
{}
|
94
154
|
end
|
95
155
|
|
96
|
-
def changes
|
156
|
+
def changes
|
97
157
|
{}
|
98
158
|
end
|
99
159
|
|
100
160
|
def change_to_attribute(attr_name)
|
101
161
|
end
|
102
162
|
|
103
|
-
def any_changes?
|
163
|
+
def any_changes?
|
104
164
|
false
|
105
165
|
end
|
106
166
|
|
107
|
-
def changed?(
|
167
|
+
def changed?(attr_name, **)
|
108
168
|
false
|
109
169
|
end
|
110
170
|
|
111
|
-
def changed_in_place?(
|
171
|
+
def changed_in_place?(attr_name)
|
112
172
|
false
|
113
173
|
end
|
114
174
|
|
115
|
-
def
|
116
|
-
end
|
117
|
-
|
118
|
-
def original_value(*)
|
119
|
-
end
|
120
|
-
|
121
|
-
def force_change(*)
|
175
|
+
def original_value(attr_name)
|
122
176
|
end
|
123
177
|
end
|
124
178
|
end
|
@@ -90,9 +90,6 @@ module ActiveModel
|
|
90
90
|
end
|
91
91
|
|
92
92
|
protected
|
93
|
-
|
94
|
-
attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
|
95
|
-
|
96
93
|
def materialize
|
97
94
|
unless @materialized
|
98
95
|
values.each_key { |key| self[key] }
|
@@ -105,6 +102,7 @@ module ActiveModel
|
|
105
102
|
end
|
106
103
|
|
107
104
|
private
|
105
|
+
attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
|
108
106
|
|
109
107
|
def assign_default_value(name)
|
110
108
|
type = additional_types.fetch(name, types[name])
|
@@ -37,16 +37,8 @@ module ActiveModel
|
|
37
37
|
attributes.each_key.select { |name| self[name].initialized? }
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
# https://github.com/jruby/jruby/pull/2562
|
43
|
-
def fetch_value(name, &block)
|
44
|
-
self[name].value(&block)
|
45
|
-
end
|
46
|
-
else
|
47
|
-
def fetch_value(name)
|
48
|
-
self[name].value { |n| yield n if block_given? }
|
49
|
-
end
|
40
|
+
def fetch_value(name, &block)
|
41
|
+
self[name].value(&block)
|
50
42
|
end
|
51
43
|
|
52
44
|
def write_from_database(name, value)
|
@@ -102,11 +94,9 @@ module ActiveModel
|
|
102
94
|
end
|
103
95
|
|
104
96
|
protected
|
105
|
-
|
106
97
|
attr_reader :attributes
|
107
98
|
|
108
99
|
private
|
109
|
-
|
110
100
|
def initialized_attributes
|
111
101
|
attributes.select { |_, attr| attr.initialized? }
|
112
102
|
end
|
@@ -26,20 +26,33 @@ module ActiveModel
|
|
26
26
|
define_attribute_method(name)
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
# Returns an array of attribute names as strings
|
30
|
+
#
|
31
|
+
# class Person
|
32
|
+
# include ActiveModel::Attributes
|
33
|
+
#
|
34
|
+
# attribute :name, :string
|
35
|
+
# attribute :age, :integer
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Person.attribute_names
|
39
|
+
# # => ["name", "age"]
|
40
|
+
def attribute_names
|
41
|
+
attribute_types.keys
|
42
|
+
end
|
30
43
|
|
44
|
+
private
|
31
45
|
def define_method_attribute=(name)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
STR
|
46
|
+
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
47
|
+
generated_attribute_methods, name, writer: true,
|
48
|
+
) do |temp_method_name, attr_name_expr|
|
49
|
+
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
50
|
+
def #{temp_method_name}(value)
|
51
|
+
name = #{attr_name_expr}
|
52
|
+
write_attribute(name, value)
|
53
|
+
end
|
54
|
+
RUBY
|
55
|
+
end
|
43
56
|
end
|
44
57
|
|
45
58
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
@@ -66,46 +79,58 @@ module ActiveModel
|
|
66
79
|
super
|
67
80
|
end
|
68
81
|
|
82
|
+
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
83
|
+
#
|
84
|
+
# class Person
|
85
|
+
# include ActiveModel::Model
|
86
|
+
# include ActiveModel::Attributes
|
87
|
+
#
|
88
|
+
# attribute :name, :string
|
89
|
+
# attribute :age, :integer
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# person = Person.new(name: 'Francesco', age: 22)
|
93
|
+
# person.attributes
|
94
|
+
# # => {"name"=>"Francesco", "age"=>22}
|
69
95
|
def attributes
|
70
96
|
@attributes.to_hash
|
71
97
|
end
|
72
98
|
|
73
|
-
|
99
|
+
# Returns an array of attribute names as strings
|
100
|
+
#
|
101
|
+
# class Person
|
102
|
+
# include ActiveModel::Attributes
|
103
|
+
#
|
104
|
+
# attribute :name, :string
|
105
|
+
# attribute :age, :integer
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# person = Person.new
|
109
|
+
# person.attribute_names
|
110
|
+
# # => ["name", "age"]
|
111
|
+
def attribute_names
|
112
|
+
@attributes.keys
|
113
|
+
end
|
74
114
|
|
115
|
+
private
|
75
116
|
def write_attribute(attr_name, value)
|
76
|
-
name =
|
77
|
-
|
78
|
-
else
|
79
|
-
attr_name.to_s
|
80
|
-
end
|
117
|
+
name = attr_name.to_s
|
118
|
+
name = self.class.attribute_aliases[name] || name
|
81
119
|
|
82
120
|
@attributes.write_from_user(name, value)
|
83
121
|
value
|
84
122
|
end
|
85
123
|
|
86
124
|
def attribute(attr_name)
|
87
|
-
name =
|
88
|
-
|
89
|
-
|
90
|
-
attr_name.to_s
|
91
|
-
end
|
125
|
+
name = attr_name.to_s
|
126
|
+
name = self.class.attribute_aliases[name] || name
|
127
|
+
|
92
128
|
@attributes.fetch_value(name)
|
93
129
|
end
|
94
130
|
|
95
|
-
#
|
131
|
+
# Dispatch target for <tt>*=</tt> attribute methods.
|
96
132
|
def attribute=(attribute_name, value)
|
97
133
|
write_attribute(attribute_name, value)
|
98
134
|
end
|
99
135
|
end
|
100
|
-
|
101
|
-
module AttributeMethods #:nodoc:
|
102
|
-
AttrNames = Module.new {
|
103
|
-
def self.set_name_cache(name, value)
|
104
|
-
const_name = "ATTR_#{name}"
|
105
|
-
unless const_defined? const_name
|
106
|
-
const_set const_name, value.dup.freeze
|
107
|
-
end
|
108
|
-
end
|
109
|
-
}
|
110
|
-
end
|
111
136
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/array/extract_options"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
4
5
|
|
5
6
|
module ActiveModel
|
6
7
|
# == Active \Model \Callbacks
|
@@ -125,28 +126,29 @@ module ActiveModel
|
|
125
126
|
end
|
126
127
|
|
127
128
|
private
|
128
|
-
|
129
129
|
def _define_before_model_callback(klass, callback)
|
130
|
-
klass.define_singleton_method("before_#{callback}") do |*args, &block|
|
131
|
-
|
130
|
+
klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
|
131
|
+
options.assert_valid_keys(:if, :unless, :prepend)
|
132
|
+
set_callback(:"#{callback}", :before, *args, options, &block)
|
132
133
|
end
|
133
134
|
end
|
134
135
|
|
135
136
|
def _define_around_model_callback(klass, callback)
|
136
|
-
klass.define_singleton_method("around_#{callback}") do |*args, &block|
|
137
|
-
|
137
|
+
klass.define_singleton_method("around_#{callback}") do |*args, **options, &block|
|
138
|
+
options.assert_valid_keys(:if, :unless, :prepend)
|
139
|
+
set_callback(:"#{callback}", :around, *args, options, &block)
|
138
140
|
end
|
139
141
|
end
|
140
142
|
|
141
143
|
def _define_after_model_callback(klass, callback)
|
142
|
-
klass.define_singleton_method("after_#{callback}") do |*args, &block|
|
143
|
-
options
|
144
|
+
klass.define_singleton_method("after_#{callback}") do |*args, **options, &block|
|
145
|
+
options.assert_valid_keys(:if, :unless, :prepend)
|
144
146
|
options[:prepend] = true
|
145
147
|
conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
|
146
148
|
v != false
|
147
149
|
}
|
148
150
|
options[:if] = Array(options[:if]) << conditional
|
149
|
-
set_callback(:"#{callback}", :after, *
|
151
|
+
set_callback(:"#{callback}", :after, *args, options, &block)
|
150
152
|
end
|
151
153
|
end
|
152
154
|
end
|
@@ -103,7 +103,7 @@ module ActiveModel
|
|
103
103
|
@_to_partial_path ||= begin
|
104
104
|
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
|
105
105
|
collection = ActiveSupport::Inflector.tableize(name)
|
106
|
-
"#{collection}/#{element}"
|
106
|
+
"#{collection}/#{element}"
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|