activemodel 7.1.5.1 → 7.2.3
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 +40 -258
- data/README.rdoc +10 -10
- data/lib/active_model/attribute.rb +1 -1
- data/lib/active_model/attribute_assignment.rb +4 -2
- data/lib/active_model/attribute_methods.rb +38 -39
- data/lib/active_model/attribute_registration.rb +62 -22
- data/lib/active_model/attributes.rb +13 -0
- data/lib/active_model/callbacks.rb +1 -1
- data/lib/active_model/dirty.rb +5 -5
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/model.rb +2 -2
- data/lib/active_model/secure_password.rb +2 -2
- data/lib/active_model/translation.rb +15 -3
- data/lib/active_model/type/float.rb +8 -8
- data/lib/active_model/type/helpers/time_value.rb +33 -12
- data/lib/active_model/type/helpers/timezone.rb +5 -1
- data/lib/active_model/type/registry.rb +2 -3
- data/lib/active_model/validations/callbacks.rb +1 -1
- data/lib/active_model/validations/comparison.rb +1 -1
- data/lib/active_model/validations/validates.rb +1 -1
- data/lib/active_model/validations.rb +4 -2
- metadata +9 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd11eb90ab99961854be28823adafd875c43c0ee7d7e6067b2af752a1f183f88
|
|
4
|
+
data.tar.gz: 690c5e435225163f5ebee8eadeb5ebdcb6b9269140be69471e187d4bce24b7fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c0c5710770bf4a62c45d71ccc25629f73c39f45c9c38f013abd7947c63e72c4e35ec0bfe85da0b32a4ab3c62892aadea62b78ca64e04666015a318339d544c00
|
|
7
|
+
data.tar.gz: e1db829ae10154c73749425d12c1749df1cbbb9f6485c68badac84f2bd945d91ae9c0022eb0d5e886c11edced5e13476be20bf49605e2b318cb2d1dd3e58d9a8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,306 +1,88 @@
|
|
|
1
|
-
## Rails 7.
|
|
1
|
+
## Rails 7.2.3 (October 28, 2025) ##
|
|
2
2
|
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## Rails 7.1.5 (October 30, 2024) ##
|
|
7
|
-
|
|
8
|
-
* Fix regression in `alias_attribute` to work with user defined methods.
|
|
9
|
-
|
|
10
|
-
`alias_attribute` would wrongly assume the attribute accessor was generated by Active Model.
|
|
11
|
-
|
|
12
|
-
```ruby
|
|
13
|
-
class Person
|
|
14
|
-
include ActiveModel::AttributeMethods
|
|
3
|
+
* Fix `has_secure_password` to perform confirmation validation of the password even when blank.
|
|
15
4
|
|
|
16
|
-
|
|
17
|
-
attr_accessor :name
|
|
5
|
+
The validation was incorrectly skipped when the password only contained whitespace characters.
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
end
|
|
7
|
+
*Fabio Sangiovanni*
|
|
21
8
|
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
*Jean Boussier*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Rails 7.1.4.2 (October 23, 2024) ##
|
|
29
|
-
|
|
30
|
-
* No changes.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## Rails 7.1.4.1 (October 15, 2024) ##
|
|
34
|
-
|
|
35
|
-
* No changes.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Rails 7.1.4 (August 22, 2024) ##
|
|
39
|
-
|
|
40
|
-
* No changes.
|
|
9
|
+
* Handle missing attributes for `ActiveModel::Translation#human_attribute_name`.
|
|
41
10
|
|
|
11
|
+
*zzak*
|
|
42
12
|
|
|
43
|
-
|
|
13
|
+
* Fix `ActiveModel::AttributeAssignment#assign_attributes` to accept objects without `each`.
|
|
44
14
|
|
|
45
|
-
*
|
|
15
|
+
*Kouhei Yanagita*
|
|
46
16
|
|
|
47
17
|
|
|
48
|
-
## Rails 7.
|
|
18
|
+
## Rails 7.2.2.2 (August 13, 2025) ##
|
|
49
19
|
|
|
50
20
|
* No changes.
|
|
51
21
|
|
|
52
22
|
|
|
53
|
-
## Rails 7.
|
|
23
|
+
## Rails 7.2.2.1 (December 10, 2024) ##
|
|
54
24
|
|
|
55
25
|
* No changes.
|
|
56
26
|
|
|
57
27
|
|
|
58
|
-
## Rails 7.
|
|
59
|
-
|
|
60
|
-
* No changes.
|
|
28
|
+
## Rails 7.2.2 (October 30, 2024) ##
|
|
61
29
|
|
|
30
|
+
* Fix regression in `alias_attribute` to work with user defined methods.
|
|
62
31
|
|
|
63
|
-
|
|
32
|
+
`alias_attribute` would wrongly assume the attribute accessor was generated by Active Model.
|
|
64
33
|
|
|
65
|
-
|
|
34
|
+
```ruby
|
|
35
|
+
class Person
|
|
36
|
+
include ActiveModel::AttributeMethods
|
|
66
37
|
|
|
38
|
+
define_attribute_methods :name
|
|
39
|
+
attr_accessor :name
|
|
67
40
|
|
|
68
|
-
|
|
41
|
+
alias_attribute :full_name, :name
|
|
42
|
+
end
|
|
69
43
|
|
|
70
|
-
|
|
44
|
+
person.full_name # => NoMethodError: undefined method `attribute' for an instance of Person
|
|
45
|
+
```
|
|
71
46
|
|
|
72
|
-
*
|
|
47
|
+
*Jean Boussier*
|
|
73
48
|
|
|
74
49
|
|
|
75
|
-
## Rails 7.1.
|
|
50
|
+
## Rails 7.2.1.2 (October 23, 2024) ##
|
|
76
51
|
|
|
77
52
|
* No changes.
|
|
78
53
|
|
|
79
54
|
|
|
80
|
-
## Rails 7.1.
|
|
55
|
+
## Rails 7.2.1.1 (October 15, 2024) ##
|
|
81
56
|
|
|
82
57
|
* No changes.
|
|
83
58
|
|
|
84
59
|
|
|
85
|
-
## Rails 7.1
|
|
60
|
+
## Rails 7.2.1 (August 22, 2024) ##
|
|
86
61
|
|
|
87
62
|
* No changes.
|
|
88
63
|
|
|
89
64
|
|
|
90
|
-
## Rails 7.
|
|
91
|
-
|
|
92
|
-
* Remove change in the typography of user facing error messages.
|
|
93
|
-
For example, “can’t be blank” is again “can't be blank”.
|
|
94
|
-
|
|
95
|
-
*Rafael Mendonça França*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
|
99
|
-
|
|
100
|
-
* Support composite identifiers in `to_key`
|
|
101
|
-
|
|
102
|
-
`to_key` avoids wrapping `#id` value into an `Array` if `#id` already an array
|
|
103
|
-
|
|
104
|
-
*Nikita Vasilevsky*
|
|
105
|
-
|
|
106
|
-
* Add `ActiveModel::Conversion.param_delimiter` to configure delimiter being used in `to_param`
|
|
107
|
-
|
|
108
|
-
*Nikita Vasilevsky*
|
|
109
|
-
|
|
110
|
-
* `undefine_attribute_methods` undefines alias attribute methods along with attribute methods.
|
|
111
|
-
|
|
112
|
-
*Nikita Vasilevsky*
|
|
113
|
-
|
|
114
|
-
* Error.full_message now strips ":base" from the message.
|
|
115
|
-
|
|
116
|
-
*zzak*
|
|
117
|
-
|
|
118
|
-
* Add a load hook for `ActiveModel::Model` (named `active_model`) to match the load hook for
|
|
119
|
-
`ActiveRecord::Base` and allow for overriding aspects of the `ActiveModel::Model` class.
|
|
120
|
-
|
|
121
|
-
*Lewis Buckley*
|
|
122
|
-
|
|
123
|
-
* Improve password length validation in ActiveModel::SecurePassword to consider byte size for BCrypt
|
|
124
|
-
compatibility.
|
|
125
|
-
|
|
126
|
-
The previous password length validation only considered the character count, which may not
|
|
127
|
-
accurately reflect the 72-byte size limit imposed by BCrypt. This change updates the validation
|
|
128
|
-
to consider both character count and byte size while keeping the character length validation in place.
|
|
129
|
-
|
|
130
|
-
```ruby
|
|
131
|
-
user = User.new(password: "a" * 73) # 73 characters
|
|
132
|
-
user.valid? # => false
|
|
133
|
-
user.errors[:password] # => ["is too long"]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
user = User.new(password: "あ" * 25) # 25 characters, 75 bytes
|
|
137
|
-
user.valid? # => false
|
|
138
|
-
user.errors[:password] # => ["is too long"]
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
*ChatGPT*, *Guillermo Iguaran*
|
|
142
|
-
|
|
143
|
-
* `has_secure_password` now generates an `#{attribute}_salt` method that returns the salt
|
|
144
|
-
used to compute the password digest. The salt will change whenever the password is changed,
|
|
145
|
-
so it can be used to create single-use password reset tokens with `generates_token_for`:
|
|
146
|
-
|
|
147
|
-
```ruby
|
|
148
|
-
class User < ActiveRecord::Base
|
|
149
|
-
has_secure_password
|
|
150
|
-
|
|
151
|
-
generates_token_for :password_reset, expires_in: 15.minutes do
|
|
152
|
-
password_salt&.last(10)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
*Lázaro Nixon*
|
|
158
|
-
|
|
159
|
-
* Improve typography of user facing error messages. In English contractions,
|
|
160
|
-
the Unicode APOSTROPHE (`U+0027`) is now RIGHT SINGLE QUOTATION MARK
|
|
161
|
-
(`U+2019`). For example, "can't be blank" is now "can’t be blank".
|
|
162
|
-
|
|
163
|
-
*Jon Dufresne*
|
|
164
|
-
|
|
165
|
-
* Add class to `ActiveModel::MissingAttributeError` error message.
|
|
166
|
-
|
|
167
|
-
Show which class is missing the attribute in the error message:
|
|
168
|
-
|
|
169
|
-
```ruby
|
|
170
|
-
user = User.first
|
|
171
|
-
user.pets.select(:id).first.user_id
|
|
172
|
-
# => ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
|
|
173
|
-
```
|
|
65
|
+
## Rails 7.2.0 (August 09, 2024) ##
|
|
174
66
|
|
|
175
|
-
|
|
67
|
+
* Fix a bug where type casting of string to `Time` and `DateTime` doesn't
|
|
68
|
+
calculate minus minute value in TZ offset correctly.
|
|
176
69
|
|
|
177
|
-
*
|
|
178
|
-
results.
|
|
70
|
+
*Akira Matsuda*
|
|
179
71
|
|
|
180
|
-
|
|
72
|
+
* Port the `type_for_attribute` method to Active Model. Classes that include
|
|
73
|
+
`ActiveModel::Attributes` will now provide this method. This method behaves
|
|
74
|
+
the same for Active Model as it does for Active Record.
|
|
181
75
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
76
|
+
```ruby
|
|
77
|
+
class MyModel
|
|
78
|
+
include ActiveModel::Attributes
|
|
185
79
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
```ruby
|
|
189
|
-
class DowncasedString < ActiveModel::Type::String
|
|
190
|
-
def cast(value)
|
|
191
|
-
super&.downcase
|
|
80
|
+
attribute :my_attribute, :integer
|
|
192
81
|
end
|
|
193
|
-
end
|
|
194
82
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
class User < ActiveRecord::Base
|
|
198
|
-
attribute :email, :downcased_string
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
user = User.new(email: "FooBar@example.com")
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Serializing the `email` attribute for the database will be roughly twice as
|
|
205
|
-
fast. More expensive `cast` operations will likely see greater improvements.
|
|
83
|
+
MyModel.type_for_attribute(:my_attribute) # => #<ActiveModel::Type::Integer ...>
|
|
84
|
+
```
|
|
206
85
|
|
|
207
86
|
*Jonathan Hefner*
|
|
208
87
|
|
|
209
|
-
|
|
210
|
-
`password_challenge` accessor and validation.
|
|
211
|
-
|
|
212
|
-
A password challenge is a safeguard to verify that the current user is
|
|
213
|
-
actually the password owner. It can be used when changing sensitive model
|
|
214
|
-
fields, such as the password itself. It is different than a password
|
|
215
|
-
confirmation, which is used to prevent password typos.
|
|
216
|
-
|
|
217
|
-
When `password_challenge` is set, the validation checks that the value's
|
|
218
|
-
digest matches the *currently persisted* `password_digest` (i.e.
|
|
219
|
-
`password_digest_was`).
|
|
220
|
-
|
|
221
|
-
This allows a password challenge to be done as part of a typical `update`
|
|
222
|
-
call, just like a password confirmation. It also allows a password
|
|
223
|
-
challenge error to be handled in the same way as other validation errors.
|
|
224
|
-
|
|
225
|
-
For example, in the controller, instead of:
|
|
226
|
-
|
|
227
|
-
```ruby
|
|
228
|
-
password_params = params.require(:password).permit(
|
|
229
|
-
:password_challenge,
|
|
230
|
-
:password,
|
|
231
|
-
:password_confirmation,
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
password_challenge = password_params.delete(:password_challenge)
|
|
235
|
-
@password_challenge_failed = !current_user.authenticate(password_challenge)
|
|
236
|
-
|
|
237
|
-
if !@password_challenge_failed && current_user.update(password_params)
|
|
238
|
-
# ...
|
|
239
|
-
end
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
You can now write:
|
|
243
|
-
|
|
244
|
-
```ruby
|
|
245
|
-
password_params = params.require(:password).permit(
|
|
246
|
-
:password_challenge,
|
|
247
|
-
:password,
|
|
248
|
-
:password_confirmation,
|
|
249
|
-
).with_defaults(password_challenge: "")
|
|
250
|
-
|
|
251
|
-
if current_user.update(password_params)
|
|
252
|
-
# ...
|
|
253
|
-
end
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
And, in the view, instead of checking `@password_challenge_failed`, you can
|
|
257
|
-
render an error for the `password_challenge` field just as you would for
|
|
258
|
-
other form fields, including utilizing `config.action_view.field_error_proc`.
|
|
259
|
-
|
|
260
|
-
*Jonathan Hefner*
|
|
261
|
-
|
|
262
|
-
* Support infinite ranges for `LengthValidator`s `:in`/`:within` options
|
|
263
|
-
|
|
264
|
-
```ruby
|
|
265
|
-
validates_length_of :first_name, in: ..30
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
*fatkodima*
|
|
269
|
-
|
|
270
|
-
* Add support for beginless ranges to inclusivity/exclusivity validators:
|
|
271
|
-
|
|
272
|
-
```ruby
|
|
273
|
-
validates_inclusion_of :birth_date, in: -> { (..Date.today) }
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
```ruby
|
|
277
|
-
validates_exclusion_of :birth_date, in: -> { (..Date.today) }
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
*Bo Jeanes*
|
|
281
|
-
|
|
282
|
-
* Make validators accept lambdas without record argument
|
|
283
|
-
|
|
284
|
-
```ruby
|
|
285
|
-
# Before
|
|
286
|
-
validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today }
|
|
287
|
-
|
|
288
|
-
# After
|
|
289
|
-
validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
*fatkodima*
|
|
293
|
-
|
|
294
|
-
* Fix casting long strings to `Date`, `Time` or `DateTime`
|
|
295
|
-
|
|
296
|
-
*fatkodima*
|
|
297
|
-
|
|
298
|
-
* Use different cache namespace for proxy calls
|
|
299
|
-
|
|
300
|
-
Models can currently have different attribute bodies for the same method
|
|
301
|
-
names, leading to conflicts. Adding a new namespace `:active_model_proxy`
|
|
302
|
-
fixes the issue.
|
|
303
|
-
|
|
304
|
-
*Chris Salzberg*
|
|
305
|
-
|
|
306
|
-
Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activemodel/CHANGELOG.md) for previous changes.
|
|
88
|
+
Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/README.rdoc
CHANGED
|
@@ -56,7 +56,7 @@ behavior out of the box:
|
|
|
56
56
|
person.clear_name
|
|
57
57
|
person.clear_age
|
|
58
58
|
|
|
59
|
-
{Learn more}[
|
|
59
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/AttributeMethods.html]
|
|
60
60
|
|
|
61
61
|
* Callbacks for certain operations
|
|
62
62
|
|
|
@@ -74,7 +74,7 @@ behavior out of the box:
|
|
|
74
74
|
This generates +before_create+, +around_create+ and +after_create+
|
|
75
75
|
class methods that wrap your create method.
|
|
76
76
|
|
|
77
|
-
{Learn more}[
|
|
77
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Callbacks.html]
|
|
78
78
|
|
|
79
79
|
* Tracking value changes
|
|
80
80
|
|
|
@@ -110,7 +110,7 @@ behavior out of the box:
|
|
|
110
110
|
person.save
|
|
111
111
|
person.previous_changes # => {'name' => ['bob, 'robert']}
|
|
112
112
|
|
|
113
|
-
{Learn more}[
|
|
113
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Dirty.html]
|
|
114
114
|
|
|
115
115
|
* Adding +errors+ interface to objects
|
|
116
116
|
|
|
@@ -141,7 +141,7 @@ behavior out of the box:
|
|
|
141
141
|
person.errors.full_messages
|
|
142
142
|
# => ["Name cannot be nil"]
|
|
143
143
|
|
|
144
|
-
{Learn more}[
|
|
144
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Errors.html]
|
|
145
145
|
|
|
146
146
|
* Model name introspection
|
|
147
147
|
|
|
@@ -152,7 +152,7 @@ behavior out of the box:
|
|
|
152
152
|
NamedPerson.model_name.name # => "NamedPerson"
|
|
153
153
|
NamedPerson.model_name.human # => "Named person"
|
|
154
154
|
|
|
155
|
-
{Learn more}[
|
|
155
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Naming.html]
|
|
156
156
|
|
|
157
157
|
* Making objects serializable
|
|
158
158
|
|
|
@@ -179,7 +179,7 @@ behavior out of the box:
|
|
|
179
179
|
s = SerialPerson.new
|
|
180
180
|
s.to_json # => "{\"name\":null}"
|
|
181
181
|
|
|
182
|
-
{Learn more}[
|
|
182
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Serialization.html]
|
|
183
183
|
|
|
184
184
|
* Internationalization (i18n) support
|
|
185
185
|
|
|
@@ -190,7 +190,7 @@ behavior out of the box:
|
|
|
190
190
|
Person.human_attribute_name('my_attribute')
|
|
191
191
|
# => "My attribute"
|
|
192
192
|
|
|
193
|
-
{Learn more}[
|
|
193
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Translation.html]
|
|
194
194
|
|
|
195
195
|
* Validation support
|
|
196
196
|
|
|
@@ -208,7 +208,7 @@ behavior out of the box:
|
|
|
208
208
|
person.first_name = 'zoolander'
|
|
209
209
|
person.valid? # => false
|
|
210
210
|
|
|
211
|
-
{Learn more}[
|
|
211
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Validations.html]
|
|
212
212
|
|
|
213
213
|
* Custom validators
|
|
214
214
|
|
|
@@ -230,7 +230,7 @@ behavior out of the box:
|
|
|
230
230
|
p.name = "Bob"
|
|
231
231
|
p.valid? # => true
|
|
232
232
|
|
|
233
|
-
{Learn more}[
|
|
233
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Validator.html]
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
== Download and installation
|
|
@@ -261,6 +261,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
261
261
|
|
|
262
262
|
* https://github.com/rails/rails/issues
|
|
263
263
|
|
|
264
|
-
Feature requests should be discussed on the
|
|
264
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
265
265
|
|
|
266
266
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -38,15 +38,17 @@ module ActiveModel
|
|
|
38
38
|
|
|
39
39
|
private
|
|
40
40
|
def _assign_attributes(attributes)
|
|
41
|
-
attributes.
|
|
41
|
+
attributes.each_pair do |k, v|
|
|
42
42
|
_assign_attribute(k, v)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def _assign_attribute(k, v)
|
|
47
47
|
setter = :"#{k}="
|
|
48
|
+
public_send(setter, v)
|
|
49
|
+
rescue NoMethodError
|
|
48
50
|
if respond_to?(setter)
|
|
49
|
-
|
|
51
|
+
raise
|
|
50
52
|
else
|
|
51
53
|
raise UnknownAttributeError.new(self, k.to_s)
|
|
52
54
|
end
|
|
@@ -66,7 +66,6 @@ module ActiveModel
|
|
|
66
66
|
|
|
67
67
|
NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
|
|
68
68
|
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
|
|
69
|
-
FORWARD_PARAMETERS = "*args"
|
|
70
69
|
|
|
71
70
|
included do
|
|
72
71
|
class_attribute :attribute_aliases, instance_writer: false, default: {}
|
|
@@ -228,28 +227,13 @@ module ActiveModel
|
|
|
228
227
|
method_name = pattern.method_name(new_name).to_s
|
|
229
228
|
target_name = pattern.method_name(old_name).to_s
|
|
230
229
|
parameters = pattern.parameters
|
|
231
|
-
mangled_name = target_name
|
|
232
230
|
|
|
233
|
-
|
|
234
|
-
mangled_name = "__temp__#{target_name.unpack1("h*")}"
|
|
235
|
-
end
|
|
231
|
+
mangled_name = build_mangled_name(target_name)
|
|
236
232
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
"self.#{target_name}(#{parameters || ''})"
|
|
240
|
-
else
|
|
241
|
-
call_args = [":'#{target_name}'"]
|
|
242
|
-
call_args << parameters if parameters
|
|
243
|
-
"send(#{call_args.join(", ")})"
|
|
244
|
-
end
|
|
233
|
+
call_args = []
|
|
234
|
+
call_args << parameters if parameters
|
|
245
235
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
batch <<
|
|
249
|
-
"#{modifier}def #{mangled_name}(#{parameters || ''})" <<
|
|
250
|
-
body <<
|
|
251
|
-
"end"
|
|
252
|
-
end
|
|
236
|
+
define_call(code_generator, method_name, target_name, mangled_name, parameters, call_args, namespace: :alias_attribute, as: method_name)
|
|
253
237
|
end
|
|
254
238
|
|
|
255
239
|
# Is +new_name+ an alias?
|
|
@@ -347,6 +331,7 @@ module ActiveModel
|
|
|
347
331
|
end
|
|
348
332
|
|
|
349
333
|
generate_method = "define_method_#{pattern.proxy_target}"
|
|
334
|
+
|
|
350
335
|
if respond_to?(generate_method, true)
|
|
351
336
|
send(generate_method, attr_name.to_s, owner: owner, as: as)
|
|
352
337
|
else
|
|
@@ -357,7 +342,7 @@ module ActiveModel
|
|
|
357
342
|
pattern.parameters,
|
|
358
343
|
attr_name.to_s,
|
|
359
344
|
namespace: :active_model_proxy,
|
|
360
|
-
as: public_method_name
|
|
345
|
+
as: public_method_name
|
|
361
346
|
)
|
|
362
347
|
end
|
|
363
348
|
end
|
|
@@ -403,6 +388,8 @@ module ActiveModel
|
|
|
403
388
|
super
|
|
404
389
|
base.class_eval do
|
|
405
390
|
@attribute_method_patterns_cache = nil
|
|
391
|
+
@aliases_by_attribute_name = nil
|
|
392
|
+
@generated_attribute_methods = nil
|
|
406
393
|
end
|
|
407
394
|
end
|
|
408
395
|
|
|
@@ -441,27 +428,41 @@ module ActiveModel
|
|
|
441
428
|
# using the given `extra` args. This falls back on `send`
|
|
442
429
|
# if the called name cannot be compiled.
|
|
443
430
|
def define_proxy_call(code_generator, name, proxy_target, parameters, *call_args, namespace:, as: name)
|
|
444
|
-
mangled_name = name
|
|
445
|
-
unless NAME_COMPILABLE_REGEXP.match?(name)
|
|
446
|
-
mangled_name = "__temp__#{name.unpack1("h*")}"
|
|
447
|
-
end
|
|
431
|
+
mangled_name = build_mangled_name(name)
|
|
448
432
|
|
|
449
433
|
call_args.map!(&:inspect)
|
|
450
434
|
call_args << parameters if parameters
|
|
435
|
+
|
|
436
|
+
# We have to use a different namespace for every target method, because
|
|
437
|
+
# if someone defines an attribute that look like an attribute method we could clash, e.g.
|
|
438
|
+
# attribute :title_was
|
|
439
|
+
# attribute :title
|
|
451
440
|
namespace = :"#{namespace}_#{proxy_target}"
|
|
452
441
|
|
|
442
|
+
define_call(code_generator, name, proxy_target, mangled_name, parameters, call_args, namespace: namespace, as: as)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def build_mangled_name(name)
|
|
446
|
+
mangled_name = name
|
|
447
|
+
|
|
448
|
+
unless NAME_COMPILABLE_REGEXP.match?(name)
|
|
449
|
+
mangled_name = :"__temp__#{name.unpack1("h*")}"
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
mangled_name
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def define_call(code_generator, name, target_name, mangled_name, parameters, call_args, namespace:, as:)
|
|
453
456
|
code_generator.define_cached_method(mangled_name, as: as, namespace: namespace) do |batch|
|
|
454
|
-
body = if CALL_COMPILABLE_REGEXP.match?(
|
|
455
|
-
"self.#{
|
|
457
|
+
body = if CALL_COMPILABLE_REGEXP.match?(target_name)
|
|
458
|
+
"self.#{target_name}(#{call_args.join(", ")})"
|
|
456
459
|
else
|
|
457
|
-
call_args.unshift(":'#{
|
|
460
|
+
call_args.unshift(":'#{target_name}'")
|
|
458
461
|
"send(#{call_args.join(", ")})"
|
|
459
462
|
end
|
|
460
463
|
|
|
461
|
-
modifier = parameters == FORWARD_PARAMETERS ? "ruby2_keywords " : ""
|
|
462
|
-
|
|
463
464
|
batch <<
|
|
464
|
-
"
|
|
465
|
+
"def #{mangled_name}(#{parameters || ''})" <<
|
|
465
466
|
body <<
|
|
466
467
|
"end"
|
|
467
468
|
end
|
|
@@ -475,7 +476,7 @@ module ActiveModel
|
|
|
475
476
|
def initialize(prefix: "", suffix: "", parameters: nil)
|
|
476
477
|
@prefix = prefix
|
|
477
478
|
@suffix = suffix
|
|
478
|
-
@parameters = parameters.nil? ?
|
|
479
|
+
@parameters = parameters.nil? ? "..." : parameters
|
|
479
480
|
@regex = /\A(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})\z/
|
|
480
481
|
@proxy_target = "#{@prefix}attribute#{@suffix}"
|
|
481
482
|
@method_name = "#{prefix}%s#{suffix}"
|
|
@@ -503,24 +504,22 @@ module ActiveModel
|
|
|
503
504
|
# It's also possible to instantiate related objects, so a <tt>Client</tt>
|
|
504
505
|
# class belonging to the +clients+ table with a +master_id+ foreign key
|
|
505
506
|
# can instantiate master through <tt>Client#master</tt>.
|
|
506
|
-
def method_missing(method,
|
|
507
|
+
def method_missing(method, ...)
|
|
507
508
|
if respond_to_without_attributes?(method, true)
|
|
508
509
|
super
|
|
509
510
|
else
|
|
510
|
-
match = matched_attribute_method(method.
|
|
511
|
-
match ? attribute_missing(match,
|
|
511
|
+
match = matched_attribute_method(method.name)
|
|
512
|
+
match ? attribute_missing(match, ...) : super
|
|
512
513
|
end
|
|
513
514
|
end
|
|
514
|
-
ruby2_keywords(:method_missing)
|
|
515
515
|
|
|
516
516
|
# +attribute_missing+ is like +method_missing+, but for attributes. When
|
|
517
517
|
# +method_missing+ is called we check to see if there is a matching
|
|
518
518
|
# attribute method. If so, we tell +attribute_missing+ to dispatch the
|
|
519
519
|
# attribute. This method can be overloaded to customize the behavior.
|
|
520
|
-
def attribute_missing(match,
|
|
521
|
-
__send__(match.proxy_target, match.attr_name,
|
|
520
|
+
def attribute_missing(match, ...)
|
|
521
|
+
__send__(match.proxy_target, match.attr_name, ...)
|
|
522
522
|
end
|
|
523
|
-
ruby2_keywords(:attribute_missing)
|
|
524
523
|
|
|
525
524
|
# A +Person+ instance with a +name+ attribute can ask
|
|
526
525
|
# <tt>person.respond_to?(:name)</tt>, <tt>person.respond_to?(:name=)</tt>,
|
|
@@ -10,17 +10,28 @@ module ActiveModel
|
|
|
10
10
|
|
|
11
11
|
module ClassMethods # :nodoc:
|
|
12
12
|
def attribute(name, type = nil, default: (no_default = true), **options)
|
|
13
|
+
name = resolve_attribute_name(name)
|
|
13
14
|
type = resolve_type_name(type, **options) if type.is_a?(Symbol)
|
|
15
|
+
type = hook_attribute_type(name, type) if type
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
pending_attribute_modifications << PendingType.new(name, type) if type || no_default
|
|
18
|
+
pending_attribute_modifications << PendingDefault.new(name, default) unless no_default
|
|
19
|
+
|
|
20
|
+
reset_default_attributes
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def decorate_attributes(names = nil, &decorator) # :nodoc:
|
|
24
|
+
names = names&.map { |name| resolve_attribute_name(name) }
|
|
25
|
+
|
|
26
|
+
pending_attribute_modifications << PendingDecorator.new(names, decorator)
|
|
18
27
|
|
|
19
28
|
reset_default_attributes
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
def _default_attributes # :nodoc:
|
|
23
|
-
@default_attributes ||=
|
|
32
|
+
@default_attributes ||= AttributeSet.new({}).tap do |attribute_set|
|
|
33
|
+
apply_pending_attribute_modifications(attribute_set)
|
|
34
|
+
end
|
|
24
35
|
end
|
|
25
36
|
|
|
26
37
|
def attribute_types # :nodoc:
|
|
@@ -29,40 +40,62 @@ module ActiveModel
|
|
|
29
40
|
end
|
|
30
41
|
end
|
|
31
42
|
|
|
43
|
+
def type_for_attribute(attribute_name, &block)
|
|
44
|
+
attribute_name = resolve_attribute_name(attribute_name)
|
|
45
|
+
|
|
46
|
+
if block
|
|
47
|
+
attribute_types.fetch(attribute_name, &block)
|
|
48
|
+
else
|
|
49
|
+
attribute_types[attribute_name]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
32
53
|
private
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
PendingType = Struct.new(:name, :type) do # :nodoc:
|
|
55
|
+
def apply_to(attribute_set)
|
|
56
|
+
attribute = attribute_set[name]
|
|
57
|
+
attribute_set[name] = attribute.with_type(type || attribute.type)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
35
60
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
attribute
|
|
61
|
+
PendingDefault = Struct.new(:name, :default) do # :nodoc:
|
|
62
|
+
def apply_to(attribute_set)
|
|
63
|
+
attribute_set[name] = attribute_set[name].with_user_default(default)
|
|
40
64
|
end
|
|
41
65
|
end
|
|
42
66
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
PendingDecorator = Struct.new(:names, :decorator) do # :nodoc:
|
|
68
|
+
def apply_to(attribute_set)
|
|
69
|
+
(names || attribute_set.keys).each do |name|
|
|
70
|
+
attribute = attribute_set[name]
|
|
71
|
+
type = decorator.call(name, attribute.type)
|
|
72
|
+
attribute_set[name] = attribute.with_type(type) if type
|
|
73
|
+
end
|
|
74
|
+
end
|
|
46
75
|
end
|
|
47
76
|
|
|
48
|
-
def
|
|
49
|
-
|
|
77
|
+
def pending_attribute_modifications
|
|
78
|
+
@pending_attribute_modifications ||= []
|
|
79
|
+
end
|
|
50
80
|
|
|
51
|
-
|
|
52
|
-
|
|
81
|
+
def apply_pending_attribute_modifications(attribute_set)
|
|
82
|
+
if superclass.respond_to?(:apply_pending_attribute_modifications, true)
|
|
83
|
+
superclass.send(:apply_pending_attribute_modifications, attribute_set)
|
|
53
84
|
end
|
|
54
85
|
|
|
55
|
-
|
|
86
|
+
pending_attribute_modifications.each do |modification|
|
|
87
|
+
modification.apply_to(attribute_set)
|
|
88
|
+
end
|
|
56
89
|
end
|
|
57
90
|
|
|
58
|
-
def
|
|
59
|
-
|
|
91
|
+
def reset_default_attributes
|
|
92
|
+
reset_default_attributes!
|
|
93
|
+
subclasses.each { |subclass| subclass.send(:reset_default_attributes) }
|
|
60
94
|
end
|
|
61
95
|
|
|
62
|
-
def reset_default_attributes
|
|
96
|
+
def reset_default_attributes!
|
|
63
97
|
@default_attributes = nil
|
|
64
98
|
@attribute_types = nil
|
|
65
|
-
subclasses.each { |subclass| subclass.send(__method__) }
|
|
66
99
|
end
|
|
67
100
|
|
|
68
101
|
def resolve_attribute_name(name)
|
|
@@ -72,6 +105,13 @@ module ActiveModel
|
|
|
72
105
|
def resolve_type_name(name, **options)
|
|
73
106
|
Type.lookup(name, **options)
|
|
74
107
|
end
|
|
108
|
+
|
|
109
|
+
# Hook for other modules to override. The attribute type is passed
|
|
110
|
+
# through this method immediately after it is resolved, before any type
|
|
111
|
+
# decorations are applied.
|
|
112
|
+
def hook_attribute_type(attribute, type)
|
|
113
|
+
type
|
|
114
|
+
end
|
|
75
115
|
end
|
|
76
116
|
end
|
|
77
117
|
end
|
|
@@ -75,6 +75,19 @@ module ActiveModel
|
|
|
75
75
|
attribute_types.keys
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
##
|
|
79
|
+
# :method: type_for_attribute
|
|
80
|
+
# :call-seq: type_for_attribute(attribute_name, &block)
|
|
81
|
+
#
|
|
82
|
+
# Returns the type of the specified attribute after applying any
|
|
83
|
+
# modifiers. This method is the only valid source of information for
|
|
84
|
+
# anything related to the types of a model's attributes. The return value
|
|
85
|
+
# of this method will implement the interface described by
|
|
86
|
+
# ActiveModel::Type::Value (though the object itself may not subclass it).
|
|
87
|
+
#--
|
|
88
|
+
# Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
|
|
89
|
+
|
|
90
|
+
##
|
|
78
91
|
private
|
|
79
92
|
def define_method_attribute=(canonical_name, owner:, as: canonical_name)
|
|
80
93
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
|
@@ -60,7 +60,7 @@ module ActiveModel
|
|
|
60
60
|
# Would only create the +after_create+ and +before_create+ callback methods in
|
|
61
61
|
# your class.
|
|
62
62
|
#
|
|
63
|
-
# NOTE:
|
|
63
|
+
# NOTE: Defining the same callback multiple times will overwrite previous callback definitions.
|
|
64
64
|
#
|
|
65
65
|
module Callbacks
|
|
66
66
|
def self.extended(base) # :nodoc:
|
data/lib/active_model/dirty.rb
CHANGED
|
@@ -108,7 +108,7 @@ module ActiveModel
|
|
|
108
108
|
# person.changes # => {"name" => ["Bill", "Bob"]}
|
|
109
109
|
#
|
|
110
110
|
# If an attribute is modified in-place then make use of
|
|
111
|
-
# {*_will_change!}[rdoc-
|
|
111
|
+
# {*_will_change!}[rdoc-ref:#*_will_change!] to mark that the attribute is changing.
|
|
112
112
|
# Otherwise \Active \Model can't track changes to in-place attributes. Note
|
|
113
113
|
# that Active Record can detect in-place modifications automatically. You do
|
|
114
114
|
# not need to call <tt>*_will_change!</tt> on Active Record models.
|
|
@@ -289,22 +289,22 @@ module ActiveModel
|
|
|
289
289
|
mutations_from_database.changed_attribute_names
|
|
290
290
|
end
|
|
291
291
|
|
|
292
|
-
# Dispatch target for {*_changed?}[rdoc-
|
|
292
|
+
# Dispatch target for {*_changed?}[rdoc-ref:#*_changed?] attribute methods.
|
|
293
293
|
def attribute_changed?(attr_name, **options)
|
|
294
294
|
mutations_from_database.changed?(attr_name.to_s, **options)
|
|
295
295
|
end
|
|
296
296
|
|
|
297
|
-
# Dispatch target for {*_was}[rdoc-
|
|
297
|
+
# Dispatch target for {*_was}[rdoc-ref:#*_was] attribute methods.
|
|
298
298
|
def attribute_was(attr_name)
|
|
299
299
|
mutations_from_database.original_value(attr_name.to_s)
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
-
# Dispatch target for {*_previously_changed?}[rdoc-
|
|
302
|
+
# Dispatch target for {*_previously_changed?}[rdoc-ref:#*_previously_changed?] attribute methods.
|
|
303
303
|
def attribute_previously_changed?(attr_name, **options)
|
|
304
304
|
mutations_before_last_save.changed?(attr_name.to_s, **options)
|
|
305
305
|
end
|
|
306
306
|
|
|
307
|
-
# Dispatch target for {*_previously_was}[rdoc-
|
|
307
|
+
# Dispatch target for {*_previously_was}[rdoc-ref:#*_previously_was] attribute methods.
|
|
308
308
|
def attribute_previously_was(attr_name)
|
|
309
309
|
mutations_before_last_save.original_value(attr_name.to_s)
|
|
310
310
|
end
|
data/lib/active_model/model.rb
CHANGED
|
@@ -54,7 +54,7 @@ module ActiveModel
|
|
|
54
54
|
#
|
|
55
55
|
# person = Person.new(id: 1, name: "bob")
|
|
56
56
|
# person.slice(:id, :name)
|
|
57
|
-
# => { "id" => 1, "name" => "bob" }
|
|
57
|
+
# # => { "id" => 1, "name" => "bob" }
|
|
58
58
|
#
|
|
59
59
|
#--
|
|
60
60
|
# Implemented by ActiveModel::Access#slice.
|
|
@@ -68,7 +68,7 @@ module ActiveModel
|
|
|
68
68
|
#
|
|
69
69
|
# person = Person.new(id: 1, name: "bob")
|
|
70
70
|
# person.values_at(:id, :name)
|
|
71
|
-
# => [1, "bob"]
|
|
71
|
+
# # => [1, "bob"]
|
|
72
72
|
#
|
|
73
73
|
#--
|
|
74
74
|
# Implemented by ActiveModel::Access#values_at.
|
|
@@ -41,7 +41,7 @@ module ActiveModel
|
|
|
41
41
|
#
|
|
42
42
|
# To use +has_secure_password+, add bcrypt (~> 3.1.7) to your Gemfile:
|
|
43
43
|
#
|
|
44
|
-
# gem
|
|
44
|
+
# gem "bcrypt", "~> 3.1.7"
|
|
45
45
|
#
|
|
46
46
|
# ==== Examples
|
|
47
47
|
#
|
|
@@ -140,7 +140,7 @@ module ActiveModel
|
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
validates_confirmation_of attribute,
|
|
143
|
+
validates_confirmation_of attribute, allow_nil: true
|
|
144
144
|
end
|
|
145
145
|
end
|
|
146
146
|
end
|
|
@@ -50,10 +50,19 @@ module ActiveModel
|
|
|
50
50
|
namespace, _, attribute = attribute.rpartition(".")
|
|
51
51
|
namespace.tr!(".", "/")
|
|
52
52
|
|
|
53
|
+
if attribute.present?
|
|
54
|
+
key = "#{namespace}.#{attribute}"
|
|
55
|
+
separator = "/"
|
|
56
|
+
else
|
|
57
|
+
key = namespace
|
|
58
|
+
separator = "."
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
defaults = lookup_ancestors.map do |klass|
|
|
54
|
-
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}
|
|
62
|
+
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}#{separator}#{key}"
|
|
55
63
|
end
|
|
56
|
-
defaults << :"#{i18n_scope}.attributes.#{
|
|
64
|
+
defaults << :"#{i18n_scope}.attributes.#{key}"
|
|
65
|
+
defaults << :"attributes.#{key}"
|
|
57
66
|
else
|
|
58
67
|
defaults = lookup_ancestors.map do |klass|
|
|
59
68
|
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
|
|
@@ -65,7 +74,10 @@ module ActiveModel
|
|
|
65
74
|
defaults << MISSING_TRANSLATION
|
|
66
75
|
|
|
67
76
|
translation = I18n.translate(defaults.shift, count: 1, **options, default: defaults)
|
|
68
|
-
|
|
77
|
+
if translation == MISSING_TRANSLATION
|
|
78
|
+
translation = attribute.present? ? attribute.humanize : namespace.humanize
|
|
79
|
+
end
|
|
80
|
+
|
|
69
81
|
translation
|
|
70
82
|
end
|
|
71
83
|
end
|
|
@@ -15,14 +15,6 @@ module ActiveModel
|
|
|
15
15
|
# attribute :weight, :float
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
|
-
# Values are cast using their +to_f+ method, except for the following
|
|
19
|
-
# strings:
|
|
20
|
-
#
|
|
21
|
-
# - Blank strings are cast to +nil+.
|
|
22
|
-
# - <tt>"Infinity"</tt> is cast to +Float::INFINITY+.
|
|
23
|
-
# - <tt>"-Infinity"</tt> is cast to <tt>-Float::INFINITY</tt>.
|
|
24
|
-
# - <tt>"NaN"</tt> is cast to +Float::NAN+.
|
|
25
|
-
#
|
|
26
18
|
# bag = BagOfCoffee.new
|
|
27
19
|
#
|
|
28
20
|
# bag.weight = "0.25"
|
|
@@ -33,6 +25,14 @@ module ActiveModel
|
|
|
33
25
|
#
|
|
34
26
|
# bag.weight = "NaN"
|
|
35
27
|
# bag.weight # => Float::NAN
|
|
28
|
+
#
|
|
29
|
+
# Values are cast using their +to_f+ method, except for the following
|
|
30
|
+
# strings:
|
|
31
|
+
#
|
|
32
|
+
# - Blank strings are cast to +nil+.
|
|
33
|
+
# - <tt>"Infinity"</tt> is cast to +Float::INFINITY+.
|
|
34
|
+
# - <tt>"-Infinity"</tt> is cast to <tt>-Float::INFINITY</tt>.
|
|
35
|
+
# - <tt>"NaN"</tt> is cast to +Float::NAN+.
|
|
36
36
|
class Float < Value
|
|
37
37
|
include Helpers::Numeric
|
|
38
38
|
|
|
@@ -70,18 +70,33 @@ module ActiveModel
|
|
|
70
70
|
/x
|
|
71
71
|
|
|
72
72
|
if RUBY_VERSION >= "3.2"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
if Time.new(2000, 1, 1, 0, 0, 0, "-00:00").yday != 1 # Early 3.2.x had a bug
|
|
74
|
+
# BUG: Wrapping the Time object with Time.at because Time.new with `in:` in Ruby 3.2.0
|
|
75
|
+
# used to return an invalid Time object
|
|
76
|
+
# see: https://bugs.ruby-lang.org/issues/19292
|
|
77
|
+
def fast_string_to_time(string)
|
|
78
|
+
return unless string.include?("-") # Time.new("1234") # => 1234-01-01 00:00:00
|
|
79
|
+
|
|
80
|
+
if is_utc?
|
|
81
|
+
::Time.at(::Time.new(string, in: "UTC"))
|
|
82
|
+
else
|
|
83
|
+
::Time.new(string)
|
|
84
|
+
end
|
|
85
|
+
rescue ArgumentError
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
def fast_string_to_time(string)
|
|
90
|
+
return unless string.include?("-") # Time.new("1234") # => 1234-01-01 00:00:00
|
|
91
|
+
|
|
92
|
+
if is_utc?
|
|
93
|
+
::Time.new(string, in: "UTC")
|
|
94
|
+
else
|
|
95
|
+
::Time.new(string)
|
|
96
|
+
end
|
|
97
|
+
rescue ArgumentError
|
|
98
|
+
nil
|
|
82
99
|
end
|
|
83
|
-
rescue ArgumentError
|
|
84
|
-
nil
|
|
85
100
|
end
|
|
86
101
|
else
|
|
87
102
|
def fast_string_to_time(string)
|
|
@@ -94,7 +109,13 @@ module ActiveModel
|
|
|
94
109
|
end
|
|
95
110
|
|
|
96
111
|
if $8
|
|
97
|
-
offset =
|
|
112
|
+
offset = \
|
|
113
|
+
if $8 == "Z"
|
|
114
|
+
0
|
|
115
|
+
else
|
|
116
|
+
offset_h, offset_m = $8.to_i, $9.to_i
|
|
117
|
+
offset_h.to_i * 3600 + (offset_h.negative? ? -1 : 1) * offset_m * 60
|
|
118
|
+
end
|
|
98
119
|
end
|
|
99
120
|
|
|
100
121
|
new_time($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, usec, offset)
|
|
@@ -7,7 +7,11 @@ module ActiveModel
|
|
|
7
7
|
module Helpers # :nodoc: all
|
|
8
8
|
module Timezone
|
|
9
9
|
def is_utc?
|
|
10
|
-
|
|
10
|
+
if default = ::Time.zone_default
|
|
11
|
+
default.name == "UTC"
|
|
12
|
+
else
|
|
13
|
+
true
|
|
14
|
+
end
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def default_timezone
|
|
@@ -20,16 +20,15 @@ module ActiveModel
|
|
|
20
20
|
registrations[type_name] = block
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def lookup(symbol,
|
|
23
|
+
def lookup(symbol, ...)
|
|
24
24
|
registration = registrations[symbol]
|
|
25
25
|
|
|
26
26
|
if registration
|
|
27
|
-
registration.call(symbol,
|
|
27
|
+
registration.call(symbol, ...)
|
|
28
28
|
else
|
|
29
29
|
raise ArgumentError, "Unknown type #{symbol.inspect}"
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
|
-
ruby2_keywords(:lookup)
|
|
33
32
|
|
|
34
33
|
private
|
|
35
34
|
attr_reader :registrations
|
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
|
10
10
|
include ResolveValue
|
|
11
11
|
|
|
12
12
|
def check_validity!
|
|
13
|
-
unless
|
|
13
|
+
unless options.keys.intersect?(COMPARE_CHECKS.keys)
|
|
14
14
|
raise ArgumentError, "Expected one of :greater_than, :greater_than_or_equal_to, "\
|
|
15
15
|
":equal_to, :less_than, :less_than_or_equal_to, or :other_than option to be supplied."
|
|
16
16
|
end
|
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
|
10
10
|
# validators can be overridden inside specific classes by creating
|
|
11
11
|
# custom validator classes in their place such as PresenceValidator.
|
|
12
12
|
#
|
|
13
|
-
# Examples of using the default
|
|
13
|
+
# Examples of using the default Rails validators:
|
|
14
14
|
#
|
|
15
15
|
# validates :username, absence: true
|
|
16
16
|
# validates :terms, acceptance: true
|
|
@@ -84,7 +84,8 @@ module ActiveModel
|
|
|
84
84
|
# end
|
|
85
85
|
# end
|
|
86
86
|
#
|
|
87
|
-
# Options
|
|
87
|
+
# ==== Options
|
|
88
|
+
#
|
|
88
89
|
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
|
89
90
|
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
90
91
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
|
@@ -150,7 +151,8 @@ module ActiveModel
|
|
|
150
151
|
# Note that the return value of validation methods is not relevant.
|
|
151
152
|
# It's not possible to halt the validate callback chain.
|
|
152
153
|
#
|
|
153
|
-
# Options
|
|
154
|
+
# ==== Options
|
|
155
|
+
#
|
|
154
156
|
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
|
155
157
|
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
156
158
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activemodel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.
|
|
4
|
+
version: 7.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activesupport
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - '='
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 7.
|
|
18
|
+
version: 7.2.3
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - '='
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 7.
|
|
25
|
+
version: 7.2.3
|
|
27
26
|
description: A toolkit for building modeling frameworks like Active Record. Rich support
|
|
28
27
|
for attributes, callbacks, validations, serialization, internationalization, and
|
|
29
28
|
testing.
|
|
@@ -112,12 +111,11 @@ licenses:
|
|
|
112
111
|
- MIT
|
|
113
112
|
metadata:
|
|
114
113
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
|
115
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.
|
|
116
|
-
documentation_uri: https://api.rubyonrails.org/v7.
|
|
114
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.2.3/activemodel/CHANGELOG.md
|
|
115
|
+
documentation_uri: https://api.rubyonrails.org/v7.2.3/
|
|
117
116
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
|
118
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.
|
|
117
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.2.3/activemodel
|
|
119
118
|
rubygems_mfa_required: 'true'
|
|
120
|
-
post_install_message:
|
|
121
119
|
rdoc_options: []
|
|
122
120
|
require_paths:
|
|
123
121
|
- lib
|
|
@@ -125,15 +123,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
125
123
|
requirements:
|
|
126
124
|
- - ">="
|
|
127
125
|
- !ruby/object:Gem::Version
|
|
128
|
-
version:
|
|
126
|
+
version: 3.1.0
|
|
129
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
128
|
requirements:
|
|
131
129
|
- - ">="
|
|
132
130
|
- !ruby/object:Gem::Version
|
|
133
131
|
version: '0'
|
|
134
132
|
requirements: []
|
|
135
|
-
rubygems_version: 3.
|
|
136
|
-
signing_key:
|
|
133
|
+
rubygems_version: 3.6.9
|
|
137
134
|
specification_version: 4
|
|
138
135
|
summary: A toolkit for building modeling frameworks (part of Rails).
|
|
139
136
|
test_files: []
|