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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71959a62773641e1874aecc67db2f57b60149c490460d03bc09a29a057acef86
|
4
|
+
data.tar.gz: 41c0f7ce6eb65f6327b5384ebb73115da359bbbbe4c912e8845d37b1a8d1e38b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad69a3ba36d179916c1ddda99416b426040589920f6b091f3c48a8136b812ccfa62b969c70ad8b09da7a6cf380e650a9859bc06e5a0617064a1847c1a03a7268
|
7
|
+
data.tar.gz: 130055a2039128507efcadf3453f0842b828d6eb0938074e37d10663cf0d4079fb0f9f0af5f120eb4dff97b39754812edf3098c2df1d1a34f9f6a6ab0d71186c
|
data/CHANGELOG.md
CHANGED
@@ -1,34 +1,109 @@
|
|
1
|
-
## Rails
|
1
|
+
## Rails 6.0.4.6 (February 11, 2022) ##
|
2
2
|
|
3
3
|
* No changes.
|
4
4
|
|
5
5
|
|
6
|
-
## Rails
|
6
|
+
## Rails 6.0.4.5 (February 11, 2022) ##
|
7
7
|
|
8
8
|
* No changes.
|
9
9
|
|
10
10
|
|
11
|
-
## Rails
|
11
|
+
## Rails 6.0.4.4 (December 15, 2021) ##
|
12
12
|
|
13
13
|
* No changes.
|
14
14
|
|
15
15
|
|
16
|
-
## Rails
|
16
|
+
## Rails 6.0.4.3 (December 14, 2021) ##
|
17
17
|
|
18
18
|
* No changes.
|
19
19
|
|
20
20
|
|
21
|
-
## Rails
|
21
|
+
## Rails 6.0.4.2 (December 14, 2021) ##
|
22
22
|
|
23
23
|
* No changes.
|
24
24
|
|
25
25
|
|
26
|
-
## Rails
|
26
|
+
## Rails 6.0.4.1 (August 19, 2021) ##
|
27
27
|
|
28
28
|
* No changes.
|
29
29
|
|
30
30
|
|
31
|
-
## Rails
|
31
|
+
## Rails 6.0.4 (June 15, 2021) ##
|
32
|
+
|
33
|
+
* No changes.
|
34
|
+
|
35
|
+
|
36
|
+
## Rails 6.0.3.7 (May 05, 2021) ##
|
37
|
+
|
38
|
+
* No changes.
|
39
|
+
|
40
|
+
|
41
|
+
## Rails 6.0.3.6 (March 26, 2021) ##
|
42
|
+
|
43
|
+
* No changes.
|
44
|
+
|
45
|
+
|
46
|
+
## Rails 6.0.3.5 (February 10, 2021) ##
|
47
|
+
|
48
|
+
* No changes.
|
49
|
+
|
50
|
+
|
51
|
+
## Rails 6.0.3.4 (October 07, 2020) ##
|
52
|
+
|
53
|
+
* No changes.
|
54
|
+
|
55
|
+
|
56
|
+
## Rails 6.0.3.3 (September 09, 2020) ##
|
57
|
+
|
58
|
+
* No changes.
|
59
|
+
|
60
|
+
|
61
|
+
## Rails 6.0.3.2 (June 17, 2020) ##
|
62
|
+
|
63
|
+
* No changes.
|
64
|
+
|
65
|
+
|
66
|
+
## Rails 6.0.3.1 (May 18, 2020) ##
|
67
|
+
|
68
|
+
* No changes.
|
69
|
+
|
70
|
+
|
71
|
+
## Rails 6.0.3 (May 06, 2020) ##
|
72
|
+
|
73
|
+
* No changes.
|
74
|
+
|
75
|
+
|
76
|
+
## Rails 6.0.2.2 (March 19, 2020) ##
|
77
|
+
|
78
|
+
* No changes.
|
79
|
+
|
80
|
+
|
81
|
+
## Rails 6.0.2.1 (December 18, 2019) ##
|
82
|
+
|
83
|
+
* No changes.
|
84
|
+
|
85
|
+
|
86
|
+
## Rails 6.0.2 (December 13, 2019) ##
|
87
|
+
|
88
|
+
* No changes.
|
89
|
+
|
90
|
+
|
91
|
+
## Rails 6.0.1 (November 5, 2019) ##
|
92
|
+
|
93
|
+
* No changes.
|
94
|
+
|
95
|
+
|
96
|
+
## Rails 6.0.0 (August 16, 2019) ##
|
97
|
+
|
98
|
+
* No changes.
|
99
|
+
|
100
|
+
|
101
|
+
## Rails 6.0.0.rc2 (July 22, 2019) ##
|
102
|
+
|
103
|
+
* No changes.
|
104
|
+
|
105
|
+
|
106
|
+
## Rails 6.0.0.rc1 (April 24, 2019) ##
|
32
107
|
|
33
108
|
* Type cast falsy boolean symbols on boolean attribute as false.
|
34
109
|
|
@@ -36,8 +111,36 @@
|
|
36
111
|
|
37
112
|
*Ryuta Kamizono*
|
38
113
|
|
114
|
+
* Change how validation error translation strings are fetched: The new behavior
|
115
|
+
will first try the more specific keys, including doing locale fallback, then try
|
116
|
+
the less specific ones.
|
39
117
|
|
40
|
-
|
118
|
+
For example, this is the order in which keys will now be tried for a `blank`
|
119
|
+
error on a `product`'s `title` attribute with current locale set to `en-US`:
|
120
|
+
|
121
|
+
en-US.activerecord.errors.models.product.attributes.title.blank
|
122
|
+
en-US.activerecord.errors.models.product.blank
|
123
|
+
en-US.activerecord.errors.messages.blank
|
124
|
+
|
125
|
+
en.activerecord.errors.models.product.attributes.title.blank
|
126
|
+
en.activerecord.errors.models.product.blank
|
127
|
+
en.activerecord.errors.messages.blank
|
128
|
+
|
129
|
+
en-US.errors.attributes.title.blank
|
130
|
+
en-US.errors.messages.blank
|
131
|
+
|
132
|
+
en.errors.attributes.title.blank
|
133
|
+
en.errors.messages.blank
|
134
|
+
|
135
|
+
*Hugo Vacher*
|
136
|
+
|
137
|
+
|
138
|
+
## Rails 6.0.0.beta3 (March 11, 2019) ##
|
139
|
+
|
140
|
+
* No changes.
|
141
|
+
|
142
|
+
|
143
|
+
## Rails 6.0.0.beta2 (February 25, 2019) ##
|
41
144
|
|
42
145
|
* Fix date value when casting a multiparameter date hash to not convert
|
43
146
|
from Gregorian date to Julian date.
|
@@ -45,109 +148,120 @@
|
|
45
148
|
Before:
|
46
149
|
|
47
150
|
Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
|
48
|
-
=> #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil>
|
151
|
+
# => #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil>
|
49
152
|
|
50
153
|
After:
|
51
154
|
|
52
155
|
Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
|
53
|
-
=> #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil>
|
156
|
+
# => #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil>
|
54
157
|
|
55
158
|
Fixes #28521.
|
56
159
|
|
57
160
|
*Sayan Chakraborty*
|
58
161
|
|
59
|
-
* Fix
|
60
|
-
by casting to `BigDecimal` on both ends of the validation.
|
61
|
-
|
62
|
-
*Gannon McGibbon*
|
162
|
+
* Fix year value when casting a multiparameter time hash.
|
63
163
|
|
164
|
+
When assigning a hash to a time attribute that's missing a year component
|
165
|
+
(e.g. a `time_select` with `:ignore_date` set to `true`) then the year
|
166
|
+
defaults to 1970 instead of the expected 2000. This results in the attribute
|
167
|
+
changing as a result of the save.
|
64
168
|
|
65
|
-
|
66
|
-
|
67
|
-
|
169
|
+
Before:
|
170
|
+
```
|
171
|
+
event = Event.new(start_time: { 4 => 20, 5 => 30 })
|
172
|
+
event.start_time # => 1970-01-01 20:30:00 UTC
|
173
|
+
event.save
|
174
|
+
event.reload
|
175
|
+
event.start_time # => 2000-01-01 20:30:00 UTC
|
176
|
+
```
|
68
177
|
|
178
|
+
After:
|
179
|
+
```
|
180
|
+
event = Event.new(start_time: { 4 => 20, 5 => 30 })
|
181
|
+
event.start_time # => 2000-01-01 20:30:00 UTC
|
182
|
+
event.save
|
183
|
+
event.reload
|
184
|
+
event.start_time # => 2000-01-01 20:30:00 UTC
|
185
|
+
```
|
69
186
|
|
70
|
-
|
187
|
+
*Andrew White*
|
71
188
|
|
72
|
-
* Fix numericality validator to still use value before type cast except Active Record.
|
73
189
|
|
74
|
-
|
190
|
+
## Rails 6.0.0.beta1 (January 18, 2019) ##
|
75
191
|
|
76
|
-
|
192
|
+
* Internal calls to `human_attribute_name` on an `Active Model` now pass attributes as strings instead of symbols
|
193
|
+
in some cases.
|
77
194
|
|
195
|
+
This is in line with examples in Rails docs and puts the code in line with the intention -
|
196
|
+
the potential use of strings or symbols.
|
78
197
|
|
79
|
-
|
198
|
+
It is recommended to cast the attribute input to your desired type as if you you are overriding that methid.
|
80
199
|
|
81
|
-
*
|
200
|
+
*Martin Larochelle*
|
82
201
|
|
202
|
+
* Add `ActiveModel::Errors#of_kind?`.
|
83
203
|
|
84
|
-
|
85
|
-
|
86
|
-
* No changes.
|
204
|
+
*bogdanvlviv*, *Rafael Mendonça França*
|
87
205
|
|
206
|
+
* Fix numericality equality validation of `BigDecimal` and `Float`
|
207
|
+
by casting to `BigDecimal` on both ends of the validation.
|
88
208
|
|
89
|
-
|
209
|
+
*Gannon McGibbon*
|
90
210
|
|
91
|
-
*
|
211
|
+
* Add `#slice!` method to `ActiveModel::Errors`.
|
92
212
|
|
93
|
-
*
|
213
|
+
*Daniel López Prat*
|
94
214
|
|
95
|
-
*
|
215
|
+
* Fix numericality validator to still use value before type cast except Active Record.
|
96
216
|
|
97
|
-
Fixes #
|
217
|
+
Fixes #33651, #33686.
|
98
218
|
|
99
|
-
*
|
219
|
+
*Ryuta Kamizono*
|
100
220
|
|
101
|
-
* Fix
|
221
|
+
* Fix `ActiveModel::Serializers::JSON#as_json` method for timestamps.
|
102
222
|
|
103
|
-
|
223
|
+
Before:
|
224
|
+
```
|
225
|
+
contact = Contact.new(created_at: Time.utc(2006, 8, 1))
|
226
|
+
contact.as_json["created_at"] # => 2006-08-01 00:00:00 UTC
|
227
|
+
```
|
104
228
|
|
105
|
-
|
229
|
+
After:
|
230
|
+
```
|
231
|
+
contact = Contact.new(created_at: Time.utc(2006, 8, 1))
|
232
|
+
contact.as_json["created_at"] # => "2006-08-01T00:00:00.000Z"
|
233
|
+
```
|
106
234
|
|
107
|
-
*
|
235
|
+
*Bogdan Gusiev*
|
108
236
|
|
109
|
-
*
|
237
|
+
* Allows configurable attribute name for `#has_secure_password`. This
|
238
|
+
still defaults to an attribute named 'password', causing no breaking
|
239
|
+
change. There is a new method `#authenticate_XXX` where XXX is the
|
240
|
+
configured attribute name, making the existing `#authenticate` now an
|
241
|
+
alias for this when the attribute is the default 'password'.
|
110
242
|
|
111
|
-
|
243
|
+
Example:
|
112
244
|
|
113
|
-
|
245
|
+
class User < ActiveRecord::Base
|
246
|
+
has_secure_password :recovery_password, validations: false
|
247
|
+
end
|
114
248
|
|
115
|
-
|
249
|
+
user = User.new()
|
250
|
+
user.recovery_password = "42password"
|
251
|
+
user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
|
252
|
+
user.authenticate_recovery_password('42password') # => user
|
116
253
|
|
117
|
-
*
|
118
|
-
values with more scale than the schema.
|
254
|
+
*Unathi Chonco*
|
119
255
|
|
120
|
-
|
256
|
+
* Add `config.active_model.i18n_customize_full_message` in order to control whether
|
257
|
+
the `full_message` error format can be overridden at the attribute or model
|
258
|
+
level in the locale files. This is `false` by default.
|
121
259
|
|
122
|
-
*
|
260
|
+
*Martin Larochelle*
|
123
261
|
|
124
|
-
|
262
|
+
* Rails 6 requires Ruby 2.5.0 or newer.
|
125
263
|
|
126
|
-
|
264
|
+
*Jeremy Daer*, *Kasper Timm Hansen*
|
127
265
|
|
128
|
-
Example:
|
129
266
|
|
130
|
-
|
131
|
-
person = Person.new
|
132
|
-
person.errors.keys # => []
|
133
|
-
person.errors.values # => []
|
134
|
-
person.errors.messages # => {}
|
135
|
-
person.errors[:name] # => []
|
136
|
-
person.errors.messages # => {:name => []}
|
137
|
-
person.errors.keys # => [:name]
|
138
|
-
person.errors.values # => [[]]
|
139
|
-
|
140
|
-
# After
|
141
|
-
person = Person.new
|
142
|
-
person.errors.keys # => []
|
143
|
-
person.errors.values # => []
|
144
|
-
person.errors.messages # => {}
|
145
|
-
person.errors[:name] # => []
|
146
|
-
person.errors.messages # => {:name => []}
|
147
|
-
person.errors.keys # => []
|
148
|
-
person.errors.values # => []
|
149
|
-
|
150
|
-
*bogdanvlviv*
|
151
|
-
|
152
|
-
|
153
|
-
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activemodel/CHANGELOG.md) for previous changes.
|
267
|
+
Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -5,6 +5,8 @@ They allow for Action Pack helpers to interact with non-Active Record models,
|
|
5
5
|
for example. Active Model also helps with building custom ORMs for use outside of
|
6
6
|
the Rails framework.
|
7
7
|
|
8
|
+
You can read more about Active Model in the {Active Model Basics}[https://edgeguides.rubyonrails.org/active_model_basics.html] guide.
|
9
|
+
|
8
10
|
Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
|
9
11
|
interact with Action Pack helpers, it was required to either copy chunks of
|
10
12
|
code from Rails, or monkey patch entire helpers to make them handle objects
|
@@ -239,7 +241,7 @@ The latest version of Active Model can be installed with RubyGems:
|
|
239
241
|
|
240
242
|
Source code can be downloaded as part of the Rails project on GitHub
|
241
243
|
|
242
|
-
* https://github.com/rails/rails/tree/
|
244
|
+
* https://github.com/rails/rails/tree/main/activemodel
|
243
245
|
|
244
246
|
|
245
247
|
== License
|
@@ -253,7 +255,7 @@ Active Model is released under the MIT license:
|
|
253
255
|
|
254
256
|
API documentation is at:
|
255
257
|
|
256
|
-
*
|
258
|
+
* https://api.rubyonrails.org
|
257
259
|
|
258
260
|
Bug reports for the Ruby on Rails project can be filed here:
|
259
261
|
|
@@ -261,4 +263,4 @@ Bug reports for the Ruby on Rails project can be filed here:
|
|
261
263
|
|
262
264
|
Feature requests should be discussed on the rails-core mailing list here:
|
263
265
|
|
264
|
-
* https://
|
266
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
@@ -133,10 +133,6 @@ module ActiveModel
|
|
133
133
|
end
|
134
134
|
|
135
135
|
protected
|
136
|
-
|
137
|
-
attr_reader :original_attribute
|
138
|
-
alias_method :assigned?, :original_attribute
|
139
|
-
|
140
136
|
def original_value_for_database
|
141
137
|
if assigned?
|
142
138
|
original_attribute.original_value_for_database
|
@@ -146,6 +142,9 @@ module ActiveModel
|
|
146
142
|
end
|
147
143
|
|
148
144
|
private
|
145
|
+
attr_reader :original_attribute
|
146
|
+
alias :assigned? :original_attribute
|
147
|
+
|
149
148
|
def initialize_dup(other)
|
150
149
|
if defined?(@value) && @value.duplicable?
|
151
150
|
@value = @value.dup
|
@@ -27,7 +27,7 @@ module ActiveModel
|
|
27
27
|
# cat.status # => 'sleeping'
|
28
28
|
def assign_attributes(new_attributes)
|
29
29
|
if !new_attributes.respond_to?(:stringify_keys)
|
30
|
-
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
|
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
|
|
@@ -38,7 +38,6 @@ module ActiveModel
|
|
38
38
|
alias attributes= assign_attributes
|
39
39
|
|
40
40
|
private
|
41
|
-
|
42
41
|
def _assign_attributes(attributes)
|
43
42
|
attributes.each do |k, v|
|
44
43
|
_assign_attribute(k, v)
|
@@ -286,12 +286,12 @@ module ActiveModel
|
|
286
286
|
method_name = matcher.method_name(attr_name)
|
287
287
|
|
288
288
|
unless instance_method_already_implemented?(method_name)
|
289
|
-
generate_method = "define_method_#{matcher.
|
289
|
+
generate_method = "define_method_#{matcher.target}"
|
290
290
|
|
291
291
|
if respond_to?(generate_method, true)
|
292
292
|
send(generate_method, attr_name.to_s)
|
293
293
|
else
|
294
|
-
define_proxy_call true, generated_attribute_methods, method_name, matcher.
|
294
|
+
define_proxy_call true, generated_attribute_methods, method_name, matcher.target, attr_name.to_s
|
295
295
|
end
|
296
296
|
end
|
297
297
|
end
|
@@ -352,53 +352,55 @@ module ActiveModel
|
|
352
352
|
|
353
353
|
def attribute_method_matchers_matching(method_name)
|
354
354
|
attribute_method_matchers_cache.compute_if_absent(method_name) do
|
355
|
-
#
|
356
|
-
#
|
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.
|
357
358
|
matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
|
358
|
-
matchers.map { |
|
359
|
+
matchers.map { |matcher| matcher.match(method_name) }.compact
|
359
360
|
end
|
360
361
|
end
|
361
362
|
|
362
363
|
# Define a method `name` in `mod` that dispatches to `send`
|
363
364
|
# using the given `extra` args. This falls back on `define_method`
|
364
365
|
# and `send` if the given names cannot be compiled.
|
365
|
-
def define_proxy_call(include_private, mod, name,
|
366
|
+
def define_proxy_call(include_private, mod, name, target, *extra)
|
366
367
|
defn = if NAME_COMPILABLE_REGEXP.match?(name)
|
367
368
|
"def #{name}(*args)"
|
368
369
|
else
|
369
370
|
"define_method(:'#{name}') do |*args|"
|
370
371
|
end
|
371
372
|
|
372
|
-
extra = (extra.map!(&:inspect) << "*args").join(", "
|
373
|
+
extra = (extra.map!(&:inspect) << "*args").join(", ")
|
373
374
|
|
374
|
-
|
375
|
-
"#{"self." unless include_private}#{
|
375
|
+
body = if CALL_COMPILABLE_REGEXP.match?(target)
|
376
|
+
"#{"self." unless include_private}#{target}(#{extra})"
|
376
377
|
else
|
377
|
-
"send(:'#{
|
378
|
+
"send(:'#{target}', #{extra})"
|
378
379
|
end
|
379
380
|
|
380
381
|
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
381
382
|
#{defn}
|
382
|
-
#{
|
383
|
+
#{body}
|
383
384
|
end
|
385
|
+
ruby2_keywords(:'#{name}') if respond_to?(:ruby2_keywords, true)
|
384
386
|
RUBY
|
385
387
|
end
|
386
388
|
|
387
389
|
class AttributeMethodMatcher #:nodoc:
|
388
|
-
attr_reader :prefix, :suffix, :
|
390
|
+
attr_reader :prefix, :suffix, :target
|
389
391
|
|
390
|
-
AttributeMethodMatch = Struct.new(:target, :attr_name
|
392
|
+
AttributeMethodMatch = Struct.new(:target, :attr_name)
|
391
393
|
|
392
394
|
def initialize(options = {})
|
393
395
|
@prefix, @suffix = options.fetch(:prefix, ""), options.fetch(:suffix, "")
|
394
396
|
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
|
395
|
-
@
|
397
|
+
@target = "#{@prefix}attribute#{@suffix}"
|
396
398
|
@method_name = "#{prefix}%s#{suffix}"
|
397
399
|
end
|
398
400
|
|
399
401
|
def match(method_name)
|
400
402
|
if @regex =~ method_name
|
401
|
-
AttributeMethodMatch.new(
|
403
|
+
AttributeMethodMatch.new(target, $1)
|
402
404
|
end
|
403
405
|
end
|
404
406
|
|
@@ -430,6 +432,7 @@ module ActiveModel
|
|
430
432
|
match ? attribute_missing(match, *args, &block) : super
|
431
433
|
end
|
432
434
|
end
|
435
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
433
436
|
|
434
437
|
# +attribute_missing+ is like +method_missing+, but for attributes. When
|
435
438
|
# +method_missing+ is called we check to see if there is a matching
|
@@ -474,5 +477,43 @@ module ActiveModel
|
|
474
477
|
def _read_attribute(attr)
|
475
478
|
__send__(attr)
|
476
479
|
end
|
480
|
+
|
481
|
+
module AttrNames # :nodoc:
|
482
|
+
DEF_SAFE_NAME = /\A[a-zA-Z_]\w*\z/
|
483
|
+
|
484
|
+
# We want to generate the methods via module_eval rather than
|
485
|
+
# define_method, because define_method is slower on dispatch.
|
486
|
+
# Evaluating many similar methods may use more memory as the instruction
|
487
|
+
# sequences are duplicated and cached (in MRI). define_method may
|
488
|
+
# be slower on dispatch, but if you're careful about the closure
|
489
|
+
# created, then define_method will consume much less memory.
|
490
|
+
#
|
491
|
+
# But sometimes the database might return columns with
|
492
|
+
# characters that are not allowed in normal method names (like
|
493
|
+
# 'my_column(omg)'. So to work around this we first define with
|
494
|
+
# the __temp__ identifier, and then use alias method to rename
|
495
|
+
# it to what we want.
|
496
|
+
#
|
497
|
+
# We are also defining a constant to hold the frozen string of
|
498
|
+
# the attribute name. Using a constant means that we do not have
|
499
|
+
# to allocate an object on each call to the attribute method.
|
500
|
+
# Making it frozen means that it doesn't get duped when used to
|
501
|
+
# key the @attributes in read_attribute.
|
502
|
+
def self.define_attribute_accessor_method(mod, attr_name, writer: false)
|
503
|
+
method_name = "#{attr_name}#{'=' if writer}"
|
504
|
+
if attr_name.ascii_only? && DEF_SAFE_NAME.match?(attr_name)
|
505
|
+
yield method_name, "'#{attr_name}'.freeze"
|
506
|
+
else
|
507
|
+
safe_name = attr_name.unpack1("h*")
|
508
|
+
const_name = "ATTR_#{safe_name}"
|
509
|
+
const_set(const_name, attr_name) unless const_defined?(const_name)
|
510
|
+
temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
|
511
|
+
attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
|
512
|
+
yield temp_method_name, attr_name_expr
|
513
|
+
mod.alias_method method_name, temp_method_name
|
514
|
+
mod.undef_method temp_method_name
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
477
518
|
end
|
478
519
|
end
|