activemodel 5.2.7.1 → 6.1.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 +65 -111
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -4
- data/lib/active_model/attribute/user_provided_default.rb +1 -2
- data/lib/active_model/attribute.rb +21 -21
- 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/builder.rb +81 -16
- data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
- data/lib/active_model/attribute_set.rb +20 -28
- 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 +4 -4
- 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/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/validations.rb +6 -9
- data/lib/active_model/validator.rb +8 -3
- data/lib/active_model.rb +2 -1
- metadata +14 -9
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
3
4
|
require "active_support/core_ext/object/deep_dup"
|
4
5
|
require "active_model/attribute_set/builder"
|
5
6
|
require "active_model/attribute_set/yaml_encoder"
|
@@ -13,11 +14,11 @@ module ActiveModel
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def [](name)
|
16
|
-
attributes[name] ||
|
17
|
+
@attributes[name] || default_attribute(name)
|
17
18
|
end
|
18
19
|
|
19
20
|
def []=(name, value)
|
20
|
-
attributes[name] = value
|
21
|
+
@attributes[name] = value
|
21
22
|
end
|
22
23
|
|
23
24
|
def values_before_type_cast
|
@@ -25,9 +26,9 @@ module ActiveModel
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def to_hash
|
28
|
-
|
29
|
+
keys.index_with { |name| self[name].value }
|
29
30
|
end
|
30
|
-
|
31
|
+
alias :to_h :to_hash
|
31
32
|
|
32
33
|
def key?(name)
|
33
34
|
attributes.key?(name) && self[name].initialized?
|
@@ -37,48 +38,41 @@ module ActiveModel
|
|
37
38
|
attributes.each_key.select { |name| self[name].initialized? }
|
38
39
|
end
|
39
40
|
|
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
|
41
|
+
def fetch_value(name, &block)
|
42
|
+
self[name].value(&block)
|
50
43
|
end
|
51
44
|
|
52
45
|
def write_from_database(name, value)
|
53
|
-
attributes[name] = self[name].with_value_from_database(value)
|
46
|
+
@attributes[name] = self[name].with_value_from_database(value)
|
54
47
|
end
|
55
48
|
|
56
49
|
def write_from_user(name, value)
|
57
|
-
attributes
|
50
|
+
raise FrozenError, "can't modify frozen attributes" if frozen?
|
51
|
+
@attributes[name] = self[name].with_value_from_user(value)
|
52
|
+
value
|
58
53
|
end
|
59
54
|
|
60
55
|
def write_cast_value(name, value)
|
61
|
-
attributes[name] = self[name].with_cast_value(value)
|
56
|
+
@attributes[name] = self[name].with_cast_value(value)
|
57
|
+
value
|
62
58
|
end
|
63
59
|
|
64
60
|
def freeze
|
65
|
-
|
61
|
+
attributes.freeze
|
66
62
|
super
|
67
63
|
end
|
68
64
|
|
69
65
|
def deep_dup
|
70
|
-
|
71
|
-
copy.instance_variable_set(:@attributes, attributes.deep_dup)
|
72
|
-
end
|
66
|
+
AttributeSet.new(attributes.deep_dup)
|
73
67
|
end
|
74
68
|
|
75
69
|
def initialize_dup(_)
|
76
|
-
@attributes = attributes.dup
|
70
|
+
@attributes = @attributes.dup
|
77
71
|
super
|
78
72
|
end
|
79
73
|
|
80
74
|
def initialize_clone(_)
|
81
|
-
@attributes = attributes.clone
|
75
|
+
@attributes = @attributes.clone
|
82
76
|
super
|
83
77
|
end
|
84
78
|
|
@@ -89,7 +83,7 @@ module ActiveModel
|
|
89
83
|
end
|
90
84
|
|
91
85
|
def accessed
|
92
|
-
attributes.select { |
|
86
|
+
attributes.each_key.select { |name| self[name].has_been_read? }
|
93
87
|
end
|
94
88
|
|
95
89
|
def map(&block)
|
@@ -102,13 +96,11 @@ module ActiveModel
|
|
102
96
|
end
|
103
97
|
|
104
98
|
protected
|
105
|
-
|
106
99
|
attr_reader :attributes
|
107
100
|
|
108
101
|
private
|
109
|
-
|
110
|
-
|
111
|
-
attributes.select { |_, attr| attr.initialized? }
|
102
|
+
def default_attribute(name)
|
103
|
+
Attribute.null(name)
|
112
104
|
end
|
113
105
|
end
|
114
106
|
end
|
@@ -26,20 +26,31 @@ module ActiveModel
|
|
26
26
|
define_attribute_method(name)
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
private
|
45
|
+
def define_method_attribute=(name, owner:)
|
46
|
+
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
47
|
+
owner, name, writer: true,
|
48
|
+
) do |temp_method_name, attr_name_expr|
|
49
|
+
owner <<
|
50
|
+
"def #{temp_method_name}(value)" <<
|
51
|
+
" _write_attribute(#{attr_name_expr}, value)" <<
|
52
|
+
"end"
|
53
|
+
end
|
43
54
|
end
|
44
55
|
|
45
56
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
@@ -66,46 +77,56 @@ module ActiveModel
|
|
66
77
|
super
|
67
78
|
end
|
68
79
|
|
80
|
+
def initialize_dup(other) # :nodoc:
|
81
|
+
@attributes = @attributes.deep_dup
|
82
|
+
super
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
86
|
+
#
|
87
|
+
# class Person
|
88
|
+
# include ActiveModel::Attributes
|
89
|
+
#
|
90
|
+
# attribute :name, :string
|
91
|
+
# attribute :age, :integer
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# person = Person.new(name: 'Francesco', age: 22)
|
95
|
+
# person.attributes
|
96
|
+
# # => {"name"=>"Francesco", "age"=>22}
|
69
97
|
def attributes
|
70
98
|
@attributes.to_hash
|
71
99
|
end
|
72
100
|
|
73
|
-
|
101
|
+
# Returns an array of attribute names as strings
|
102
|
+
#
|
103
|
+
# class Person
|
104
|
+
# include ActiveModel::Attributes
|
105
|
+
#
|
106
|
+
# attribute :name, :string
|
107
|
+
# attribute :age, :integer
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# person = Person.new
|
111
|
+
# person.attribute_names
|
112
|
+
# # => ["name", "age"]
|
113
|
+
def attribute_names
|
114
|
+
@attributes.keys
|
115
|
+
end
|
74
116
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
attr_name.to_s
|
80
|
-
end
|
117
|
+
def freeze
|
118
|
+
@attributes = @attributes.clone.freeze unless frozen?
|
119
|
+
super
|
120
|
+
end
|
81
121
|
|
82
|
-
|
83
|
-
|
122
|
+
private
|
123
|
+
def _write_attribute(attr_name, value)
|
124
|
+
@attributes.write_from_user(attr_name, value)
|
84
125
|
end
|
126
|
+
alias :attribute= :_write_attribute
|
85
127
|
|
86
128
|
def attribute(attr_name)
|
87
|
-
|
88
|
-
self.class.attribute_alias(attr_name).to_s
|
89
|
-
else
|
90
|
-
attr_name.to_s
|
91
|
-
end
|
92
|
-
@attributes.fetch_value(name)
|
93
|
-
end
|
94
|
-
|
95
|
-
# Handle *= for method_missing.
|
96
|
-
def attribute=(attribute_name, value)
|
97
|
-
write_attribute(attribute_name, value)
|
98
|
-
end
|
99
|
-
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
|
129
|
+
@attributes.fetch_value(attr_name)
|
108
130
|
end
|
109
|
-
}
|
110
131
|
end
|
111
132
|
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
|
-
options[:if] = Array(options[:if])
|
149
|
-
set_callback(:"#{callback}", :after, *
|
150
|
+
options[:if] = Array(options[:if]) + [conditional]
|
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
|
data/lib/active_model/dirty.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/hash_with_indifferent_access"
|
4
|
-
require "active_support/core_ext/object/duplicable"
|
5
3
|
require "active_model/attribute_mutation_tracker"
|
6
4
|
|
7
5
|
module ActiveModel
|
@@ -85,7 +83,9 @@ module ActiveModel
|
|
85
83
|
#
|
86
84
|
# person.previous_changes # => {"name" => [nil, "Bill"]}
|
87
85
|
# person.name_previously_changed? # => true
|
86
|
+
# person.name_previously_changed?(from: nil, to: "Bill") # => true
|
88
87
|
# person.name_previous_change # => [nil, "Bill"]
|
88
|
+
# person.name_previously_was # => nil
|
89
89
|
# person.reload!
|
90
90
|
# person.previous_changes # => {}
|
91
91
|
#
|
@@ -122,13 +122,11 @@ module ActiveModel
|
|
122
122
|
extend ActiveSupport::Concern
|
123
123
|
include ActiveModel::AttributeMethods
|
124
124
|
|
125
|
-
OPTION_NOT_GIVEN = Object.new # :nodoc:
|
126
|
-
private_constant :OPTION_NOT_GIVEN
|
127
|
-
|
128
125
|
included do
|
129
126
|
attribute_method_suffix "_changed?", "_change", "_will_change!", "_was"
|
130
|
-
attribute_method_suffix "_previously_changed?", "_previous_change"
|
127
|
+
attribute_method_suffix "_previously_changed?", "_previous_change", "_previously_was"
|
131
128
|
attribute_method_affix prefix: "restore_", suffix: "!"
|
129
|
+
attribute_method_affix prefix: "clear_", suffix: "_change"
|
132
130
|
end
|
133
131
|
|
134
132
|
def initialize_dup(other) # :nodoc:
|
@@ -141,25 +139,29 @@ module ActiveModel
|
|
141
139
|
@mutations_from_database = nil
|
142
140
|
end
|
143
141
|
|
144
|
-
|
142
|
+
def as_json(options = {}) # :nodoc:
|
143
|
+
options[:except] = [options[:except], "mutations_from_database"].flatten
|
144
|
+
super(options)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Clears dirty data and moves +changes+ to +previous_changes+ and
|
145
148
|
# +mutations_from_database+ to +mutations_before_last_save+ respectively.
|
146
149
|
def changes_applied
|
147
150
|
unless defined?(@attributes)
|
148
|
-
|
151
|
+
mutations_from_database.finalize_changes
|
149
152
|
end
|
150
153
|
@mutations_before_last_save = mutations_from_database
|
151
|
-
@attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
|
152
154
|
forget_attribute_assignments
|
153
155
|
@mutations_from_database = nil
|
154
156
|
end
|
155
157
|
|
156
|
-
# Returns +true+ if any of the attributes
|
158
|
+
# Returns +true+ if any of the attributes has unsaved changes, +false+ otherwise.
|
157
159
|
#
|
158
160
|
# person.changed? # => false
|
159
161
|
# person.name = 'bob'
|
160
162
|
# person.changed? # => true
|
161
163
|
def changed?
|
162
|
-
|
164
|
+
mutations_from_database.any_changes?
|
163
165
|
end
|
164
166
|
|
165
167
|
# Returns an array with the name of the attributes with unsaved changes.
|
@@ -168,42 +170,42 @@ module ActiveModel
|
|
168
170
|
# person.name = 'bob'
|
169
171
|
# person.changed # => ["name"]
|
170
172
|
def changed
|
171
|
-
|
173
|
+
mutations_from_database.changed_attribute_names
|
172
174
|
end
|
173
175
|
|
174
|
-
#
|
175
|
-
def attribute_changed?(
|
176
|
-
|
177
|
-
(to == OPTION_NOT_GIVEN || to == _read_attribute(attr)) &&
|
178
|
-
(from == OPTION_NOT_GIVEN || from == changed_attributes[attr])
|
176
|
+
# Dispatch target for <tt>*_changed?</tt> attribute methods.
|
177
|
+
def attribute_changed?(attr_name, **options) # :nodoc:
|
178
|
+
mutations_from_database.changed?(attr_name.to_s, **options)
|
179
179
|
end
|
180
180
|
|
181
|
-
#
|
182
|
-
def attribute_was(
|
183
|
-
|
181
|
+
# Dispatch target for <tt>*_was</tt> attribute methods.
|
182
|
+
def attribute_was(attr_name) # :nodoc:
|
183
|
+
mutations_from_database.original_value(attr_name.to_s)
|
184
184
|
end
|
185
185
|
|
186
|
-
#
|
187
|
-
def attribute_previously_changed?(
|
188
|
-
|
186
|
+
# Dispatch target for <tt>*_previously_changed?</tt> attribute methods.
|
187
|
+
def attribute_previously_changed?(attr_name, **options) # :nodoc:
|
188
|
+
mutations_before_last_save.changed?(attr_name.to_s, **options)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Dispatch target for <tt>*_previously_was</tt> attribute methods.
|
192
|
+
def attribute_previously_was(attr_name) # :nodoc:
|
193
|
+
mutations_before_last_save.original_value(attr_name.to_s)
|
189
194
|
end
|
190
195
|
|
191
196
|
# Restore all previous data of the provided attributes.
|
192
|
-
def restore_attributes(
|
193
|
-
|
197
|
+
def restore_attributes(attr_names = changed)
|
198
|
+
attr_names.each { |attr_name| restore_attribute!(attr_name) }
|
194
199
|
end
|
195
200
|
|
196
201
|
# Clears all dirty data: current changes and previous changes.
|
197
202
|
def clear_changes_information
|
198
|
-
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
199
203
|
@mutations_before_last_save = nil
|
200
|
-
@attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
|
201
204
|
forget_attribute_assignments
|
202
205
|
@mutations_from_database = nil
|
203
206
|
end
|
204
207
|
|
205
208
|
def clear_attribute_changes(attr_names)
|
206
|
-
attributes_changed_by_setter.except!(*attr_names)
|
207
209
|
attr_names.each do |attr_name|
|
208
210
|
clear_attribute_change(attr_name)
|
209
211
|
end
|
@@ -216,13 +218,7 @@ module ActiveModel
|
|
216
218
|
# person.name = 'robert'
|
217
219
|
# person.changed_attributes # => {"name" => "bob"}
|
218
220
|
def changed_attributes
|
219
|
-
|
220
|
-
# multiple times when it is known that the computed value cannot change.
|
221
|
-
if defined?(@cached_changed_attributes)
|
222
|
-
@cached_changed_attributes
|
223
|
-
else
|
224
|
-
attributes_changed_by_setter.reverse_merge(mutations_from_database.changed_values).freeze
|
225
|
-
end
|
221
|
+
mutations_from_database.changed_values
|
226
222
|
end
|
227
223
|
|
228
224
|
# Returns a hash of changed attributes indicating their original
|
@@ -232,9 +228,7 @@ module ActiveModel
|
|
232
228
|
# person.name = 'bob'
|
233
229
|
# person.changes # => { "name" => ["bill", "bob"] }
|
234
230
|
def changes
|
235
|
-
|
236
|
-
ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
|
237
|
-
end
|
231
|
+
mutations_from_database.changes
|
238
232
|
end
|
239
233
|
|
240
234
|
# Returns a hash of attributes that were changed before the model was saved.
|
@@ -244,27 +238,23 @@ module ActiveModel
|
|
244
238
|
# person.save
|
245
239
|
# person.previous_changes # => {"name" => ["bob", "robert"]}
|
246
240
|
def previous_changes
|
247
|
-
|
248
|
-
@previously_changed.merge(mutations_before_last_save.changes)
|
241
|
+
mutations_before_last_save.changes
|
249
242
|
end
|
250
243
|
|
251
244
|
def attribute_changed_in_place?(attr_name) # :nodoc:
|
252
|
-
mutations_from_database.changed_in_place?(attr_name)
|
245
|
+
mutations_from_database.changed_in_place?(attr_name.to_s)
|
253
246
|
end
|
254
247
|
|
255
248
|
private
|
256
249
|
def clear_attribute_change(attr_name)
|
257
|
-
mutations_from_database.forget_change(attr_name)
|
250
|
+
mutations_from_database.forget_change(attr_name.to_s)
|
258
251
|
end
|
259
252
|
|
260
253
|
def mutations_from_database
|
261
|
-
unless defined?(@mutations_from_database)
|
262
|
-
@mutations_from_database = nil
|
263
|
-
end
|
264
254
|
@mutations_from_database ||= if defined?(@attributes)
|
265
255
|
ActiveModel::AttributeMutationTracker.new(@attributes)
|
266
256
|
else
|
267
|
-
|
257
|
+
ActiveModel::ForcedMutationTracker.new(self)
|
268
258
|
end
|
269
259
|
end
|
270
260
|
|
@@ -276,68 +266,28 @@ module ActiveModel
|
|
276
266
|
@mutations_before_last_save ||= ActiveModel::NullMutationTracker.instance
|
277
267
|
end
|
278
268
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
ensure
|
283
|
-
clear_changed_attributes_cache
|
284
|
-
end
|
285
|
-
|
286
|
-
def clear_changed_attributes_cache
|
287
|
-
remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
|
288
|
-
end
|
289
|
-
|
290
|
-
# Returns +true+ if attr_name is changed, +false+ otherwise.
|
291
|
-
def changes_include?(attr_name)
|
292
|
-
attributes_changed_by_setter.include?(attr_name) || mutations_from_database.changed?(attr_name)
|
293
|
-
end
|
294
|
-
alias attribute_changed_by_setter? changes_include?
|
295
|
-
|
296
|
-
# Returns +true+ if attr_name were changed before the model was saved,
|
297
|
-
# +false+ otherwise.
|
298
|
-
def previous_changes_include?(attr_name)
|
299
|
-
previous_changes.include?(attr_name)
|
269
|
+
# Dispatch target for <tt>*_change</tt> attribute methods.
|
270
|
+
def attribute_change(attr_name)
|
271
|
+
mutations_from_database.change_to_attribute(attr_name.to_s)
|
300
272
|
end
|
301
273
|
|
302
|
-
#
|
303
|
-
def
|
304
|
-
|
274
|
+
# Dispatch target for <tt>*_previous_change</tt> attribute methods.
|
275
|
+
def attribute_previous_change(attr_name)
|
276
|
+
mutations_before_last_save.change_to_attribute(attr_name.to_s)
|
305
277
|
end
|
306
278
|
|
307
|
-
#
|
308
|
-
def
|
309
|
-
|
279
|
+
# Dispatch target for <tt>*_will_change!</tt> attribute methods.
|
280
|
+
def attribute_will_change!(attr_name)
|
281
|
+
mutations_from_database.force_change(attr_name.to_s)
|
310
282
|
end
|
311
283
|
|
312
|
-
#
|
313
|
-
def
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
rescue TypeError, NoMethodError
|
319
|
-
end
|
320
|
-
|
321
|
-
set_attribute_was(attr, value)
|
284
|
+
# Dispatch target for <tt>restore_*!</tt> attribute methods.
|
285
|
+
def restore_attribute!(attr_name)
|
286
|
+
attr_name = attr_name.to_s
|
287
|
+
if attribute_changed?(attr_name)
|
288
|
+
__send__("#{attr_name}=", attribute_was(attr_name))
|
289
|
+
clear_attribute_change(attr_name)
|
322
290
|
end
|
323
|
-
mutations_from_database.force_change(attr)
|
324
|
-
end
|
325
|
-
|
326
|
-
# Handles <tt>restore_*!</tt> for +method_missing+.
|
327
|
-
def restore_attribute!(attr)
|
328
|
-
if attribute_changed?(attr)
|
329
|
-
__send__("#{attr}=", changed_attributes[attr])
|
330
|
-
clear_attribute_changes([attr])
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
def attributes_changed_by_setter
|
335
|
-
@attributes_changed_by_setter ||= ActiveSupport::HashWithIndifferentAccess.new
|
336
|
-
end
|
337
|
-
|
338
|
-
# Force an attribute to have a particular "before" value
|
339
|
-
def set_attribute_was(attr, old_value)
|
340
|
-
attributes_changed_by_setter[attr] = old_value
|
341
291
|
end
|
342
292
|
end
|
343
293
|
end
|