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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -182
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_model.rb +2 -1
- data/lib/active_model/attribute.rb +15 -14
- data/lib/active_model/attribute_assignment.rb +3 -4
- data/lib/active_model/attribute_methods.rb +74 -38
- data/lib/active_model/attribute_mutation_tracker.rb +8 -5
- data/lib/active_model/attribute_set.rb +18 -16
- data/lib/active_model/attribute_set/builder.rb +80 -13
- data/lib/active_model/attributes.rb +20 -24
- data/lib/active_model/dirty.rb +12 -4
- data/lib/active_model/error.rb +207 -0
- data/lib/active_model/errors.rb +316 -208
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/lint.rb +1 -1
- data/lib/active_model/naming.rb +2 -2
- data/lib/active_model/nested_error.rb +22 -0
- data/lib/active_model/railtie.rb +1 -1
- data/lib/active_model/secure_password.rb +14 -14
- data/lib/active_model/serialization.rb +9 -6
- data/lib/active_model/serializers/json.rb +7 -0
- data/lib/active_model/type/date_time.rb +2 -2
- data/lib/active_model/type/float.rb +2 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +11 -7
- data/lib/active_model/type/helpers/numeric.rb +8 -3
- data/lib/active_model/type/helpers/time_value.rb +27 -17
- data/lib/active_model/type/helpers/timezone.rb +1 -1
- data/lib/active_model/type/immutable_string.rb +14 -10
- data/lib/active_model/type/integer.rb +11 -2
- data/lib/active_model/type/registry.rb +11 -4
- data/lib/active_model/type/string.rb +12 -2
- data/lib/active_model/type/value.rb +9 -1
- data/lib/active_model/validations.rb +6 -6
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/clusivity.rb +5 -1
- data/lib/active_model/validations/confirmation.rb +2 -2
- data/lib/active_model/validations/exclusion.rb +1 -1
- data/lib/active_model/validations/format.rb +2 -2
- data/lib/active_model/validations/inclusion.rb +1 -1
- data/lib/active_model/validations/length.rb +2 -2
- data/lib/active_model/validations/numericality.rb +48 -41
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/validates.rb +6 -4
- data/lib/active_model/validator.rb +7 -1
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1caa114ce8c604cb9ac388ef4ebbfd796f8ff253799322db9fa714193bca6821
|
4
|
+
data.tar.gz: 49f8fb7f47a3154022161dee9871d6aba46f7750b3381e690e37d7ff2663ef48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26271099e8c36f89e1ee19fcb3469be914240ea887f15aba4d0de79b3db57f17d8467f523010330ba4f70e4df01d14398a6db7687df0a9be674f17ffd284be5d
|
7
|
+
data.tar.gz: e32d32dac693b8cae0445b2b9634345688b4010ecd4cbda054288934b4d0aeb353486415b55934e2c298fc8ab09655712ede1214e175ef4b67fe8af80926bb86
|
data/CHANGELOG.md
CHANGED
@@ -1,207 +1,73 @@
|
|
1
|
-
## Rails 6.0
|
1
|
+
## Rails 6.1.0 (December 09, 2020) ##
|
2
2
|
|
3
|
-
*
|
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
|
-
|
9
|
+
*Filipe Sabella*
|
7
10
|
|
8
|
-
*
|
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
|
-
*
|
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
|
-
|
17
|
+
topic.update!(status: :archived)
|
18
|
+
topic.status_previously_changed?(from: "active", to: "archived")
|
19
|
+
# => true
|
79
20
|
|
80
|
-
*
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
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
|
-
|
36
|
+
* Add `*_previously_was` attribute methods when dirty tracking. Example:
|
176
37
|
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
42
|
+
*DHH*
|
184
43
|
|
185
|
-
|
186
|
-
has_secure_password :recovery_password, validations: false
|
187
|
-
end
|
44
|
+
* Encapsulate each validation error as an Error object.
|
188
45
|
|
189
|
-
|
190
|
-
|
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
|
-
|
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
|
-
|
197
|
-
|
198
|
-
|
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
|
-
|
58
|
+
The list of deprecated methods and their planned future behavioral changes at the next major release are:
|
201
59
|
|
202
|
-
*
|
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
|
-
*
|
70
|
+
*lulalala*
|
205
71
|
|
206
72
|
|
207
|
-
Please check [
|
73
|
+
Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -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,
|
203
|
+
record.errors.add attr, "starts with z." if value.start_with?("z")
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
data/lib/active_model.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2004-
|
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,
|
9
|
-
FromDatabase.new(name,
|
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,
|
13
|
-
FromUser.new(name,
|
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,
|
17
|
-
WithCastValue.new(name,
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
-
|
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
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
382
|
-
|
383
|
-
|
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(
|
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}'
|
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
|
-
|
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
|