activemodel 7.0.3.1 → 6.1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -84
  3. data/MIT-LICENSE +0 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_model/attribute.rb +0 -4
  6. data/lib/active_model/attribute_methods.rb +82 -67
  7. data/lib/active_model/attribute_set/builder.rb +10 -1
  8. data/lib/active_model/attribute_set.rb +1 -4
  9. data/lib/active_model/attributes.rb +12 -15
  10. data/lib/active_model/callbacks.rb +3 -3
  11. data/lib/active_model/conversion.rb +2 -2
  12. data/lib/active_model/dirty.rb +4 -5
  13. data/lib/active_model/error.rb +2 -2
  14. data/lib/active_model/errors.rb +248 -55
  15. data/lib/active_model/gem_version.rb +4 -4
  16. data/lib/active_model/locale/en.yml +0 -1
  17. data/lib/active_model/model.rb +59 -6
  18. data/lib/active_model/naming.rb +8 -15
  19. data/lib/active_model/secure_password.rb +1 -1
  20. data/lib/active_model/serialization.rb +2 -6
  21. data/lib/active_model/translation.rb +2 -2
  22. data/lib/active_model/type/date.rb +1 -1
  23. data/lib/active_model/type/helpers/numeric.rb +1 -9
  24. data/lib/active_model/type/helpers/time_value.rb +3 -3
  25. data/lib/active_model/type/integer.rb +1 -4
  26. data/lib/active_model/type/registry.rb +38 -8
  27. data/lib/active_model/type/time.rb +1 -1
  28. data/lib/active_model/type.rb +6 -6
  29. data/lib/active_model/validations/absence.rb +2 -2
  30. data/lib/active_model/validations/acceptance.rb +1 -1
  31. data/lib/active_model/validations/callbacks.rb +1 -1
  32. data/lib/active_model/validations/clusivity.rb +1 -1
  33. data/lib/active_model/validations/confirmation.rb +5 -5
  34. data/lib/active_model/validations/exclusion.rb +3 -3
  35. data/lib/active_model/validations/format.rb +1 -1
  36. data/lib/active_model/validations/inclusion.rb +3 -3
  37. data/lib/active_model/validations/length.rb +2 -2
  38. data/lib/active_model/validations/numericality.rb +22 -29
  39. data/lib/active_model/validations/presence.rb +1 -1
  40. data/lib/active_model/validations/validates.rb +3 -3
  41. data/lib/active_model/validations/with.rb +4 -4
  42. data/lib/active_model/validations.rb +12 -12
  43. data/lib/active_model/validator.rb +5 -5
  44. data/lib/active_model/version.rb +1 -1
  45. data/lib/active_model.rb +0 -1
  46. metadata +9 -12
  47. data/lib/active_model/api.rb +0 -99
  48. data/lib/active_model/validations/comparability.rb +0 -29
  49. data/lib/active_model/validations/comparison.rb +0 -82
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2845eb500d658cf1e666a48cbba3e2e96894a92cec97a1dfaff8cf4883f3909a
4
- data.tar.gz: 0c6f8b7de47368da12af30e2c0a03c7b0022ae08a40a316a018899481801804c
3
+ metadata.gz: d2706dba9ce622452b7e50ce891f67af2eb27717b37ed0279148ad064ef1c6ce
4
+ data.tar.gz: 16856f088c941213ab4647ec5041995c8eaadf1ff230e76a98eb4ddd34ce0856
5
5
  SHA512:
6
- metadata.gz: 4f23d0bc2105d523983d1b72915adf3ff5ec3cd7bbd690bb15c51bfb048d6b2f14c4e5760ca3a0eaae409be1ac5c643411d5a60ada8812d8116ae16d540ca249
7
- data.tar.gz: bfa2d89dc9f7674eeb49112b427bebd8e1d72f5f316b7103905f426ebf7c4901c9682db5c7851d495b88721182e71a9c4b4ec6898a352fb253fcd4d2c1dbbe3b
6
+ metadata.gz: 8b234b3e5a18c9831af5d029ee72d91481c4a82f539f4184ca9d274c0d3d13529e8fa3e71ac34a2caaa614b74db98da21432914e2d04212a9232b61b6e585ee7
7
+ data.tar.gz: 10c2d8a7571fa6340749ccfef90e1f54b423a6f1c259adf6961c0043ccfea04e5400e7f76b733e8d0a0f4d81dfe3c233895aeddc444a28cd87fa01a4ec6e1894
data/CHANGELOG.md CHANGED
@@ -1,188 +1,207 @@
1
- ## Rails 7.0.3.1 (July 12, 2022) ##
1
+ ## Rails 6.1.7.1 (January 17, 2023) ##
2
2
 
3
3
  * No changes.
4
4
 
5
5
 
6
- ## Rails 7.0.3 (May 09, 2022) ##
6
+ ## Rails 6.1.7 (September 09, 2022) ##
7
7
 
8
8
  * No changes.
9
9
 
10
10
 
11
- ## Rails 7.0.2.4 (April 26, 2022) ##
11
+ ## Rails 6.1.6.1 (July 12, 2022) ##
12
12
 
13
13
  * No changes.
14
14
 
15
15
 
16
- ## Rails 7.0.2.3 (March 08, 2022) ##
16
+ ## Rails 6.1.6 (May 09, 2022) ##
17
17
 
18
18
  * No changes.
19
19
 
20
20
 
21
- ## Rails 7.0.2.2 (February 11, 2022) ##
21
+ ## Rails 6.1.5.1 (April 26, 2022) ##
22
22
 
23
23
  * No changes.
24
24
 
25
25
 
26
- ## Rails 7.0.2.1 (February 11, 2022) ##
26
+ ## Rails 6.1.5 (March 09, 2022) ##
27
27
 
28
- * No changes.
28
+ * Clear secure password cache if password is set to `nil`
29
29
 
30
+ Before:
30
31
 
31
- ## Rails 7.0.2 (February 08, 2022) ##
32
+ user.password = 'something'
33
+ user.password = nil
32
34
 
33
- * Use different cache namespace for proxy calls
35
+ user.password # => 'something'
34
36
 
35
- Models can currently have different attribute bodies for the same method
36
- names, leading to conflicts. Adding a new namespace `:active_model_proxy`
37
- fixes the issue.
37
+ Now:
38
38
 
39
- *Chris Salzberg*
39
+ user.password = 'something'
40
+ user.password = nil
40
41
 
42
+ user.password # => nil
41
43
 
42
- ## Rails 7.0.1 (January 06, 2022) ##
44
+ *Markus Doits*
43
45
 
44
- * No changes.
46
+ * Fix delegation in `ActiveModel::Type::Registry#lookup` and `ActiveModel::Type.lookup`
45
47
 
48
+ Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
46
49
 
47
- ## Rails 7.0.0 (December 15, 2021) ##
50
+ *Benoit Daloze*
48
51
 
49
- * No changes.
52
+ * Fix `to_json` after `changes_applied` for `ActiveModel::Dirty` object.
50
53
 
54
+ *Ryuta Kamizono*
51
55
 
52
- ## Rails 7.0.0.rc3 (December 14, 2021) ##
56
+
57
+ ## Rails 6.1.4.7 (March 08, 2022) ##
53
58
 
54
59
  * No changes.
55
60
 
56
61
 
57
- ## Rails 7.0.0.rc2 (December 14, 2021) ##
62
+ ## Rails 6.1.4.6 (February 11, 2022) ##
58
63
 
59
64
  * No changes.
60
65
 
61
- ## Rails 7.0.0.rc1 (December 06, 2021) ##
62
66
 
63
- * Remove support to Marshal load Rails 5.x `ActiveModel::AttributeSet` format.
67
+ ## Rails 6.1.4.5 (February 11, 2022) ##
64
68
 
65
- *Rafael Mendonça França*
69
+ * No changes.
66
70
 
67
- * Remove support to Marshal and YAML load Rails 5.x error format.
68
71
 
69
- *Rafael Mendonça França*
72
+ ## Rails 6.1.4.4 (December 15, 2021) ##
70
73
 
71
- * Remove deprecated support to use `[]=` in `ActiveModel::Errors#messages`.
74
+ * No changes.
72
75
 
73
- *Rafael Mendonça França*
74
76
 
75
- * Remove deprecated support to `delete` errors from `ActiveModel::Errors#messages`.
77
+ ## Rails 6.1.4.3 (December 14, 2021) ##
76
78
 
77
- *Rafael Mendonça França*
79
+ * No changes.
78
80
 
79
- * Remove deprecated support to `clear` errors from `ActiveModel::Errors#messages`.
80
81
 
81
- *Rafael Mendonça França*
82
+ ## Rails 6.1.4.2 (December 14, 2021) ##
82
83
 
83
- * Remove deprecated support concat errors to `ActiveModel::Errors#messages`.
84
+ * No changes.
84
85
 
85
- *Rafael Mendonça França*
86
86
 
87
- * Remove deprecated `ActiveModel::Errors#to_xml`.
87
+ ## Rails 6.1.4.1 (August 19, 2021) ##
88
88
 
89
- *Rafael Mendonça França*
89
+ * No changes.
90
90
 
91
- * Remove deprecated `ActiveModel::Errors#keys`.
92
91
 
93
- *Rafael Mendonça França*
92
+ ## Rails 6.1.4 (June 24, 2021) ##
94
93
 
95
- * Remove deprecated `ActiveModel::Errors#values`.
94
+ * Fix `to_json` for `ActiveModel::Dirty` object.
96
95
 
97
- *Rafael Mendonça França*
96
+ Exclude +mutations_from_database+ attribute from json as it lead to recursion.
98
97
 
99
- * Remove deprecated `ActiveModel::Errors#slice!`.
98
+ *Anil Maurya*
100
99
 
101
- *Rafael Mendonça França*
102
100
 
103
- * Remove deprecated `ActiveModel::Errors#to_h`.
101
+ ## Rails 6.1.3.2 (May 05, 2021) ##
104
102
 
105
- *Rafael Mendonça França*
103
+ * No changes.
106
104
 
107
- * Remove deprecated enumeration of `ActiveModel::Errors` instances as a Hash.
108
105
 
109
- *Rafael Mendonça França*
106
+ ## Rails 6.1.3.1 (March 26, 2021) ##
110
107
 
111
- * Clear secure password cache if password is set to `nil`
108
+ * No changes.
112
109
 
113
- Before:
114
110
 
115
- user.password = 'something'
116
- user.password = nil
111
+ ## Rails 6.1.3 (February 17, 2021) ##
117
112
 
118
- user.password # => 'something'
113
+ * No changes.
119
114
 
120
- Now:
121
115
 
122
- user.password = 'something'
123
- user.password = nil
116
+ ## Rails 6.1.2.1 (February 10, 2021) ##
124
117
 
125
- user.password # => nil
118
+ * No changes.
126
119
 
127
- *Markus Doits*
128
120
 
129
- ## Rails 7.0.0.alpha2 (September 15, 2021) ##
121
+ ## Rails 6.1.2 (February 09, 2021) ##
130
122
 
131
123
  * No changes.
132
124
 
133
125
 
134
- ## Rails 7.0.0.alpha1 (September 15, 2021) ##
126
+ ## Rails 6.1.1 (January 07, 2021) ##
135
127
 
136
- * Introduce `ActiveModel::API`.
128
+ * No changes.
137
129
 
138
- Make `ActiveModel::API` the minimum API to talk with Action Pack and Action View.
139
- This will allow adding more functionality to `ActiveModel::Model`.
140
130
 
141
- *Petrik de Heus*, *Nathaniel Watts*
131
+ ## Rails 6.1.0 (December 09, 2020) ##
142
132
 
143
- * Fix dirty check for Float::NaN and BigDecimal::NaN.
133
+ * Pass in `base` instead of `base_class` to Error.human_attribute_name
144
134
 
145
- Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720)
146
- and can't be compared with `==`.
135
+ This is useful in cases where the `human_attribute_name` method depends
136
+ on other attributes' values of the class under validation to derive what the
137
+ attribute name should be.
147
138
 
148
- *Marcelo Lauxen*
139
+ *Filipe Sabella*
149
140
 
150
- * Fix `to_json` for `ActiveModel::Dirty` object.
141
+ * Deprecate marshalling load from legacy attributes format.
151
142
 
152
- Exclude `mutations_from_database` attribute from json as it lead to recursion.
143
+ *Ryuta Kamizono*
153
144
 
154
- *Anil Maurya*
145
+ * `*_previously_changed?` accepts `:from` and `:to` keyword arguments like `*_changed?`.
155
146
 
156
- * Add `ActiveModel::AttributeSet#values_for_database`.
147
+ topic.update!(status: :archived)
148
+ topic.status_previously_changed?(from: "active", to: "archived")
149
+ # => true
157
150
 
158
- Returns attributes with values for assignment to the database.
151
+ *George Claghorn*
159
152
 
160
- *Chris Salzberg*
153
+ * Raise FrozenError when trying to write attributes that aren't backed by the database on an object that is frozen:
161
154
 
162
- * Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup.
155
+ class Animal
156
+ include ActiveModel::Attributes
157
+ attribute :age
158
+ end
163
159
 
164
- Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
160
+ animal = Animal.new
161
+ animal.freeze
162
+ animal.age = 25 # => FrozenError, "can't modify a frozen Animal"
165
163
 
166
- *Benoit Daloze*
164
+ *Josh Brody*
165
+
166
+ * Add `*_previously_was` attribute methods when dirty tracking. Example:
167
167
 
168
- * Cache and re-use generated attribute methods.
168
+ pirate.update(catchphrase: "Ahoy!")
169
+ pirate.previous_changes["catchphrase"] # => ["Thar She Blows!", "Ahoy!"]
170
+ pirate.catchphrase_previously_was # => "Thar She Blows!"
169
171
 
170
- Generated methods with identical implementations will now share their instruction sequences
171
- leading to reduced memory retention, and slightly faster load time.
172
+ *DHH*
172
173
 
173
- *Jean Boussier*
174
+ * Encapsulate each validation error as an Error object.
174
175
 
175
- * Add `in: range` parameter to `numericality` validator.
176
+ The `ActiveModel`’s `errors` collection is now an array of these Error
177
+ objects, instead of messages/details hash.
176
178
 
177
- *Michal Papis*
179
+ For each of these `Error` object, its `message` and `full_message` methods
180
+ are for generating error messages. Its `details` method would return error’s
181
+ extra parameters, found in the original `details` hash.
178
182
 
179
- * Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
180
- `plural`, `route_key` and `singular_route_key` values.
183
+ The change tries its best at maintaining backward compatibility, however
184
+ some edge cases won’t be covered, like `errors#first` will return `ActiveModel::Error` and manipulating
185
+ `errors.messages` and `errors.details` hashes directly will have no effect. Moving forward,
186
+ please convert those direct manipulations to use provided API methods instead.
187
+ Please note that `errors#add` now accepts `options` as keyword arguments instead of `Hash` which
188
+ introduced a change in Ruby 3 to [keyword arguments][kwargs-ann].
181
189
 
182
- *Lukas Pokorny*
190
+ [kwargs-ann]: https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
183
191
 
184
- * Make ActiveModel::Errors#inspect slimmer for readability
192
+ The list of deprecated methods and their planned future behavioral changes at the next major release are:
193
+
194
+ * `errors#slice!` will be removed.
195
+ * `errors#each` with the `key, value` two-arguments block will stop working, while the `error` single-argument block would return `Error` object.
196
+ * `errors#values` will be removed.
197
+ * `errors#keys` will be removed.
198
+ * `errors#to_xml` will be removed.
199
+ * `errors#to_h` will be removed, and can be replaced with `errors#to_hash`.
200
+ * Manipulating `errors` itself as a hash will have no effect (e.g. `errors[:foo] = 'bar'`).
201
+ * Manipulating the hash returned by `errors#messages` (e.g. `errors.messages[:foo] = 'bar'`) will have no effect.
202
+ * Manipulating the hash returned by `errors#details` (e.g. `errors.details[:foo].clear`) will have no effect.
185
203
 
186
204
  *lulalala*
187
205
 
188
- Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activemodel/CHANGELOG.md) for previous changes.
206
+
207
+ Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activemodel/CHANGELOG.md) for previous changes.
data/MIT-LICENSE CHANGED
@@ -18,4 +18,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
-
data/README.rdoc CHANGED
@@ -16,10 +16,10 @@ Model solves this by defining an explicit API. You can read more about the
16
16
  API in <tt>ActiveModel::Lint::Tests</tt>.
17
17
 
18
18
  Active Model provides a default module that implements the basic API required
19
- to integrate with Action Pack out of the box: <tt>ActiveModel::API</tt>.
19
+ to integrate with Action Pack out of the box: <tt>ActiveModel::Model</tt>.
20
20
 
21
21
  class Person
22
- include ActiveModel::API
22
+ include ActiveModel::Model
23
23
 
24
24
  attr_accessor :name, :age
25
25
  validates_presence_of :name
@@ -32,7 +32,7 @@ to integrate with Action Pack out of the box: <tt>ActiveModel::API</tt>.
32
32
 
33
33
  It includes model name introspections, conversions, translations and
34
34
  validations, resulting in a class suitable to be used with Action Pack.
35
- See <tt>ActiveModel::API</tt> for more examples.
35
+ See <tt>ActiveModel::Model</tt> for more examples.
36
36
 
37
37
  Active Model also provides the following functionality to have ORM-like
38
38
  behavior out of the box:
@@ -56,10 +56,6 @@ module ActiveModel
56
56
  type.serialize(value)
57
57
  end
58
58
 
59
- def serializable?(&block)
60
- type.serializable?(value, &block)
61
- end
62
-
63
59
  def changed?
64
60
  changed_from_assignment? || changed_in_place?
65
61
  end
@@ -67,7 +67,6 @@ module ActiveModel
67
67
 
68
68
  NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
69
69
  CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
70
- FORWARD_PARAMETERS = "*args"
71
70
 
72
71
  included do
73
72
  class_attribute :attribute_aliases, instance_writer: false, default: {}
@@ -106,8 +105,8 @@ module ActiveModel
106
105
  # person.name # => "Bob"
107
106
  # person.clear_name
108
107
  # person.name # => nil
109
- def attribute_method_prefix(*prefixes, parameters: nil)
110
- self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new(prefix: prefix, parameters: parameters) }
108
+ def attribute_method_prefix(*prefixes)
109
+ self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new prefix: prefix }
111
110
  undefine_attribute_methods
112
111
  end
113
112
 
@@ -141,8 +140,8 @@ module ActiveModel
141
140
  # person.name = 'Bob'
142
141
  # person.name # => "Bob"
143
142
  # person.name_short? # => true
144
- def attribute_method_suffix(*suffixes, parameters: nil)
145
- self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new(suffix: suffix, parameters: parameters) }
143
+ def attribute_method_suffix(*suffixes)
144
+ self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new suffix: suffix }
146
145
  undefine_attribute_methods
147
146
  end
148
147
 
@@ -178,7 +177,7 @@ module ActiveModel
178
177
  # person.reset_name_to_default!
179
178
  # person.name # => 'Default Name'
180
179
  def attribute_method_affix(*affixes)
181
- self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new(**affix) }
180
+ self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new prefix: affix[:prefix], suffix: affix[:suffix] }
182
181
  undefine_attribute_methods
183
182
  end
184
183
 
@@ -208,33 +207,11 @@ module ActiveModel
208
207
  # person.nickname_short? # => true
209
208
  def alias_attribute(new_name, old_name)
210
209
  self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
211
- ActiveSupport::CodeGenerator.batch(self, __FILE__, __LINE__) do |code_generator|
210
+ CodeGenerator.batch(self, __FILE__, __LINE__) do |owner|
212
211
  attribute_method_matchers.each do |matcher|
213
- method_name = matcher.method_name(new_name).to_s
214
- target_name = matcher.method_name(old_name).to_s
215
- parameters = matcher.parameters
216
-
217
- mangled_name = target_name
218
- unless NAME_COMPILABLE_REGEXP.match?(target_name)
219
- mangled_name = "__temp__#{target_name.unpack1("h*")}"
220
- end
221
-
222
- code_generator.define_cached_method(method_name, as: mangled_name, namespace: :alias_attribute) do |batch|
223
- body = if CALL_COMPILABLE_REGEXP.match?(target_name)
224
- "self.#{target_name}(#{parameters || ''})"
225
- else
226
- call_args = [":'#{target_name}'"]
227
- call_args << parameters if parameters
228
- "send(#{call_args.join(", ")})"
229
- end
230
-
231
- modifier = matcher.parameters == FORWARD_PARAMETERS ? "ruby2_keywords " : ""
232
-
233
- batch <<
234
- "#{modifier}def #{mangled_name}(#{parameters || ''})" <<
235
- body <<
236
- "end"
237
- end
212
+ matcher_new = matcher.method_name(new_name).to_s
213
+ matcher_old = matcher.method_name(old_name).to_s
214
+ define_proxy_call false, owner, matcher_new, matcher_old
238
215
  end
239
216
  end
240
217
  end
@@ -253,7 +230,7 @@ module ActiveModel
253
230
  # <tt>ActiveModel::AttributeMethods</tt>.
254
231
  #
255
232
  # To use, pass attribute names (as strings or symbols). Be sure to declare
256
- # +define_attribute_methods+ after you define any prefix, suffix, or affix
233
+ # +define_attribute_methods+ after you define any prefix, suffix or affix
257
234
  # methods, or they will not hook in.
258
235
  #
259
236
  # class Person
@@ -274,7 +251,7 @@ module ActiveModel
274
251
  # end
275
252
  # end
276
253
  def define_attribute_methods(*attr_names)
277
- ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
254
+ CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
278
255
  attr_names.flatten.each { |attr_name| define_attribute_method(attr_name, _owner: owner) }
279
256
  end
280
257
  end
@@ -309,7 +286,7 @@ module ActiveModel
309
286
  # person.name # => "Bob"
310
287
  # person.name_short? # => true
311
288
  def define_attribute_method(attr_name, _owner: generated_attribute_methods)
312
- ActiveSupport::CodeGenerator.batch(_owner, __FILE__, __LINE__) do |owner|
289
+ CodeGenerator.batch(_owner, __FILE__, __LINE__) do |owner|
313
290
  attribute_method_matchers.each do |matcher|
314
291
  method_name = matcher.method_name(attr_name)
315
292
 
@@ -319,7 +296,7 @@ module ActiveModel
319
296
  if respond_to?(generate_method, true)
320
297
  send(generate_method, attr_name.to_s, owner: owner)
321
298
  else
322
- define_proxy_call(owner, method_name, matcher.target, matcher.parameters, attr_name.to_s, namespace: :active_model_proxy)
299
+ define_proxy_call true, owner, method_name, matcher.target, attr_name.to_s
323
300
  end
324
301
  end
325
302
  end
@@ -358,6 +335,46 @@ module ActiveModel
358
335
  end
359
336
 
360
337
  private
338
+ class CodeGenerator
339
+ class << self
340
+ def batch(owner, path, line)
341
+ if owner.is_a?(CodeGenerator)
342
+ yield owner
343
+ else
344
+ instance = new(owner, path, line)
345
+ result = yield instance
346
+ instance.execute
347
+ result
348
+ end
349
+ end
350
+ end
351
+
352
+ def initialize(owner, path, line)
353
+ @owner = owner
354
+ @path = path
355
+ @line = line
356
+ @sources = ["# frozen_string_literal: true\n"]
357
+ @renames = {}
358
+ end
359
+
360
+ def <<(source_line)
361
+ @sources << source_line
362
+ end
363
+
364
+ def rename_method(old_name, new_name)
365
+ @renames[old_name] = new_name
366
+ end
367
+
368
+ def execute
369
+ @owner.module_eval(@sources.join(";"), @path, @line - 1)
370
+ @renames.each do |old_name, new_name|
371
+ @owner.alias_method new_name, old_name
372
+ @owner.undef_method old_name
373
+ end
374
+ end
375
+ end
376
+ private_constant :CodeGenerator
377
+
361
378
  def generated_attribute_methods
362
379
  @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
363
380
  end
@@ -381,48 +398,42 @@ module ActiveModel
381
398
 
382
399
  def attribute_method_matchers_matching(method_name)
383
400
  attribute_method_matchers_cache.compute_if_absent(method_name) do
384
- attribute_method_matchers.filter_map { |matcher| matcher.match(method_name) }
401
+ attribute_method_matchers.map { |matcher| matcher.match(method_name) }.compact
385
402
  end
386
403
  end
387
404
 
388
405
  # Define a method `name` in `mod` that dispatches to `send`
389
- # using the given `extra` args. This falls back on `send`
390
- # if the called name cannot be compiled.
391
- def define_proxy_call(code_generator, name, target, parameters, *call_args, namespace:)
392
- mangled_name = name
393
- unless NAME_COMPILABLE_REGEXP.match?(name)
394
- mangled_name = "__temp__#{name.unpack1("h*")}"
406
+ # using the given `extra` args. This falls back on `define_method`
407
+ # and `send` if the given names cannot be compiled.
408
+ def define_proxy_call(include_private, code_generator, name, target, *extra)
409
+ defn = if NAME_COMPILABLE_REGEXP.match?(name)
410
+ "def #{name}(*args)"
411
+ else
412
+ "define_method(:'#{name}') do |*args|"
395
413
  end
396
414
 
397
- code_generator.define_cached_method(name, as: mangled_name, namespace: namespace) do |batch|
398
- call_args.map!(&:inspect)
399
- call_args << parameters if parameters
400
-
401
- body = if CALL_COMPILABLE_REGEXP.match?(target)
402
- "self.#{target}(#{call_args.join(", ")})"
403
- else
404
- call_args.unshift(":'#{target}'")
405
- "send(#{call_args.join(", ")})"
406
- end
415
+ extra = (extra.map!(&:inspect) << "*args").join(", ")
407
416
 
408
- modifier = parameters == FORWARD_PARAMETERS ? "ruby2_keywords " : ""
409
-
410
- batch <<
411
- "#{modifier}def #{mangled_name}(#{parameters || ''})" <<
412
- body <<
413
- "end"
417
+ body = if CALL_COMPILABLE_REGEXP.match?(target)
418
+ "#{"self." unless include_private}#{target}(#{extra})"
419
+ else
420
+ "send(:'#{target}', #{extra})"
414
421
  end
422
+
423
+ code_generator <<
424
+ defn <<
425
+ body <<
426
+ "end" <<
427
+ "ruby2_keywords(:'#{name}') if respond_to?(:ruby2_keywords, true)"
415
428
  end
416
429
 
417
- class AttributeMethodMatcher # :nodoc:
418
- attr_reader :prefix, :suffix, :target, :parameters
430
+ class AttributeMethodMatcher #:nodoc:
431
+ attr_reader :prefix, :suffix, :target
419
432
 
420
433
  AttributeMethodMatch = Struct.new(:target, :attr_name)
421
434
 
422
- def initialize(prefix: "", suffix: "", parameters: nil)
423
- @prefix = prefix
424
- @suffix = suffix
425
- @parameters = parameters.nil? ? FORWARD_PARAMETERS : parameters
435
+ def initialize(options = {})
436
+ @prefix, @suffix = options.fetch(:prefix, ""), options.fetch(:suffix, "")
426
437
  @regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
427
438
  @target = "#{@prefix}attribute#{@suffix}"
428
439
  @method_name = "#{prefix}%s#{suffix}"
@@ -458,7 +469,7 @@ module ActiveModel
458
469
  match ? attribute_missing(match, *args, &block) : super
459
470
  end
460
471
  end
461
- ruby2_keywords(:method_missing)
472
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
462
473
 
463
474
  # +attribute_missing+ is like +method_missing+, but for attributes. When
464
475
  # +method_missing+ is called we check to see if there is a matching
@@ -467,7 +478,6 @@ module ActiveModel
467
478
  def attribute_missing(match, *args, &block)
468
479
  __send__(match.target, match.attr_name, *args, &block)
469
480
  end
470
- ruby2_keywords(:attribute_missing)
471
481
 
472
482
  # A +Person+ instance with a +name+ attribute can ask
473
483
  # <tt>person.respond_to?(:name)</tt>, <tt>person.respond_to?(:name=)</tt>,
@@ -510,6 +520,10 @@ module ActiveModel
510
520
 
511
521
  # We want to generate the methods via module_eval rather than
512
522
  # define_method, because define_method is slower on dispatch.
523
+ # Evaluating many similar methods may use more memory as the instruction
524
+ # sequences are duplicated and cached (in MRI). define_method may
525
+ # be slower on dispatch, but if you're careful about the closure
526
+ # created, then define_method will consume much less memory.
513
527
  #
514
528
  # But sometimes the database might return columns with
515
529
  # characters that are not allowed in normal method names (like
@@ -533,6 +547,7 @@ module ActiveModel
533
547
  temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
534
548
  attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
535
549
  yield temp_method_name, attr_name_expr
550
+ owner.rename_method(temp_method_name, method_name)
536
551
  end
537
552
  end
538
553
  end
@@ -144,7 +144,16 @@ module ActiveModel
144
144
  end
145
145
 
146
146
  def marshal_load(values)
147
- initialize(*values)
147
+ if values.is_a?(Hash)
148
+ ActiveSupport::Deprecation.warn(<<~MSG)
149
+ Marshalling load from legacy attributes format is deprecated and will be removed in Rails 7.0.
150
+ MSG
151
+ empty_hash = {}.freeze
152
+ initialize(empty_hash, empty_hash, empty_hash, empty_hash, values)
153
+ @materialized = true
154
+ else
155
+ initialize(*values)
156
+ end
148
157
  end
149
158
 
150
159
  protected
@@ -25,10 +25,6 @@ module ActiveModel
25
25
  attributes.transform_values(&:value_before_type_cast)
26
26
  end
27
27
 
28
- def values_for_database
29
- attributes.transform_values(&:value_for_database)
30
- end
31
-
32
28
  def to_hash
33
29
  keys.index_with { |name| self[name].value }
34
30
  end
@@ -58,6 +54,7 @@ module ActiveModel
58
54
 
59
55
  def write_cast_value(name, value)
60
56
  @attributes[name] = self[name].with_cast_value(value)
57
+ value
61
58
  end
62
59
 
63
60
  def freeze