activemodel 7.0.7 → 7.1.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -142
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/active_model/access.rb +16 -0
- data/lib/active_model/api.rb +5 -5
- data/lib/active_model/attribute/user_provided_default.rb +4 -0
- data/lib/active_model/attribute.rb +26 -1
- data/lib/active_model/attribute_assignment.rb +1 -1
- data/lib/active_model/attribute_methods.rb +102 -63
- data/lib/active_model/attribute_registration.rb +77 -0
- data/lib/active_model/attribute_set.rb +10 -1
- data/lib/active_model/attributes.rb +62 -45
- data/lib/active_model/callbacks.rb +5 -5
- data/lib/active_model/conversion.rb +14 -4
- data/lib/active_model/deprecator.rb +7 -0
- data/lib/active_model/dirty.rb +134 -13
- data/lib/active_model/error.rb +4 -3
- data/lib/active_model/errors.rb +37 -6
- data/lib/active_model/forbidden_attributes_protection.rb +2 -0
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/lint.rb +1 -1
- data/lib/active_model/locale/en.yml +1 -0
- data/lib/active_model/model.rb +34 -2
- data/lib/active_model/naming.rb +29 -10
- data/lib/active_model/railtie.rb +4 -0
- data/lib/active_model/secure_password.rb +61 -23
- data/lib/active_model/serialization.rb +3 -3
- data/lib/active_model/serializers/json.rb +1 -1
- data/lib/active_model/translation.rb +18 -16
- data/lib/active_model/type/big_integer.rb +23 -1
- data/lib/active_model/type/binary.rb +7 -1
- data/lib/active_model/type/boolean.rb +11 -9
- data/lib/active_model/type/date.rb +28 -2
- data/lib/active_model/type/date_time.rb +45 -3
- data/lib/active_model/type/decimal.rb +39 -1
- data/lib/active_model/type/float.rb +30 -1
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +5 -1
- data/lib/active_model/type/helpers/numeric.rb +6 -1
- data/lib/active_model/type/helpers/time_value.rb +28 -12
- data/lib/active_model/type/immutable_string.rb +37 -1
- data/lib/active_model/type/integer.rb +44 -1
- data/lib/active_model/type/serialize_cast_value.rb +47 -0
- data/lib/active_model/type/string.rb +9 -1
- data/lib/active_model/type/time.rb +48 -7
- data/lib/active_model/type/value.rb +17 -1
- data/lib/active_model/type.rb +1 -0
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/callbacks.rb +4 -4
- data/lib/active_model/validations/clusivity.rb +5 -8
- data/lib/active_model/validations/comparability.rb +0 -11
- data/lib/active_model/validations/comparison.rb +15 -7
- data/lib/active_model/validations/format.rb +6 -7
- data/lib/active_model/validations/length.rb +10 -8
- data/lib/active_model/validations/numericality.rb +35 -23
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/resolve_value.rb +26 -0
- data/lib/active_model/validations/validates.rb +4 -4
- data/lib/active_model/validations/with.rb +9 -2
- data/lib/active_model/validations.rb +44 -9
- data/lib/active_model/validator.rb +7 -5
- data/lib/active_model/version.rb +1 -1
- data/lib/active_model.rb +5 -1
- metadata +13 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62a8e27551c3779ec7676e6d2e0e66c13c2a8f95c88562722bfdcb8d9d319f2f
|
4
|
+
data.tar.gz: 2cd6f1f9bcf63a13c2688fbc6854d1173f657bfecbfca5ef9a24bd06b5c61f32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e2132e134510005376a33192f81e408fdc3e5813f368c7c8a0ce2b00328d963c94970549219a7e9b2352136a8c223da5bb2e753e9cae9e43ce2b0f6304ad8df
|
7
|
+
data.tar.gz: bb81aaefa19e40042623a008d8e4d0e062f35590f4150eabb6349dd0b394b3b0d58276b2acdf17137a09c4f25a97caf011069590ff09cebb43581a3d7549919b
|
data/CHANGELOG.md
CHANGED
@@ -1,249 +1,264 @@
|
|
1
|
-
## Rails 7.
|
2
|
-
|
3
|
-
* Error.full_message now strips ":base" from the message.
|
4
|
-
|
5
|
-
*zzak*
|
6
|
-
|
7
|
-
* Add a load hook for `ActiveModel::Model` (named `active_model`) to match the load hook for
|
8
|
-
`ActiveRecord::Base` and allow for overriding aspects of the `ActiveModel::Model` class.
|
9
|
-
|
10
|
-
|
11
|
-
## Rails 7.0.6 (June 29, 2023) ##
|
12
|
-
|
13
|
-
* No changes.
|
14
|
-
|
15
|
-
|
16
|
-
## Rails 7.0.5.1 (June 26, 2023) ##
|
1
|
+
## Rails 7.1.3.4 (June 04, 2024) ##
|
17
2
|
|
18
3
|
* No changes.
|
19
4
|
|
20
5
|
|
21
|
-
## Rails 7.
|
6
|
+
## Rails 7.1.3.3 (May 16, 2024) ##
|
22
7
|
|
23
8
|
* No changes.
|
24
9
|
|
25
10
|
|
26
|
-
## Rails 7.
|
11
|
+
## Rails 7.1.3.2 (February 21, 2024) ##
|
27
12
|
|
28
13
|
* No changes.
|
29
14
|
|
30
15
|
|
31
|
-
## Rails 7.
|
16
|
+
## Rails 7.1.3.1 (February 21, 2024) ##
|
32
17
|
|
33
18
|
* No changes.
|
34
19
|
|
35
20
|
|
36
|
-
## Rails 7.
|
21
|
+
## Rails 7.1.3 (January 16, 2024) ##
|
37
22
|
|
38
23
|
* No changes.
|
39
24
|
|
40
25
|
|
41
|
-
## Rails 7.
|
42
|
-
|
43
|
-
* Handle name clashes in attribute methods code generation cache.
|
44
|
-
|
45
|
-
When two distinct attribute methods would generate similar names,
|
46
|
-
the first implementation would be incorrectly re-used.
|
47
|
-
|
48
|
-
```ruby
|
49
|
-
class A
|
50
|
-
attribute_method_suffix "_changed?"
|
51
|
-
define_attribute_methods :x
|
52
|
-
end
|
53
|
-
|
54
|
-
class B
|
55
|
-
attribute_method_suffix "?"
|
56
|
-
define_attribute_methods :x_changed
|
57
|
-
end
|
58
|
-
```
|
59
|
-
|
60
|
-
*Jean Boussier*
|
26
|
+
## Rails 7.1.2 (November 10, 2023) ##
|
61
27
|
|
62
|
-
|
28
|
+
* Make `==(other)` method of AttributeSet safe.
|
63
29
|
|
64
|
-
*
|
30
|
+
*Dmitry Pogrebnoy*
|
65
31
|
|
66
32
|
|
67
|
-
## Rails 7.
|
33
|
+
## Rails 7.1.1 (October 11, 2023) ##
|
68
34
|
|
69
35
|
* No changes.
|
70
36
|
|
71
37
|
|
72
|
-
## Rails 7.0
|
38
|
+
## Rails 7.1.0 (October 05, 2023) ##
|
73
39
|
|
74
40
|
* No changes.
|
75
41
|
|
76
42
|
|
77
|
-
## Rails 7.0.
|
43
|
+
## Rails 7.1.0.rc2 (October 01, 2023) ##
|
78
44
|
|
79
45
|
* No changes.
|
80
46
|
|
81
47
|
|
82
|
-
## Rails 7.0.
|
48
|
+
## Rails 7.1.0.rc1 (September 27, 2023) ##
|
83
49
|
|
84
|
-
*
|
50
|
+
* Remove change in the typography of user facing error messages.
|
51
|
+
For example, “can’t be blank” is again “can't be blank”.
|
85
52
|
|
53
|
+
*Rafael Mendonça França*
|
86
54
|
|
87
|
-
## Rails 7.0.2.1 (February 11, 2022) ##
|
88
|
-
|
89
|
-
* No changes.
|
90
55
|
|
56
|
+
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
91
57
|
|
92
|
-
|
58
|
+
* Support composite identifiers in `to_key`
|
93
59
|
|
94
|
-
|
60
|
+
`to_key` avoids wrapping `#id` value into an `Array` if `#id` already an array
|
95
61
|
|
96
|
-
|
97
|
-
names, leading to conflicts. Adding a new namespace `:active_model_proxy`
|
98
|
-
fixes the issue.
|
62
|
+
*Nikita Vasilevsky*
|
99
63
|
|
100
|
-
|
64
|
+
* Add `ActiveModel::Conversion.param_delimiter` to configure delimiter being used in `to_param`
|
101
65
|
|
66
|
+
*Nikita Vasilevsky*
|
102
67
|
|
103
|
-
|
68
|
+
* `undefine_attribute_methods` undefines alias attribute methods along with attribute methods.
|
104
69
|
|
105
|
-
*
|
70
|
+
*Nikita Vasilevsky*
|
106
71
|
|
72
|
+
* Error.full_message now strips ":base" from the message.
|
107
73
|
|
108
|
-
|
74
|
+
*zzak*
|
109
75
|
|
110
|
-
*
|
76
|
+
* Add a load hook for `ActiveModel::Model` (named `active_model`) to match the load hook for
|
77
|
+
`ActiveRecord::Base` and allow for overriding aspects of the `ActiveModel::Model` class.
|
111
78
|
|
79
|
+
*Lewis Buckley*
|
112
80
|
|
113
|
-
|
81
|
+
* Improve password length validation in ActiveModel::SecurePassword to consider byte size for BCrypt
|
82
|
+
compatibility.
|
114
83
|
|
115
|
-
|
84
|
+
The previous password length validation only considered the character count, which may not
|
85
|
+
accurately reflect the 72-byte size limit imposed by BCrypt. This change updates the validation
|
86
|
+
to consider both character count and byte size while keeping the character length validation in place.
|
116
87
|
|
88
|
+
```ruby
|
89
|
+
user = User.new(password: "a" * 73) # 73 characters
|
90
|
+
user.valid? # => false
|
91
|
+
user.errors[:password] # => ["is too long"]
|
117
92
|
|
118
|
-
## Rails 7.0.0.rc2 (December 14, 2021) ##
|
119
93
|
|
120
|
-
*
|
94
|
+
user = User.new(password: "あ" * 25) # 25 characters, 75 bytes
|
95
|
+
user.valid? # => false
|
96
|
+
user.errors[:password] # => ["is too long"]
|
97
|
+
```
|
121
98
|
|
122
|
-
|
99
|
+
*ChatGPT*, *Guillermo Iguaran*
|
123
100
|
|
124
|
-
*
|
101
|
+
* `has_secure_password` now generates an `#{attribute}_salt` method that returns the salt
|
102
|
+
used to compute the password digest. The salt will change whenever the password is changed,
|
103
|
+
so it can be used to create single-use password reset tokens with `generates_token_for`:
|
125
104
|
|
126
|
-
|
105
|
+
```ruby
|
106
|
+
class User < ActiveRecord::Base
|
107
|
+
has_secure_password
|
127
108
|
|
128
|
-
|
109
|
+
generates_token_for :password_reset, expires_in: 15.minutes do
|
110
|
+
password_salt&.last(10)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
129
114
|
|
130
|
-
*
|
115
|
+
*Lázaro Nixon*
|
131
116
|
|
132
|
-
*
|
117
|
+
* Improve typography of user facing error messages. In English contractions,
|
118
|
+
the Unicode APOSTROPHE (`U+0027`) is now RIGHT SINGLE QUOTATION MARK
|
119
|
+
(`U+2019`). For example, "can't be blank" is now "can’t be blank".
|
133
120
|
|
134
|
-
*
|
121
|
+
*Jon Dufresne*
|
135
122
|
|
136
|
-
*
|
123
|
+
* Add class to `ActiveModel::MissingAttributeError` error message.
|
137
124
|
|
138
|
-
|
125
|
+
Show which class is missing the attribute in the error message:
|
139
126
|
|
140
|
-
|
127
|
+
```ruby
|
128
|
+
user = User.first
|
129
|
+
user.pets.select(:id).first.user_id
|
130
|
+
# => ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
|
131
|
+
```
|
141
132
|
|
142
|
-
*
|
133
|
+
*Petrik de Heus*
|
143
134
|
|
144
|
-
*
|
135
|
+
* Raise `NoMethodError` in `ActiveModel::Type::Value#as_json` to avoid unpredictable
|
136
|
+
results.
|
145
137
|
|
146
|
-
*
|
138
|
+
*Vasiliy Ermolovich*
|
147
139
|
|
148
|
-
*
|
140
|
+
* Custom attribute types that inherit from Active Model built-in types and do
|
141
|
+
not override the `serialize` method will now benefit from an optimization
|
142
|
+
when serializing attribute values for the database.
|
149
143
|
|
150
|
-
|
144
|
+
For example, with a custom type like the following:
|
151
145
|
|
152
|
-
|
146
|
+
```ruby
|
147
|
+
class DowncasedString < ActiveModel::Type::String
|
148
|
+
def cast(value)
|
149
|
+
super&.downcase
|
150
|
+
end
|
151
|
+
end
|
153
152
|
|
154
|
-
|
153
|
+
ActiveRecord::Type.register(:downcased_string, DowncasedString)
|
155
154
|
|
156
|
-
|
155
|
+
class User < ActiveRecord::Base
|
156
|
+
attribute :email, :downcased_string
|
157
|
+
end
|
157
158
|
|
158
|
-
|
159
|
+
user = User.new(email: "FooBar@example.com")
|
160
|
+
```
|
159
161
|
|
160
|
-
|
162
|
+
Serializing the `email` attribute for the database will be roughly twice as
|
163
|
+
fast. More expensive `cast` operations will likely see greater improvements.
|
161
164
|
|
162
|
-
*
|
165
|
+
*Jonathan Hefner*
|
163
166
|
|
164
|
-
*
|
167
|
+
* `has_secure_password` now supports password challenges via a
|
168
|
+
`password_challenge` accessor and validation.
|
165
169
|
|
166
|
-
|
170
|
+
A password challenge is a safeguard to verify that the current user is
|
171
|
+
actually the password owner. It can be used when changing sensitive model
|
172
|
+
fields, such as the password itself. It is different than a password
|
173
|
+
confirmation, which is used to prevent password typos.
|
167
174
|
|
168
|
-
|
175
|
+
When `password_challenge` is set, the validation checks that the value's
|
176
|
+
digest matches the *currently persisted* `password_digest` (i.e.
|
177
|
+
`password_digest_was`).
|
169
178
|
|
170
|
-
|
179
|
+
This allows a password challenge to be done as part of a typical `update`
|
180
|
+
call, just like a password confirmation. It also allows a password
|
181
|
+
challenge error to be handled in the same way as other validation errors.
|
171
182
|
|
172
|
-
|
183
|
+
For example, in the controller, instead of:
|
173
184
|
|
174
|
-
|
185
|
+
```ruby
|
186
|
+
password_params = params.require(:password).permit(
|
187
|
+
:password_challenge,
|
188
|
+
:password,
|
189
|
+
:password_confirmation,
|
190
|
+
)
|
175
191
|
|
176
|
-
|
177
|
-
|
192
|
+
password_challenge = password_params.delete(:password_challenge)
|
193
|
+
@password_challenge_failed = !current_user.authenticate(password_challenge)
|
178
194
|
|
179
|
-
|
195
|
+
if !@password_challenge_failed && current_user.update(password_params)
|
196
|
+
# ...
|
197
|
+
end
|
198
|
+
```
|
180
199
|
|
181
|
-
|
200
|
+
You can now write:
|
182
201
|
|
183
|
-
|
184
|
-
|
202
|
+
```ruby
|
203
|
+
password_params = params.require(:password).permit(
|
204
|
+
:password_challenge,
|
205
|
+
:password,
|
206
|
+
:password_confirmation,
|
207
|
+
).with_defaults(password_challenge: "")
|
208
|
+
|
209
|
+
if current_user.update(password_params)
|
210
|
+
# ...
|
211
|
+
end
|
212
|
+
```
|
185
213
|
|
186
|
-
|
214
|
+
And, in the view, instead of checking `@password_challenge_failed`, you can
|
215
|
+
render an error for the `password_challenge` field just as you would for
|
216
|
+
other form fields, including utilizing `config.action_view.field_error_proc`.
|
187
217
|
|
188
|
-
*
|
218
|
+
*Jonathan Hefner*
|
189
219
|
|
190
|
-
|
220
|
+
* Support infinite ranges for `LengthValidator`s `:in`/`:within` options
|
191
221
|
|
192
|
-
|
222
|
+
```ruby
|
223
|
+
validates_length_of :first_name, in: ..30
|
224
|
+
```
|
193
225
|
|
226
|
+
*fatkodima*
|
194
227
|
|
195
|
-
|
228
|
+
* Add support for beginless ranges to inclusivity/exclusivity validators:
|
196
229
|
|
197
|
-
|
230
|
+
```ruby
|
231
|
+
validates_inclusion_of :birth_date, in: -> { (..Date.today) }
|
232
|
+
```
|
198
233
|
|
199
|
-
|
200
|
-
|
234
|
+
```ruby
|
235
|
+
validates_exclusion_of :birth_date, in: -> { (..Date.today) }
|
236
|
+
```
|
201
237
|
|
202
|
-
*
|
238
|
+
*Bo Jeanes*
|
203
239
|
|
204
|
-
*
|
240
|
+
* Make validators accept lambdas without record argument
|
205
241
|
|
206
|
-
|
207
|
-
|
242
|
+
```ruby
|
243
|
+
# Before
|
244
|
+
validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today }
|
208
245
|
|
209
|
-
|
246
|
+
# After
|
247
|
+
validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
|
248
|
+
```
|
210
249
|
|
211
|
-
*
|
250
|
+
*fatkodima*
|
212
251
|
|
213
|
-
|
252
|
+
* Fix casting long strings to `Date`, `Time` or `DateTime`
|
214
253
|
|
215
|
-
*
|
254
|
+
*fatkodima*
|
216
255
|
|
217
|
-
*
|
256
|
+
* Use different cache namespace for proxy calls
|
218
257
|
|
219
|
-
|
258
|
+
Models can currently have different attribute bodies for the same method
|
259
|
+
names, leading to conflicts. Adding a new namespace `:active_model_proxy`
|
260
|
+
fixes the issue.
|
220
261
|
|
221
262
|
*Chris Salzberg*
|
222
263
|
|
223
|
-
|
224
|
-
|
225
|
-
Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
|
226
|
-
|
227
|
-
*Benoit Daloze*
|
228
|
-
|
229
|
-
* Cache and re-use generated attribute methods.
|
230
|
-
|
231
|
-
Generated methods with identical implementations will now share their instruction sequences
|
232
|
-
leading to reduced memory retention, and slightly faster load time.
|
233
|
-
|
234
|
-
*Jean Boussier*
|
235
|
-
|
236
|
-
* Add `in: range` parameter to `numericality` validator.
|
237
|
-
|
238
|
-
*Michal Papis*
|
239
|
-
|
240
|
-
* Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
|
241
|
-
`plural`, `route_key` and `singular_route_key` values.
|
242
|
-
|
243
|
-
*Lukas Pokorny*
|
244
|
-
|
245
|
-
* Make ActiveModel::Errors#inspect slimmer for readability
|
246
|
-
|
247
|
-
*lulalala*
|
248
|
-
|
249
|
-
Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activemodel/CHANGELOG.md) for previous changes.
|
264
|
+
Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
= Active Model -- model interfaces for Rails
|
1
|
+
= Active Model -- model interfaces for \Rails
|
2
2
|
|
3
3
|
Active Model provides a known set of interfaces for usage in model classes.
|
4
4
|
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
|
-
the Rails framework.
|
6
|
+
the \Rails framework.
|
7
7
|
|
8
|
-
You can read more about Active Model in the {Active Model Basics}[https://
|
8
|
+
You can read more about Active Model in the {Active Model Basics}[https://guides.rubyonrails.org/active_model_basics.html] guide.
|
9
9
|
|
10
|
-
Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
|
10
|
+
Prior to \Rails 3.0, if a plugin or gem developer wanted to have an object
|
11
11
|
interact with Action Pack helpers, it was required to either copy chunks of
|
12
|
-
code from Rails, or monkey patch entire helpers to make them handle objects
|
12
|
+
code from \Rails, or monkey patch entire helpers to make them handle objects
|
13
13
|
that did not exactly conform to the Active Record interface. This would result
|
14
14
|
in code duplication and fragile applications that broke on upgrades. Active
|
15
15
|
Model solves this by defining an explicit API. You can read more about the
|
16
|
-
API in
|
16
|
+
API in +ActiveModel::Lint::Tests+.
|
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:
|
19
|
+
to integrate with Action Pack out of the box: ActiveModel::API.
|
20
20
|
|
21
21
|
class Person
|
22
22
|
include ActiveModel::API
|
@@ -32,7 +32,7 @@ to integrate with Action Pack out of the box: <tt>ActiveModel::API</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
|
35
|
+
See ActiveModel::API 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:
|
@@ -156,7 +156,7 @@ behavior out of the box:
|
|
156
156
|
|
157
157
|
* Making objects serializable
|
158
158
|
|
159
|
-
|
159
|
+
ActiveModel::Serialization provides a standard interface for your object
|
160
160
|
to provide +to_json+ serialization.
|
161
161
|
|
162
162
|
class SerialPerson
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
require "active_support/core_ext/hash/indifferent_access"
|
5
|
+
|
6
|
+
module ActiveModel
|
7
|
+
module Access # :nodoc:
|
8
|
+
def slice(*methods)
|
9
|
+
methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
|
10
|
+
end
|
11
|
+
|
12
|
+
def values_at(*methods)
|
13
|
+
methods.flatten.map! { |method| public_send(method) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/active_model/api.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveModel
|
4
|
-
#
|
4
|
+
# = Active \Model \API
|
5
5
|
#
|
6
6
|
# Includes the required interface for an object to interact with
|
7
|
-
# Action Pack and Action View, using different Active Model modules.
|
7
|
+
# Action Pack and Action View, using different Active \Model modules.
|
8
8
|
# It includes model name introspections, conversions, translations, and
|
9
9
|
# validations. Besides that, it allows you to initialize the object with a
|
10
10
|
# hash of attributes, pretty much like Active Record does.
|
@@ -20,7 +20,7 @@ module ActiveModel
|
|
20
20
|
# person.name # => "bob"
|
21
21
|
# person.age # => "18"
|
22
22
|
#
|
23
|
-
# Note that, by default,
|
23
|
+
# Note that, by default, +ActiveModel::API+ implements #persisted?
|
24
24
|
# to return +false+, which is the most common case. You may want to override
|
25
25
|
# it in your class to simulate a different scenario:
|
26
26
|
#
|
@@ -36,7 +36,7 @@ module ActiveModel
|
|
36
36
|
# person = Person.new(id: 1, name: 'bob')
|
37
37
|
# person.persisted? # => true
|
38
38
|
#
|
39
|
-
# Also, if for some reason you need to run code on
|
39
|
+
# Also, if for some reason you need to run code on initialize ( ::new ), make
|
40
40
|
# sure you call +super+ if you want the attributes hash initialization to
|
41
41
|
# happen.
|
42
42
|
#
|
@@ -54,7 +54,7 @@ module ActiveModel
|
|
54
54
|
# person.omg # => true
|
55
55
|
#
|
56
56
|
# For more detailed information on other functionalities available, please
|
57
|
-
# refer to the specific modules included in
|
57
|
+
# refer to the specific modules included in +ActiveModel::API+
|
58
58
|
# (see below).
|
59
59
|
module API
|
60
60
|
extend ActiveSupport::Concern
|
@@ -4,6 +4,10 @@ require "active_model/attribute"
|
|
4
4
|
|
5
5
|
module ActiveModel
|
6
6
|
class Attribute # :nodoc:
|
7
|
+
def with_user_default(value)
|
8
|
+
UserProvidedDefault.new(name, value, type, self.is_a?(FromDatabase) ? self : original_attribute)
|
9
|
+
end
|
10
|
+
|
7
11
|
class UserProvidedDefault < FromUser # :nodoc:
|
8
12
|
def initialize(name, value, type, database_default)
|
9
13
|
@user_provided_value = value
|
@@ -53,7 +53,10 @@ module ActiveModel
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def value_for_database
|
56
|
-
type.
|
56
|
+
if !defined?(@value_for_database) || type.changed_in_place?(@value_for_database, value)
|
57
|
+
@value_for_database = _value_for_database
|
58
|
+
end
|
59
|
+
@value_for_database
|
57
60
|
end
|
58
61
|
|
59
62
|
def serializable?(&block)
|
@@ -159,6 +162,10 @@ module ActiveModel
|
|
159
162
|
assigned? && type.changed?(original_value, value, value_before_type_cast)
|
160
163
|
end
|
161
164
|
|
165
|
+
def _value_for_database
|
166
|
+
type.serialize(value)
|
167
|
+
end
|
168
|
+
|
162
169
|
def _original_value_for_database
|
163
170
|
type.serialize(original_value)
|
164
171
|
end
|
@@ -168,6 +175,19 @@ module ActiveModel
|
|
168
175
|
type.deserialize(value)
|
169
176
|
end
|
170
177
|
|
178
|
+
def forgetting_assignment
|
179
|
+
# If this attribute was not persisted (with a `value_for_database`
|
180
|
+
# that might differ from `value_before_type_cast`) and `value` has not
|
181
|
+
# changed in place, we can use the existing `value_before_type_cast`
|
182
|
+
# to avoid deserialize / cast / serialize calls from computing the new
|
183
|
+
# attribute's `value_before_type_cast`.
|
184
|
+
if !defined?(@value_for_database) && !changed_in_place?
|
185
|
+
with_value_from_database(value_before_type_cast)
|
186
|
+
else
|
187
|
+
super
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
171
191
|
private
|
172
192
|
def _original_value_for_database
|
173
193
|
value_before_type_cast
|
@@ -182,6 +202,11 @@ module ActiveModel
|
|
182
202
|
def came_from_user?
|
183
203
|
!type.value_constructed_by_mass_assignment?(value_before_type_cast)
|
184
204
|
end
|
205
|
+
|
206
|
+
private
|
207
|
+
def _value_for_database
|
208
|
+
Type::SerializeCastValue.serialize(type, value)
|
209
|
+
end
|
185
210
|
end
|
186
211
|
|
187
212
|
class WithCastValue < Attribute # :nodoc:
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
10
10
|
# keys matching the attribute names.
|
11
11
|
#
|
12
12
|
# If the passed hash responds to <tt>permitted?</tt> method and the return value
|
13
|
-
# of this method is +false+ an
|
13
|
+
# of this method is +false+ an ActiveModel::ForbiddenAttributesError
|
14
14
|
# exception is raised.
|
15
15
|
#
|
16
16
|
# class Cat
|