activemodel 6.1.4.1 → 7.0.0.rc2
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 +77 -66
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_model/api.rb +99 -0
- data/lib/active_model/attribute.rb +4 -0
- data/lib/active_model/attribute_methods.rb +65 -81
- data/lib/active_model/attribute_set/builder.rb +1 -10
- data/lib/active_model/attribute_set.rb +4 -1
- data/lib/active_model/attributes.rb +15 -12
- data/lib/active_model/callbacks.rb +1 -1
- data/lib/active_model/conversion.rb +2 -2
- data/lib/active_model/dirty.rb +6 -5
- data/lib/active_model/errors.rb +35 -235
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/locale/en.yml +1 -0
- data/lib/active_model/model.rb +6 -59
- data/lib/active_model/naming.rb +15 -8
- data/lib/active_model/secure_password.rb +2 -1
- data/lib/active_model/serialization.rb +7 -2
- data/lib/active_model/translation.rb +1 -1
- data/lib/active_model/type/date.rb +1 -1
- data/lib/active_model/type/helpers/numeric.rb +9 -1
- data/lib/active_model/type/helpers/time_value.rb +3 -3
- data/lib/active_model/type/integer.rb +4 -1
- data/lib/active_model/type/registry.rb +9 -41
- data/lib/active_model/type/time.rb +1 -1
- data/lib/active_model/type.rb +6 -5
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/clusivity.rb +1 -1
- data/lib/active_model/validations/comparability.rb +29 -0
- data/lib/active_model/validations/comparison.rb +82 -0
- data/lib/active_model/validations/confirmation.rb +4 -4
- data/lib/active_model/validations/numericality.rb +28 -21
- data/lib/active_model/validations.rb +4 -4
- data/lib/active_model/validator.rb +2 -2
- data/lib/active_model.rb +2 -1
- metadata +14 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9079e5f5b6e97cb434d3d881df63a462deaa5d76f4a903404a80a6fb352a4a34
|
4
|
+
data.tar.gz: 324766119f9caa91770f1948700d4085aad8a9814a2265d0a19a12a65ba040ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00dd34e112e676941a1c7a25d615dba688803f35dcd2c1fd71fc094490e26081d978728acb309d5cf588be6bcfaa22502017a2f058d33d83a09a35ac7f09d475
|
7
|
+
data.tar.gz: 24b905c955a57deb86c34ba76527f5f9ba7bc1b262825613db904b3d5abf189e463d131219217bbff3ce5ff452b60642886df6c50c8e4f11a3149cf3ebcac5df
|
data/CHANGELOG.md
CHANGED
@@ -1,117 +1,128 @@
|
|
1
|
-
## Rails
|
1
|
+
## Rails 7.0.0.rc2 (December 14, 2021) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Remove support to Marshal load Rails 5.x `ActiveModel::AttributeSet` format.
|
4
4
|
|
5
|
+
*Rafael Mendonça França*
|
5
6
|
|
6
|
-
|
7
|
+
* Remove support to Marshal and YAML load Rails 5.x error format.
|
7
8
|
|
8
|
-
*
|
9
|
+
*Rafael Mendonça França*
|
9
10
|
|
10
|
-
|
11
|
+
* Remove deprecated support to use `[]=` in `ActiveModel::Errors#messages`.
|
11
12
|
|
12
|
-
*
|
13
|
+
*Rafael Mendonça França*
|
13
14
|
|
15
|
+
* Remove deprecated support to `delete` errors from `ActiveModel::Errors#messages`.
|
14
16
|
|
15
|
-
|
17
|
+
*Rafael Mendonça França*
|
16
18
|
|
17
|
-
*
|
19
|
+
* Remove deprecated support to `clear` errors from `ActiveModel::Errors#messages`.
|
18
20
|
|
21
|
+
*Rafael Mendonça França*
|
19
22
|
|
20
|
-
|
23
|
+
* Remove deprecated support concat errors to `ActiveModel::Errors#messages`.
|
21
24
|
|
22
|
-
*
|
25
|
+
*Rafael Mendonça França*
|
23
26
|
|
27
|
+
* Remove deprecated `ActiveModel::Errors#to_xml`.
|
24
28
|
|
25
|
-
|
29
|
+
*Rafael Mendonça França*
|
26
30
|
|
27
|
-
*
|
31
|
+
* Remove deprecated `ActiveModel::Errors#keys`.
|
28
32
|
|
33
|
+
*Rafael Mendonça França*
|
29
34
|
|
30
|
-
|
35
|
+
* Remove deprecated `ActiveModel::Errors#values`.
|
31
36
|
|
32
|
-
*
|
37
|
+
*Rafael Mendonça França*
|
33
38
|
|
39
|
+
* Remove deprecated `ActiveModel::Errors#slice!`.
|
34
40
|
|
35
|
-
|
41
|
+
*Rafael Mendonça França*
|
36
42
|
|
37
|
-
*
|
43
|
+
* Remove deprecated `ActiveModel::Errors#to_h`.
|
44
|
+
|
45
|
+
*Rafael Mendonça França*
|
46
|
+
|
47
|
+
* Remove deprecated enumeration of `ActiveModel::Errors` instances as a Hash.
|
48
|
+
|
49
|
+
*Rafael Mendonça França*
|
50
|
+
|
51
|
+
* Clear secure password cache if password is set to `nil`
|
38
52
|
|
53
|
+
Before:
|
39
54
|
|
40
|
-
|
55
|
+
user.password = 'something'
|
56
|
+
user.password = nil
|
57
|
+
|
58
|
+
user.password # => 'something'
|
59
|
+
|
60
|
+
Now:
|
61
|
+
|
62
|
+
user.password = 'something'
|
63
|
+
user.password = nil
|
64
|
+
|
65
|
+
user.password # => nil
|
66
|
+
|
67
|
+
*Markus Doits*
|
68
|
+
|
69
|
+
## Rails 7.0.0.alpha2 (September 15, 2021) ##
|
41
70
|
|
42
71
|
* No changes.
|
43
72
|
|
44
73
|
|
45
|
-
## Rails
|
74
|
+
## Rails 7.0.0.alpha1 (September 15, 2021) ##
|
46
75
|
|
47
|
-
*
|
76
|
+
* Introduce `ActiveModel::API`.
|
48
77
|
|
49
|
-
|
50
|
-
|
51
|
-
attribute name should be.
|
78
|
+
Make `ActiveModel::API` the minimum API to talk with Action Pack and Action View.
|
79
|
+
This will allow adding more functionality to `ActiveModel::Model`.
|
52
80
|
|
53
|
-
*
|
81
|
+
*Petrik de Heus*, *Nathaniel Watts*
|
54
82
|
|
55
|
-
*
|
83
|
+
* Fix dirty check for Float::NaN and BigDecimal::NaN.
|
56
84
|
|
57
|
-
|
85
|
+
Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720)
|
86
|
+
and can't be compared with `==`.
|
58
87
|
|
59
|
-
*
|
88
|
+
*Marcelo Lauxen*
|
60
89
|
|
61
|
-
|
62
|
-
topic.status_previously_changed?(from: "active", to: "archived")
|
63
|
-
# => true
|
90
|
+
* Fix `to_json` for `ActiveModel::Dirty` object.
|
64
91
|
|
65
|
-
|
92
|
+
Exclude `mutations_from_database` attribute from json as it lead to recursion.
|
66
93
|
|
67
|
-
*
|
94
|
+
*Anil Maurya*
|
68
95
|
|
69
|
-
|
70
|
-
include ActiveModel::Attributes
|
71
|
-
attribute :age
|
72
|
-
end
|
96
|
+
* Add `ActiveModel::AttributeSet#values_for_database`.
|
73
97
|
|
74
|
-
|
75
|
-
animal.freeze
|
76
|
-
animal.age = 25 # => FrozenError, "can't modify a frozen Animal"
|
98
|
+
Returns attributes with values for assignment to the database.
|
77
99
|
|
78
|
-
*
|
100
|
+
*Chris Salzberg*
|
79
101
|
|
80
|
-
*
|
102
|
+
* Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup.
|
81
103
|
|
82
|
-
|
83
|
-
pirate.previous_changes["catchphrase"] # => ["Thar She Blows!", "Ahoy!"]
|
84
|
-
pirate.catchphrase_previously_was # => "Thar She Blows!"
|
104
|
+
Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
|
85
105
|
|
86
|
-
*
|
106
|
+
*Benoit Daloze*
|
87
107
|
|
88
|
-
*
|
108
|
+
* Cache and re-use generated attribute methods.
|
89
109
|
|
90
|
-
|
91
|
-
|
110
|
+
Generated methods with identical implementations will now share their instruction sequences
|
111
|
+
leading to reduced memory retention, and slightly faster load time.
|
92
112
|
|
93
|
-
|
94
|
-
are for generating error messages. Its `details` method would return error’s
|
95
|
-
extra parameters, found in the original `details` hash.
|
113
|
+
*Jean Boussier*
|
96
114
|
|
97
|
-
|
98
|
-
some edge cases won’t be covered, like `errors#first` will return `ActiveModel::Error` and manipulating
|
99
|
-
`errors.messages` and `errors.details` hashes directly will have no effect. Moving forward,
|
100
|
-
please convert those direct manipulations to use provided API methods instead.
|
115
|
+
* Add `in: range` parameter to `numericality` validator.
|
101
116
|
|
102
|
-
|
117
|
+
*Michal Papis*
|
103
118
|
|
104
|
-
|
105
|
-
|
106
|
-
* `errors#values` will be removed.
|
107
|
-
* `errors#keys` will be removed.
|
108
|
-
* `errors#to_xml` will be removed.
|
109
|
-
* `errors#to_h` will be removed, and can be replaced with `errors#to_hash`.
|
110
|
-
* Manipulating `errors` itself as a hash will have no effect (e.g. `errors[:foo] = 'bar'`).
|
111
|
-
* Manipulating the hash returned by `errors#messages` (e.g. `errors.messages[:foo] = 'bar'`) will have no effect.
|
112
|
-
* Manipulating the hash returned by `errors#details` (e.g. `errors.details[:foo].clear`) will have no effect.
|
119
|
+
* Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
|
120
|
+
`plural`, `route_key` and `singular_route_key` values.
|
113
121
|
|
114
|
-
*
|
122
|
+
*Lukas Pokorny*
|
115
123
|
|
124
|
+
* Make ActiveModel::Errors#inspect slimmer for readability
|
125
|
+
|
126
|
+
*lulalala*
|
116
127
|
|
117
|
-
Please check [6-
|
128
|
+
Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
CHANGED
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::
|
19
|
+
to integrate with Action Pack out of the box: <tt>ActiveModel::API</tt>.
|
20
20
|
|
21
21
|
class Person
|
22
|
-
include ActiveModel::
|
22
|
+
include ActiveModel::API
|
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::Model</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::
|
35
|
+
See <tt>ActiveModel::API</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:
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
# == Active \Model \API
|
5
|
+
#
|
6
|
+
# Includes the required interface for an object to interact with
|
7
|
+
# Action Pack and Action View, using different Active Model modules.
|
8
|
+
# It includes model name introspections, conversions, translations and
|
9
|
+
# validations. Besides that, it allows you to initialize the object with a
|
10
|
+
# hash of attributes, pretty much like Active Record does.
|
11
|
+
#
|
12
|
+
# A minimal implementation could be:
|
13
|
+
#
|
14
|
+
# class Person
|
15
|
+
# include ActiveModel::API
|
16
|
+
# attr_accessor :name, :age
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# person = Person.new(name: 'bob', age: '18')
|
20
|
+
# person.name # => "bob"
|
21
|
+
# person.age # => "18"
|
22
|
+
#
|
23
|
+
# Note that, by default, <tt>ActiveModel::API</tt> implements <tt>persisted?</tt>
|
24
|
+
# to return +false+, which is the most common case. You may want to override
|
25
|
+
# it in your class to simulate a different scenario:
|
26
|
+
#
|
27
|
+
# class Person
|
28
|
+
# include ActiveModel::API
|
29
|
+
# attr_accessor :id, :name
|
30
|
+
#
|
31
|
+
# def persisted?
|
32
|
+
# self.id.present?
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# person = Person.new(id: 1, name: 'bob')
|
37
|
+
# person.persisted? # => true
|
38
|
+
#
|
39
|
+
# Also, if for some reason you need to run code on <tt>initialize</tt>, make
|
40
|
+
# sure you call +super+ if you want the attributes hash initialization to
|
41
|
+
# happen.
|
42
|
+
#
|
43
|
+
# class Person
|
44
|
+
# include ActiveModel::API
|
45
|
+
# attr_accessor :id, :name, :omg
|
46
|
+
#
|
47
|
+
# def initialize(attributes={})
|
48
|
+
# super
|
49
|
+
# @omg ||= true
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# person = Person.new(id: 1, name: 'bob')
|
54
|
+
# person.omg # => true
|
55
|
+
#
|
56
|
+
# For more detailed information on other functionalities available, please
|
57
|
+
# refer to the specific modules included in <tt>ActiveModel::API</tt>
|
58
|
+
# (see below).
|
59
|
+
module API
|
60
|
+
extend ActiveSupport::Concern
|
61
|
+
include ActiveModel::AttributeAssignment
|
62
|
+
include ActiveModel::Validations
|
63
|
+
include ActiveModel::Conversion
|
64
|
+
|
65
|
+
included do
|
66
|
+
extend ActiveModel::Naming
|
67
|
+
extend ActiveModel::Translation
|
68
|
+
end
|
69
|
+
|
70
|
+
# Initializes a new model with the given +params+.
|
71
|
+
#
|
72
|
+
# class Person
|
73
|
+
# include ActiveModel::API
|
74
|
+
# attr_accessor :name, :age
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# person = Person.new(name: 'bob', age: '18')
|
78
|
+
# person.name # => "bob"
|
79
|
+
# person.age # => "18"
|
80
|
+
def initialize(attributes = {})
|
81
|
+
assign_attributes(attributes) if attributes
|
82
|
+
|
83
|
+
super()
|
84
|
+
end
|
85
|
+
|
86
|
+
# Indicates if the model is persisted. Default is +false+.
|
87
|
+
#
|
88
|
+
# class Person
|
89
|
+
# include ActiveModel::API
|
90
|
+
# attr_accessor :id, :name
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# person = Person.new(id: 1, name: 'bob')
|
94
|
+
# person.persisted? # => false
|
95
|
+
def persisted?
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -67,6 +67,7 @@ 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"
|
70
71
|
|
71
72
|
included do
|
72
73
|
class_attribute :attribute_aliases, instance_writer: false, default: {}
|
@@ -105,8 +106,8 @@ module ActiveModel
|
|
105
106
|
# person.name # => "Bob"
|
106
107
|
# person.clear_name
|
107
108
|
# person.name # => nil
|
108
|
-
def attribute_method_prefix(*prefixes)
|
109
|
-
self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new
|
109
|
+
def attribute_method_prefix(*prefixes, parameters: nil)
|
110
|
+
self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new(prefix: prefix, parameters: parameters) }
|
110
111
|
undefine_attribute_methods
|
111
112
|
end
|
112
113
|
|
@@ -140,8 +141,8 @@ module ActiveModel
|
|
140
141
|
# person.name = 'Bob'
|
141
142
|
# person.name # => "Bob"
|
142
143
|
# person.name_short? # => true
|
143
|
-
def attribute_method_suffix(*suffixes)
|
144
|
-
self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new
|
144
|
+
def attribute_method_suffix(*suffixes, parameters: nil)
|
145
|
+
self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new(suffix: suffix, parameters: parameters) }
|
145
146
|
undefine_attribute_methods
|
146
147
|
end
|
147
148
|
|
@@ -177,7 +178,7 @@ module ActiveModel
|
|
177
178
|
# person.reset_name_to_default!
|
178
179
|
# person.name # => 'Default Name'
|
179
180
|
def attribute_method_affix(*affixes)
|
180
|
-
self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new
|
181
|
+
self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new(**affix) }
|
181
182
|
undefine_attribute_methods
|
182
183
|
end
|
183
184
|
|
@@ -207,11 +208,33 @@ module ActiveModel
|
|
207
208
|
# person.nickname_short? # => true
|
208
209
|
def alias_attribute(new_name, old_name)
|
209
210
|
self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
|
210
|
-
CodeGenerator.batch(self, __FILE__, __LINE__) do |
|
211
|
+
ActiveSupport::CodeGenerator.batch(self, __FILE__, __LINE__) do |code_generator|
|
211
212
|
attribute_method_matchers.each do |matcher|
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
215
238
|
end
|
216
239
|
end
|
217
240
|
end
|
@@ -251,7 +274,7 @@ module ActiveModel
|
|
251
274
|
# end
|
252
275
|
# end
|
253
276
|
def define_attribute_methods(*attr_names)
|
254
|
-
CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
|
277
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
|
255
278
|
attr_names.flatten.each { |attr_name| define_attribute_method(attr_name, _owner: owner) }
|
256
279
|
end
|
257
280
|
end
|
@@ -286,7 +309,7 @@ module ActiveModel
|
|
286
309
|
# person.name # => "Bob"
|
287
310
|
# person.name_short? # => true
|
288
311
|
def define_attribute_method(attr_name, _owner: generated_attribute_methods)
|
289
|
-
CodeGenerator.batch(_owner, __FILE__, __LINE__) do |owner|
|
312
|
+
ActiveSupport::CodeGenerator.batch(_owner, __FILE__, __LINE__) do |owner|
|
290
313
|
attribute_method_matchers.each do |matcher|
|
291
314
|
method_name = matcher.method_name(attr_name)
|
292
315
|
|
@@ -296,7 +319,7 @@ module ActiveModel
|
|
296
319
|
if respond_to?(generate_method, true)
|
297
320
|
send(generate_method, attr_name.to_s, owner: owner)
|
298
321
|
else
|
299
|
-
define_proxy_call
|
322
|
+
define_proxy_call(owner, method_name, matcher.target, matcher.parameters, attr_name.to_s, namespace: :active_model)
|
300
323
|
end
|
301
324
|
end
|
302
325
|
end
|
@@ -335,46 +358,6 @@ module ActiveModel
|
|
335
358
|
end
|
336
359
|
|
337
360
|
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
|
-
|
378
361
|
def generated_attribute_methods
|
379
362
|
@generated_attribute_methods ||= Module.new.tap { |mod| include mod }
|
380
363
|
end
|
@@ -398,42 +381,48 @@ module ActiveModel
|
|
398
381
|
|
399
382
|
def attribute_method_matchers_matching(method_name)
|
400
383
|
attribute_method_matchers_cache.compute_if_absent(method_name) do
|
401
|
-
attribute_method_matchers.
|
384
|
+
attribute_method_matchers.filter_map { |matcher| matcher.match(method_name) }
|
402
385
|
end
|
403
386
|
end
|
404
387
|
|
405
388
|
# Define a method `name` in `mod` that dispatches to `send`
|
406
|
-
# using the given `extra` args. This falls back on `
|
407
|
-
#
|
408
|
-
def define_proxy_call(
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
"define_method(:'#{name}') do |*args|"
|
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*")}"
|
413
395
|
end
|
414
396
|
|
415
|
-
|
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
|
416
400
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
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
|
407
|
+
|
408
|
+
modifier = parameters == FORWARD_PARAMETERS ? "ruby2_keywords " : ""
|
422
409
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
410
|
+
batch <<
|
411
|
+
"#{modifier}def #{mangled_name}(#{parameters || ''})" <<
|
412
|
+
body <<
|
413
|
+
"end"
|
414
|
+
end
|
428
415
|
end
|
429
416
|
|
430
|
-
class AttributeMethodMatcher
|
431
|
-
attr_reader :prefix, :suffix, :target
|
417
|
+
class AttributeMethodMatcher # :nodoc:
|
418
|
+
attr_reader :prefix, :suffix, :target, :parameters
|
432
419
|
|
433
420
|
AttributeMethodMatch = Struct.new(:target, :attr_name)
|
434
421
|
|
435
|
-
def initialize(
|
436
|
-
@prefix
|
422
|
+
def initialize(prefix: "", suffix: "", parameters: nil)
|
423
|
+
@prefix = prefix
|
424
|
+
@suffix = suffix
|
425
|
+
@parameters = parameters.nil? ? FORWARD_PARAMETERS : parameters
|
437
426
|
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
|
438
427
|
@target = "#{@prefix}attribute#{@suffix}"
|
439
428
|
@method_name = "#{prefix}%s#{suffix}"
|
@@ -469,7 +458,7 @@ module ActiveModel
|
|
469
458
|
match ? attribute_missing(match, *args, &block) : super
|
470
459
|
end
|
471
460
|
end
|
472
|
-
ruby2_keywords(:method_missing)
|
461
|
+
ruby2_keywords(:method_missing)
|
473
462
|
|
474
463
|
# +attribute_missing+ is like +method_missing+, but for attributes. When
|
475
464
|
# +method_missing+ is called we check to see if there is a matching
|
@@ -520,10 +509,6 @@ module ActiveModel
|
|
520
509
|
|
521
510
|
# We want to generate the methods via module_eval rather than
|
522
511
|
# 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.
|
527
512
|
#
|
528
513
|
# But sometimes the database might return columns with
|
529
514
|
# characters that are not allowed in normal method names (like
|
@@ -547,7 +532,6 @@ module ActiveModel
|
|
547
532
|
temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
|
548
533
|
attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
|
549
534
|
yield temp_method_name, attr_name_expr
|
550
|
-
owner.rename_method(temp_method_name, method_name)
|
551
535
|
end
|
552
536
|
end
|
553
537
|
end
|
@@ -144,16 +144,7 @@ module ActiveModel
|
|
144
144
|
end
|
145
145
|
|
146
146
|
def marshal_load(values)
|
147
|
-
|
148
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
149
|
-
Marshalling load from legacy attributes format is deprecated and will be removed in Rails 6.2.
|
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
|
147
|
+
initialize(*values)
|
157
148
|
end
|
158
149
|
|
159
150
|
protected
|
@@ -25,6 +25,10 @@ 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
|
+
|
28
32
|
def to_hash
|
29
33
|
keys.index_with { |name| self[name].value }
|
30
34
|
end
|
@@ -54,7 +58,6 @@ module ActiveModel
|
|
54
58
|
|
55
59
|
def write_cast_value(name, value)
|
56
60
|
@attributes[name] = self[name].with_cast_value(value)
|
57
|
-
value
|
58
61
|
end
|
59
62
|
|
60
63
|
def freeze
|