activemodel 6.0.3.2 → 6.1.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -182
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_model.rb +2 -1
  6. data/lib/active_model/attribute.rb +15 -14
  7. data/lib/active_model/attribute_assignment.rb +3 -4
  8. data/lib/active_model/attribute_methods.rb +74 -38
  9. data/lib/active_model/attribute_mutation_tracker.rb +8 -5
  10. data/lib/active_model/attribute_set.rb +18 -16
  11. data/lib/active_model/attribute_set/builder.rb +80 -13
  12. data/lib/active_model/attributes.rb +20 -24
  13. data/lib/active_model/dirty.rb +12 -4
  14. data/lib/active_model/error.rb +207 -0
  15. data/lib/active_model/errors.rb +316 -208
  16. data/lib/active_model/gem_version.rb +3 -3
  17. data/lib/active_model/lint.rb +1 -1
  18. data/lib/active_model/naming.rb +2 -2
  19. data/lib/active_model/nested_error.rb +22 -0
  20. data/lib/active_model/railtie.rb +1 -1
  21. data/lib/active_model/secure_password.rb +14 -14
  22. data/lib/active_model/serialization.rb +9 -6
  23. data/lib/active_model/serializers/json.rb +7 -0
  24. data/lib/active_model/type/date_time.rb +2 -2
  25. data/lib/active_model/type/float.rb +2 -0
  26. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +11 -7
  27. data/lib/active_model/type/helpers/numeric.rb +8 -3
  28. data/lib/active_model/type/helpers/time_value.rb +27 -17
  29. data/lib/active_model/type/helpers/timezone.rb +1 -1
  30. data/lib/active_model/type/immutable_string.rb +14 -10
  31. data/lib/active_model/type/integer.rb +11 -2
  32. data/lib/active_model/type/registry.rb +11 -4
  33. data/lib/active_model/type/string.rb +12 -2
  34. data/lib/active_model/type/value.rb +9 -1
  35. data/lib/active_model/validations.rb +6 -6
  36. data/lib/active_model/validations/absence.rb +1 -1
  37. data/lib/active_model/validations/acceptance.rb +1 -1
  38. data/lib/active_model/validations/clusivity.rb +5 -1
  39. data/lib/active_model/validations/confirmation.rb +2 -2
  40. data/lib/active_model/validations/exclusion.rb +1 -1
  41. data/lib/active_model/validations/format.rb +2 -2
  42. data/lib/active_model/validations/inclusion.rb +1 -1
  43. data/lib/active_model/validations/length.rb +2 -2
  44. data/lib/active_model/validations/numericality.rb +48 -41
  45. data/lib/active_model/validations/presence.rb +1 -1
  46. data/lib/active_model/validations/validates.rb +6 -4
  47. data/lib/active_model/validator.rb +7 -1
  48. metadata +13 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c72e24b51aad0dbe8fb35b8387aff6a1985d4a0b3c733b99d5df7e981946ccf1
4
- data.tar.gz: 8fee375a9932f90d17a75081ada0de6d5d02975847feca52e47ac02fa11af46d
3
+ metadata.gz: 1caa114ce8c604cb9ac388ef4ebbfd796f8ff253799322db9fa714193bca6821
4
+ data.tar.gz: 49f8fb7f47a3154022161dee9871d6aba46f7750b3381e690e37d7ff2663ef48
5
5
  SHA512:
6
- metadata.gz: 11377ef7fb923a77472eef17be41bde6d87f8e2edba93e0e098688182d60bcc9b9f32806394e462739d2deff8888eaf8f94bca7fb6d0e7e1f78307b48692f63e
7
- data.tar.gz: d1c959b3159ac159659cf001b086577c8279cbb9df7bdfbfac6a6775bdaea44f74284e98aecf5ee7316eb4ca346408dee6ef8c4f84ad211d04124a782af2e63a
6
+ metadata.gz: 26271099e8c36f89e1ee19fcb3469be914240ea887f15aba4d0de79b3db57f17d8467f523010330ba4f70e4df01d14398a6db7687df0a9be674f17ffd284be5d
7
+ data.tar.gz: e32d32dac693b8cae0445b2b9634345688b4010ecd4cbda054288934b4d0aeb353486415b55934e2c298fc8ab09655712ede1214e175ef4b67fe8af80926bb86
@@ -1,207 +1,73 @@
1
- ## Rails 6.0.3.2 (June 17, 2020) ##
1
+ ## Rails 6.1.0 (December 09, 2020) ##
2
2
 
3
- * No changes.
3
+ * Pass in `base` instead of `base_class` to Error.human_attribute_name
4
4
 
5
+ This is useful in cases where the `human_attribute_name` method depends
6
+ on other attributes' values of the class under validation to derive what the
7
+ attribute name should be.
5
8
 
6
- ## Rails 6.0.3.1 (May 18, 2020) ##
9
+ *Filipe Sabella*
7
10
 
8
- * No changes.
9
-
10
-
11
- ## Rails 6.0.3 (May 06, 2020) ##
12
-
13
- * No changes.
14
-
15
-
16
- ## Rails 6.0.2.2 (March 19, 2020) ##
17
-
18
- * No changes.
19
-
20
-
21
- ## Rails 6.0.2.1 (December 18, 2019) ##
22
-
23
- * No changes.
24
-
25
-
26
- ## Rails 6.0.2 (December 13, 2019) ##
27
-
28
- * No changes.
29
-
30
-
31
- ## Rails 6.0.1 (November 5, 2019) ##
32
-
33
- * No changes.
34
-
35
-
36
- ## Rails 6.0.0 (August 16, 2019) ##
37
-
38
- * No changes.
39
-
40
-
41
- ## Rails 6.0.0.rc2 (July 22, 2019) ##
42
-
43
- * No changes.
44
-
45
-
46
- ## Rails 6.0.0.rc1 (April 24, 2019) ##
47
-
48
- * Type cast falsy boolean symbols on boolean attribute as false.
49
-
50
- Fixes #35676.
11
+ * Deprecate marshalling load from legacy attributes format.
51
12
 
52
13
  *Ryuta Kamizono*
53
14
 
54
- * Change how validation error translation strings are fetched: The new behavior
55
- will first try the more specific keys, including doing locale fallback, then try
56
- the less specific ones.
57
-
58
- For example, this is the order in which keys will now be tried for a `blank`
59
- error on a `product`'s `title` attribute with current locale set to `en-US`:
60
-
61
- en-US.activerecord.errors.models.product.attributes.title.blank
62
- en-US.activerecord.errors.models.product.blank
63
- en-US.activerecord.errors.messages.blank
64
-
65
- en.activerecord.errors.models.product.attributes.title.blank
66
- en.activerecord.errors.models.product.blank
67
- en.activerecord.errors.messages.blank
68
-
69
- en-US.errors.attributes.title.blank
70
- en-US.errors.messages.blank
71
-
72
- en.errors.attributes.title.blank
73
- en.errors.messages.blank
74
-
75
- *Hugo Vacher*
76
-
15
+ * `*_previously_changed?` accepts `:from` and `:to` keyword arguments like `*_changed?`.
77
16
 
78
- ## Rails 6.0.0.beta3 (March 11, 2019) ##
17
+ topic.update!(status: :archived)
18
+ topic.status_previously_changed?(from: "active", to: "archived")
19
+ # => true
79
20
 
80
- * No changes.
21
+ *George Claghorn*
81
22
 
23
+ * Raise FrozenError when trying to write attributes that aren't backed by the database on an object that is frozen:
82
24
 
83
- ## Rails 6.0.0.beta2 (February 25, 2019) ##
84
-
85
- * Fix date value when casting a multiparameter date hash to not convert
86
- from Gregorian date to Julian date.
87
-
88
- Before:
89
-
90
- Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
91
- # => #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil>
92
-
93
- After:
94
-
95
- Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
96
- # => #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil>
97
-
98
- Fixes #28521.
99
-
100
- *Sayan Chakraborty*
101
-
102
- * Fix year value when casting a multiparameter time hash.
103
-
104
- When assigning a hash to a time attribute that's missing a year component
105
- (e.g. a `time_select` with `:ignore_date` set to `true`) then the year
106
- defaults to 1970 instead of the expected 2000. This results in the attribute
107
- changing as a result of the save.
108
-
109
- Before:
110
- ```
111
- event = Event.new(start_time: { 4 => 20, 5 => 30 })
112
- event.start_time # => 1970-01-01 20:30:00 UTC
113
- event.save
114
- event.reload
115
- event.start_time # => 2000-01-01 20:30:00 UTC
116
- ```
117
-
118
- After:
119
- ```
120
- event = Event.new(start_time: { 4 => 20, 5 => 30 })
121
- event.start_time # => 2000-01-01 20:30:00 UTC
122
- event.save
123
- event.reload
124
- event.start_time # => 2000-01-01 20:30:00 UTC
125
- ```
126
-
127
- *Andrew White*
128
-
129
-
130
- ## Rails 6.0.0.beta1 (January 18, 2019) ##
131
-
132
- * Internal calls to `human_attribute_name` on an `Active Model` now pass attributes as strings instead of symbols
133
- in some cases.
134
-
135
- This is in line with examples in Rails docs and puts the code in line with the intention -
136
- the potential use of strings or symbols.
137
-
138
- It is recommended to cast the attribute input to your desired type as if you you are overriding that methid.
139
-
140
- *Martin Larochelle*
141
-
142
- * Add `ActiveModel::Errors#of_kind?`.
143
-
144
- *bogdanvlviv*, *Rafael Mendonça França*
145
-
146
- * Fix numericality equality validation of `BigDecimal` and `Float`
147
- by casting to `BigDecimal` on both ends of the validation.
148
-
149
- *Gannon McGibbon*
150
-
151
- * Add `#slice!` method to `ActiveModel::Errors`.
152
-
153
- *Daniel López Prat*
154
-
155
- * Fix numericality validator to still use value before type cast except Active Record.
156
-
157
- Fixes #33651, #33686.
158
-
159
- *Ryuta Kamizono*
160
-
161
- * Fix `ActiveModel::Serializers::JSON#as_json` method for timestamps.
25
+ class Animal
26
+ include ActiveModel::Attributes
27
+ attribute :age
28
+ end
162
29
 
163
- Before:
164
- ```
165
- contact = Contact.new(created_at: Time.utc(2006, 8, 1))
166
- contact.as_json["created_at"] # => 2006-08-01 00:00:00 UTC
167
- ```
30
+ animal = Animal.new
31
+ animal.freeze
32
+ animal.age = 25 # => FrozenError, "can't modify a frozen Animal"
168
33
 
169
- After:
170
- ```
171
- contact = Contact.new(created_at: Time.utc(2006, 8, 1))
172
- contact.as_json["created_at"] # => "2006-08-01T00:00:00.000Z"
173
- ```
34
+ *Josh Brody*
174
35
 
175
- *Bogdan Gusiev*
36
+ * Add `*_previously_was` attribute methods when dirty tracking. Example:
176
37
 
177
- * Allows configurable attribute name for `#has_secure_password`. This
178
- still defaults to an attribute named 'password', causing no breaking
179
- change. There is a new method `#authenticate_XXX` where XXX is the
180
- configured attribute name, making the existing `#authenticate` now an
181
- alias for this when the attribute is the default 'password'.
38
+ pirate.update(catchphrase: "Ahoy!")
39
+ pirate.previous_changes["catchphrase"] # => ["Thar She Blows!", "Ahoy!"]
40
+ pirate.catchphrase_previously_was # => "Thar She Blows!"
182
41
 
183
- Example:
42
+ *DHH*
184
43
 
185
- class User < ActiveRecord::Base
186
- has_secure_password :recovery_password, validations: false
187
- end
44
+ * Encapsulate each validation error as an Error object.
188
45
 
189
- user = User.new()
190
- user.recovery_password = "42password"
191
- user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
192
- user.authenticate_recovery_password('42password') # => user
46
+ The `ActiveModel`’s `errors` collection is now an array of these Error
47
+ objects, instead of messages/details hash.
193
48
 
194
- *Unathi Chonco*
49
+ For each of these `Error` object, its `message` and `full_message` methods
50
+ are for generating error messages. Its `details` method would return error’s
51
+ extra parameters, found in the original `details` hash.
195
52
 
196
- * Add `config.active_model.i18n_customize_full_message` in order to control whether
197
- the `full_message` error format can be overridden at the attribute or model
198
- level in the locale files. This is `false` by default.
53
+ The change tries its best at maintaining backward compatibility, however
54
+ some edge cases won’t be covered, like `errors#first` will return `ActiveModel::Error` and manipulating
55
+ `errors.messages` and `errors.details` hashes directly will have no effect. Moving forward,
56
+ please convert those direct manipulations to use provided API methods instead.
199
57
 
200
- *Martin Larochelle*
58
+ The list of deprecated methods and their planned future behavioral changes at the next major release are:
201
59
 
202
- * Rails 6 requires Ruby 2.5.0 or newer.
60
+ * `errors#slice!` will be removed.
61
+ * `errors#each` with the `key, value` two-arguments block will stop working, while the `error` single-argument block would return `Error` object.
62
+ * `errors#values` will be removed.
63
+ * `errors#keys` will be removed.
64
+ * `errors#to_xml` will be removed.
65
+ * `errors#to_h` will be removed, and can be replaced with `errors#to_hash`.
66
+ * Manipulating `errors` itself as a hash will have no effect (e.g. `errors[:foo] = 'bar'`).
67
+ * Manipulating the hash returned by `errors#messages` (e.g. `errors.messages[:foo] = 'bar'`) will have no effect.
68
+ * Manipulating the hash returned by `errors#details` (e.g. `errors.details[:foo].clear`) will have no effect.
203
69
 
204
- *Jeremy Daer*, *Kasper Timm Hansen*
70
+ *lulalala*
205
71
 
206
72
 
207
- Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activemodel/CHANGELOG.md) for previous changes.
73
+ Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activemodel/CHANGELOG.md) for previous changes.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2019 David Heinemeier Hansson
1
+ Copyright (c) 2004-2020 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -200,7 +200,7 @@ behavior out of the box:
200
200
  attr_accessor :first_name, :last_name
201
201
 
202
202
  validates_each :first_name, :last_name do |record, attr, value|
203
- record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
203
+ record.errors.add attr, "starts with z." if value.start_with?("z")
204
204
  end
205
205
  end
206
206
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2019 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2020 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -53,6 +53,7 @@ module ActiveModel
53
53
 
54
54
  eager_autoload do
55
55
  autoload :Errors
56
+ autoload :Error
56
57
  autoload :RangeError, "active_model/errors"
57
58
  autoload :StrictValidationFailed, "active_model/errors"
58
59
  autoload :UnknownAttributeError, "active_model/errors"
@@ -5,16 +5,16 @@ require "active_support/core_ext/object/duplicable"
5
5
  module ActiveModel
6
6
  class Attribute # :nodoc:
7
7
  class << self
8
- def from_database(name, value, type)
9
- FromDatabase.new(name, value, type)
8
+ def from_database(name, value_before_type_cast, type, value = nil)
9
+ FromDatabase.new(name, value_before_type_cast, type, nil, value)
10
10
  end
11
11
 
12
- def from_user(name, value, type, original_attribute = nil)
13
- FromUser.new(name, value, type, original_attribute)
12
+ def from_user(name, value_before_type_cast, type, original_attribute = nil)
13
+ FromUser.new(name, value_before_type_cast, type, original_attribute)
14
14
  end
15
15
 
16
- def with_cast_value(name, value, type)
17
- WithCastValue.new(name, value, type)
16
+ def with_cast_value(name, value_before_type_cast, type)
17
+ WithCastValue.new(name, value_before_type_cast, type)
18
18
  end
19
19
 
20
20
  def null(name)
@@ -30,11 +30,12 @@ module ActiveModel
30
30
 
31
31
  # This method should not be called directly.
32
32
  # Use #from_database or #from_user
33
- def initialize(name, value_before_type_cast, type, original_attribute = nil)
33
+ def initialize(name, value_before_type_cast, type, original_attribute = nil, value = nil)
34
34
  @name = name
35
35
  @value_before_type_cast = value_before_type_cast
36
36
  @type = type
37
37
  @original_attribute = original_attribute
38
+ @value = value unless value.nil?
38
39
  end
39
40
 
40
41
  def value
@@ -132,14 +133,13 @@ module ActiveModel
132
133
  coder["value"] = value if defined?(@value)
133
134
  end
134
135
 
135
- protected
136
- def original_value_for_database
137
- if assigned?
138
- original_attribute.original_value_for_database
139
- else
140
- _original_value_for_database
141
- end
136
+ def original_value_for_database
137
+ if assigned?
138
+ original_attribute.original_value_for_database
139
+ else
140
+ _original_value_for_database
142
141
  end
142
+ end
143
143
 
144
144
  private
145
145
  attr_reader :original_attribute
@@ -167,6 +167,7 @@ module ActiveModel
167
167
  def _original_value_for_database
168
168
  value_before_type_cast
169
169
  end
170
+ private :_original_value_for_database
170
171
  end
171
172
 
172
173
  class FromUser < Attribute # :nodoc:
@@ -26,13 +26,12 @@ module ActiveModel
26
26
  # cat.name # => 'Gorby'
27
27
  # cat.status # => 'sleeping'
28
28
  def assign_attributes(new_attributes)
29
- if !new_attributes.respond_to?(:stringify_keys)
29
+ unless new_attributes.respond_to?(:each_pair)
30
30
  raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{new_attributes.class} passed."
31
31
  end
32
32
  return if new_attributes.empty?
33
33
 
34
- attributes = new_attributes.stringify_keys
35
- _assign_attributes(sanitize_for_mass_assignment(attributes))
34
+ _assign_attributes(sanitize_for_mass_assignment(new_attributes))
36
35
  end
37
36
 
38
37
  alias attributes= assign_attributes
@@ -49,7 +48,7 @@ module ActiveModel
49
48
  if respond_to?(setter)
50
49
  public_send(setter, v)
51
50
  else
52
- raise UnknownAttributeError.new(self, k)
51
+ raise UnknownAttributeError.new(self, k.to_s)
53
52
  end
54
53
  end
55
54
  end
@@ -207,10 +207,12 @@ module ActiveModel
207
207
  # person.nickname_short? # => true
208
208
  def alias_attribute(new_name, old_name)
209
209
  self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
210
- attribute_method_matchers.each do |matcher|
211
- matcher_new = matcher.method_name(new_name).to_s
212
- matcher_old = matcher.method_name(old_name).to_s
213
- define_proxy_call false, self, matcher_new, matcher_old
210
+ CodeGenerator.batch(self, __FILE__, __LINE__) do |owner|
211
+ attribute_method_matchers.each do |matcher|
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
215
+ end
214
216
  end
215
217
  end
216
218
 
@@ -249,7 +251,9 @@ module ActiveModel
249
251
  # end
250
252
  # end
251
253
  def define_attribute_methods(*attr_names)
252
- attr_names.flatten.each { |attr_name| define_attribute_method(attr_name) }
254
+ CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
255
+ attr_names.flatten.each { |attr_name| define_attribute_method(attr_name, _owner: owner) }
256
+ end
253
257
  end
254
258
 
255
259
  # Declares an attribute that should be prefixed and suffixed by
@@ -281,21 +285,23 @@ module ActiveModel
281
285
  # person.name = 'Bob'
282
286
  # person.name # => "Bob"
283
287
  # person.name_short? # => true
284
- def define_attribute_method(attr_name)
285
- attribute_method_matchers.each do |matcher|
286
- method_name = matcher.method_name(attr_name)
287
-
288
- unless instance_method_already_implemented?(method_name)
289
- generate_method = "define_method_#{matcher.target}"
290
-
291
- if respond_to?(generate_method, true)
292
- send(generate_method, attr_name.to_s)
293
- else
294
- define_proxy_call true, generated_attribute_methods, method_name, matcher.target, attr_name.to_s
288
+ def define_attribute_method(attr_name, _owner: generated_attribute_methods)
289
+ CodeGenerator.batch(_owner, __FILE__, __LINE__) do |owner|
290
+ attribute_method_matchers.each do |matcher|
291
+ method_name = matcher.method_name(attr_name)
292
+
293
+ unless instance_method_already_implemented?(method_name)
294
+ generate_method = "define_method_#{matcher.target}"
295
+
296
+ if respond_to?(generate_method, true)
297
+ send(generate_method, attr_name.to_s, owner: owner)
298
+ else
299
+ define_proxy_call true, owner, method_name, matcher.target, attr_name.to_s
300
+ end
295
301
  end
296
302
  end
303
+ attribute_method_matchers_cache.clear
297
304
  end
298
- attribute_method_matchers_cache.clear
299
305
  end
300
306
 
301
307
  # Removes all the previously dynamically defined methods from the class.
@@ -323,12 +329,52 @@ module ActiveModel
323
329
  # person.name_short? # => NoMethodError
324
330
  def undefine_attribute_methods
325
331
  generated_attribute_methods.module_eval do
326
- instance_methods.each { |m| undef_method(m) }
332
+ undef_method(*instance_methods)
327
333
  end
328
334
  attribute_method_matchers_cache.clear
329
335
  end
330
336
 
331
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
+
332
378
  def generated_attribute_methods
333
379
  @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
334
380
  end
@@ -352,18 +398,14 @@ module ActiveModel
352
398
 
353
399
  def attribute_method_matchers_matching(method_name)
354
400
  attribute_method_matchers_cache.compute_if_absent(method_name) do
355
- # Bump plain matcher to last place so that only methods that do not
356
- # match any other pattern match the actual attribute name.
357
- # This is currently only needed to support legacy usage.
358
- matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
359
- matchers.map { |matcher| matcher.match(method_name) }.compact
401
+ attribute_method_matchers.map { |matcher| matcher.match(method_name) }.compact
360
402
  end
361
403
  end
362
404
 
363
405
  # Define a method `name` in `mod` that dispatches to `send`
364
406
  # using the given `extra` args. This falls back on `define_method`
365
407
  # and `send` if the given names cannot be compiled.
366
- def define_proxy_call(include_private, mod, name, target, *extra)
408
+ def define_proxy_call(include_private, code_generator, name, target, *extra)
367
409
  defn = if NAME_COMPILABLE_REGEXP.match?(name)
368
410
  "def #{name}(*args)"
369
411
  else
@@ -378,12 +420,11 @@ module ActiveModel
378
420
  "send(:'#{target}', #{extra})"
379
421
  end
380
422
 
381
- mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
382
- #{defn}
383
- #{body}
384
- end
385
- ruby2_keywords(:'#{name}') if respond_to?(:ruby2_keywords, true)
386
- RUBY
423
+ code_generator <<
424
+ defn <<
425
+ body <<
426
+ "end" <<
427
+ "ruby2_keywords(:'#{name}') if respond_to?(:ruby2_keywords, true)"
387
428
  end
388
429
 
389
430
  class AttributeMethodMatcher #:nodoc:
@@ -407,10 +448,6 @@ module ActiveModel
407
448
  def method_name(attr_name)
408
449
  @method_name % attr_name
409
450
  end
410
-
411
- def plain?
412
- prefix.empty? && suffix.empty?
413
- end
414
451
  end
415
452
  end
416
453
 
@@ -499,10 +536,10 @@ module ActiveModel
499
536
  # to allocate an object on each call to the attribute method.
500
537
  # Making it frozen means that it doesn't get duped when used to
501
538
  # key the @attributes in read_attribute.
502
- def self.define_attribute_accessor_method(mod, attr_name, writer: false)
539
+ def self.define_attribute_accessor_method(owner, attr_name, writer: false)
503
540
  method_name = "#{attr_name}#{'=' if writer}"
504
541
  if attr_name.ascii_only? && DEF_SAFE_NAME.match?(attr_name)
505
- yield method_name, "'#{attr_name}'.freeze"
542
+ yield method_name, "'#{attr_name}'"
506
543
  else
507
544
  safe_name = attr_name.unpack1("h*")
508
545
  const_name = "ATTR_#{safe_name}"
@@ -510,8 +547,7 @@ module ActiveModel
510
547
  temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
511
548
  attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
512
549
  yield temp_method_name, attr_name_expr
513
- mod.alias_method method_name, temp_method_name
514
- mod.undef_method temp_method_name
550
+ owner.rename_method(temp_method_name, method_name)
515
551
  end
516
552
  end
517
553
  end