activemodel 7.0.4 → 7.1.5.1

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +191 -94
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +11 -11
  5. data/lib/active_model/access.rb +16 -0
  6. data/lib/active_model/api.rb +5 -5
  7. data/lib/active_model/attribute/user_provided_default.rb +4 -0
  8. data/lib/active_model/attribute.rb +26 -1
  9. data/lib/active_model/attribute_assignment.rb +1 -1
  10. data/lib/active_model/attribute_methods.rb +165 -112
  11. data/lib/active_model/attribute_mutation_tracker.rb +10 -2
  12. data/lib/active_model/attribute_registration.rb +77 -0
  13. data/lib/active_model/attribute_set.rb +10 -1
  14. data/lib/active_model/attributes.rb +65 -48
  15. data/lib/active_model/callbacks.rb +6 -6
  16. data/lib/active_model/conversion.rb +14 -4
  17. data/lib/active_model/deprecator.rb +7 -0
  18. data/lib/active_model/dirty.rb +134 -13
  19. data/lib/active_model/error.rb +5 -4
  20. data/lib/active_model/errors.rb +37 -6
  21. data/lib/active_model/forbidden_attributes_protection.rb +2 -0
  22. data/lib/active_model/gem_version.rb +4 -4
  23. data/lib/active_model/lint.rb +1 -1
  24. data/lib/active_model/locale/en.yml +1 -0
  25. data/lib/active_model/model.rb +34 -2
  26. data/lib/active_model/naming.rb +29 -10
  27. data/lib/active_model/railtie.rb +4 -0
  28. data/lib/active_model/secure_password.rb +61 -23
  29. data/lib/active_model/serialization.rb +3 -3
  30. data/lib/active_model/serializers/json.rb +1 -1
  31. data/lib/active_model/translation.rb +18 -16
  32. data/lib/active_model/type/big_integer.rb +23 -1
  33. data/lib/active_model/type/binary.rb +13 -3
  34. data/lib/active_model/type/boolean.rb +11 -9
  35. data/lib/active_model/type/date.rb +28 -2
  36. data/lib/active_model/type/date_time.rb +45 -3
  37. data/lib/active_model/type/decimal.rb +39 -1
  38. data/lib/active_model/type/float.rb +30 -1
  39. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +5 -1
  40. data/lib/active_model/type/helpers/mutable.rb +4 -0
  41. data/lib/active_model/type/helpers/numeric.rb +6 -1
  42. data/lib/active_model/type/helpers/time_value.rb +28 -12
  43. data/lib/active_model/type/immutable_string.rb +37 -1
  44. data/lib/active_model/type/integer.rb +44 -1
  45. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  46. data/lib/active_model/type/string.rb +9 -1
  47. data/lib/active_model/type/time.rb +48 -7
  48. data/lib/active_model/type/value.rb +25 -1
  49. data/lib/active_model/type.rb +1 -0
  50. data/lib/active_model/validations/absence.rb +1 -1
  51. data/lib/active_model/validations/acceptance.rb +1 -1
  52. data/lib/active_model/validations/callbacks.rb +10 -12
  53. data/lib/active_model/validations/clusivity.rb +5 -8
  54. data/lib/active_model/validations/comparability.rb +0 -11
  55. data/lib/active_model/validations/comparison.rb +15 -7
  56. data/lib/active_model/validations/format.rb +6 -7
  57. data/lib/active_model/validations/length.rb +13 -12
  58. data/lib/active_model/validations/numericality.rb +35 -23
  59. data/lib/active_model/validations/presence.rb +1 -1
  60. data/lib/active_model/validations/resolve_value.rb +26 -0
  61. data/lib/active_model/validations/validates.rb +5 -5
  62. data/lib/active_model/validations/with.rb +9 -2
  63. data/lib/active_model/validations.rb +44 -9
  64. data/lib/active_model/validator.rb +7 -5
  65. data/lib/active_model/version.rb +1 -1
  66. data/lib/active_model.rb +6 -1
  67. metadata +16 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 630260cc44166916794fb7e43002e72a303ff43b834e82e530cff1f13f25ee7f
4
- data.tar.gz: f136cec57987c03ab4aa3fba2b65fe2964b010a326e28cdc0d32f755b45612f1
3
+ metadata.gz: 9f79873d7c2a9772e4c9ca33cf468880b923a818c11b874eb7c8c40954a1e652
4
+ data.tar.gz: a264d11a6a4a6783d08513145436a78440847774f64ab8bfe5436e0e0c56224b
5
5
  SHA512:
6
- metadata.gz: 9b16a3dd6f96bf2ec1bfa5e3abba2e513567b12035e34e8ea30c24fb8a08ca70089d394f5daa0f40fdc4e14fb63470593904a5a335f864782983c719cf0d57d2
7
- data.tar.gz: 66f3f86fcf4b90ffbab8ea82777ac2df48f1774f7ff528d44ec81e12ea47cb2050309b5535e9e960757eb8f9350efa9c6a128754c3b117fdedef3e3c42a43765
6
+ metadata.gz: 7019a3654dabd54a630605e8b032450f170fe5aa8f3fa0ec626f80546dbf9c09cea143bed32356608319b059127ede72aae6e08c5a280dfae9021e98f027640f
7
+ data.tar.gz: 72936b14496e7b9465b6cdaf7894b80b502bb275e58f7e4b9e52d67ccfd671ee1a4f952ea3da25b504147d81a645755a4cb16dde63f4ecbc68b6787636201b0b
data/CHANGELOG.md CHANGED
@@ -1,209 +1,306 @@
1
- ## Rails 7.0.4 (September 09, 2022) ##
1
+ ## Rails 7.1.5.1 (December 10, 2024) ##
2
2
 
3
- * Handle name clashes in attribute methods code generation cache.
3
+ * No changes.
4
+
5
+
6
+ ## Rails 7.1.5 (October 30, 2024) ##
4
7
 
5
- When two distinct attribute methods would generate similar names,
6
- the first implementation would be incorrectly re-used.
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.
7
11
 
8
12
  ```ruby
9
- class A
10
- attribute_method_suffix "_changed?"
11
- define_attribute_methods :x
12
- end
13
+ class Person
14
+ include ActiveModel::AttributeMethods
13
15
 
14
- class B
15
- attribute_method_suffix "?"
16
- define_attribute_methods :x_changed
16
+ define_attribute_methods :name
17
+ attr_accessor :name
18
+
19
+ alias_attribute :full_name, :name
17
20
  end
21
+
22
+ person.full_name # => NoMethodError: undefined method `attribute' for an instance of Person
18
23
  ```
19
24
 
20
25
  *Jean Boussier*
21
26
 
22
- ## Rails 7.0.3.1 (July 12, 2022) ##
27
+
28
+ ## Rails 7.1.4.2 (October 23, 2024) ##
23
29
 
24
30
  * No changes.
25
31
 
26
32
 
27
- ## Rails 7.0.3 (May 09, 2022) ##
33
+ ## Rails 7.1.4.1 (October 15, 2024) ##
28
34
 
29
35
  * No changes.
30
36
 
31
37
 
32
- ## Rails 7.0.2.4 (April 26, 2022) ##
38
+ ## Rails 7.1.4 (August 22, 2024) ##
33
39
 
34
40
  * No changes.
35
41
 
36
42
 
37
- ## Rails 7.0.2.3 (March 08, 2022) ##
43
+ ## Rails 7.1.3.4 (June 04, 2024) ##
38
44
 
39
45
  * No changes.
40
46
 
41
47
 
42
- ## Rails 7.0.2.2 (February 11, 2022) ##
48
+ ## Rails 7.1.3.3 (May 16, 2024) ##
43
49
 
44
50
  * No changes.
45
51
 
46
52
 
47
- ## Rails 7.0.2.1 (February 11, 2022) ##
53
+ ## Rails 7.1.3.2 (February 21, 2024) ##
48
54
 
49
55
  * No changes.
50
56
 
51
57
 
52
- ## Rails 7.0.2 (February 08, 2022) ##
58
+ ## Rails 7.1.3.1 (February 21, 2024) ##
53
59
 
54
- * Use different cache namespace for proxy calls
60
+ * No changes.
55
61
 
56
- Models can currently have different attribute bodies for the same method
57
- names, leading to conflicts. Adding a new namespace `:active_model_proxy`
58
- fixes the issue.
59
62
 
60
- *Chris Salzberg*
63
+ ## Rails 7.1.3 (January 16, 2024) ##
61
64
 
65
+ * No changes.
62
66
 
63
- ## Rails 7.0.1 (January 06, 2022) ##
64
67
 
65
- * No changes.
68
+ ## Rails 7.1.2 (November 10, 2023) ##
69
+
70
+ * Make `==(other)` method of AttributeSet safe.
71
+
72
+ *Dmitry Pogrebnoy*
66
73
 
67
74
 
68
- ## Rails 7.0.0 (December 15, 2021) ##
75
+ ## Rails 7.1.1 (October 11, 2023) ##
69
76
 
70
77
  * No changes.
71
78
 
72
79
 
73
- ## Rails 7.0.0.rc3 (December 14, 2021) ##
80
+ ## Rails 7.1.0 (October 05, 2023) ##
74
81
 
75
82
  * No changes.
76
83
 
77
84
 
78
- ## Rails 7.0.0.rc2 (December 14, 2021) ##
85
+ ## Rails 7.1.0.rc2 (October 01, 2023) ##
79
86
 
80
87
  * No changes.
81
88
 
82
- ## Rails 7.0.0.rc1 (December 06, 2021) ##
83
89
 
84
- * Remove support to Marshal load Rails 5.x `ActiveModel::AttributeSet` format.
90
+ ## Rails 7.1.0.rc1 (September 27, 2023) ##
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”.
85
94
 
86
95
  *Rafael Mendonça França*
87
96
 
88
- * Remove support to Marshal and YAML load Rails 5.x error format.
89
97
 
90
- *Rafael Mendonça França*
98
+ ## Rails 7.1.0.beta1 (September 13, 2023) ##
91
99
 
92
- * Remove deprecated support to use `[]=` in `ActiveModel::Errors#messages`.
100
+ * Support composite identifiers in `to_key`
93
101
 
94
- *Rafael Mendonça França*
102
+ `to_key` avoids wrapping `#id` value into an `Array` if `#id` already an array
95
103
 
96
- * Remove deprecated support to `delete` errors from `ActiveModel::Errors#messages`.
104
+ *Nikita Vasilevsky*
97
105
 
98
- *Rafael Mendonça França*
106
+ * Add `ActiveModel::Conversion.param_delimiter` to configure delimiter being used in `to_param`
99
107
 
100
- * Remove deprecated support to `clear` errors from `ActiveModel::Errors#messages`.
108
+ *Nikita Vasilevsky*
101
109
 
102
- *Rafael Mendonça França*
110
+ * `undefine_attribute_methods` undefines alias attribute methods along with attribute methods.
103
111
 
104
- * Remove deprecated support concat errors to `ActiveModel::Errors#messages`.
112
+ *Nikita Vasilevsky*
105
113
 
106
- *Rafael Mendonça França*
114
+ * Error.full_message now strips ":base" from the message.
107
115
 
108
- * Remove deprecated `ActiveModel::Errors#to_xml`.
116
+ *zzak*
109
117
 
110
- *Rafael Mendonça França*
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.
111
120
 
112
- * Remove deprecated `ActiveModel::Errors#keys`.
121
+ *Lewis Buckley*
113
122
 
114
- *Rafael Mendonça França*
123
+ * Improve password length validation in ActiveModel::SecurePassword to consider byte size for BCrypt
124
+ compatibility.
115
125
 
116
- * Remove deprecated `ActiveModel::Errors#values`.
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.
117
129
 
118
- *Rafael Mendonça França*
130
+ ```ruby
131
+ user = User.new(password: "a" * 73) # 73 characters
132
+ user.valid? # => false
133
+ user.errors[:password] # => ["is too long"]
119
134
 
120
- * Remove deprecated `ActiveModel::Errors#slice!`.
121
135
 
122
- *Rafael Mendonça França*
136
+ user = User.new(password: "あ" * 25) # 25 characters, 75 bytes
137
+ user.valid? # => false
138
+ user.errors[:password] # => ["is too long"]
139
+ ```
123
140
 
124
- * Remove deprecated `ActiveModel::Errors#to_h`.
141
+ *ChatGPT*, *Guillermo Iguaran*
125
142
 
126
- *Rafael Mendonça França*
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`:
127
146
 
128
- * Remove deprecated enumeration of `ActiveModel::Errors` instances as a Hash.
147
+ ```ruby
148
+ class User < ActiveRecord::Base
149
+ has_secure_password
129
150
 
130
- *Rafael Mendonça França*
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*
131
158
 
132
- * Clear secure password cache if password is set to `nil`
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".
133
162
 
134
- Before:
163
+ *Jon Dufresne*
135
164
 
136
- user.password = 'something'
137
- user.password = nil
165
+ * Add class to `ActiveModel::MissingAttributeError` error message.
138
166
 
139
- user.password # => 'something'
167
+ Show which class is missing the attribute in the error message:
140
168
 
141
- Now:
169
+ ```ruby
170
+ user = User.first
171
+ user.pets.select(:id).first.user_id
172
+ # => ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
173
+ ```
142
174
 
143
- user.password = 'something'
144
- user.password = nil
175
+ *Petrik de Heus*
145
176
 
146
- user.password # => nil
177
+ * Raise `NoMethodError` in `ActiveModel::Type::Value#as_json` to avoid unpredictable
178
+ results.
147
179
 
148
- *Markus Doits*
180
+ *Vasiliy Ermolovich*
149
181
 
150
- ## Rails 7.0.0.alpha2 (September 15, 2021) ##
182
+ * Custom attribute types that inherit from Active Model built-in types and do
183
+ not override the `serialize` method will now benefit from an optimization
184
+ when serializing attribute values for the database.
151
185
 
152
- * No changes.
186
+ For example, with a custom type like the following:
153
187
 
188
+ ```ruby
189
+ class DowncasedString < ActiveModel::Type::String
190
+ def cast(value)
191
+ super&.downcase
192
+ end
193
+ end
154
194
 
155
- ## Rails 7.0.0.alpha1 (September 15, 2021) ##
195
+ ActiveRecord::Type.register(:downcased_string, DowncasedString)
156
196
 
157
- * Introduce `ActiveModel::API`.
197
+ class User < ActiveRecord::Base
198
+ attribute :email, :downcased_string
199
+ end
158
200
 
159
- Make `ActiveModel::API` the minimum API to talk with Action Pack and Action View.
160
- This will allow adding more functionality to `ActiveModel::Model`.
201
+ user = User.new(email: "FooBar@example.com")
202
+ ```
161
203
 
162
- *Petrik de Heus*, *Nathaniel Watts*
204
+ Serializing the `email` attribute for the database will be roughly twice as
205
+ fast. More expensive `cast` operations will likely see greater improvements.
163
206
 
164
- * Fix dirty check for Float::NaN and BigDecimal::NaN.
207
+ *Jonathan Hefner*
165
208
 
166
- Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720)
167
- and can't be compared with `==`.
209
+ * `has_secure_password` now supports password challenges via a
210
+ `password_challenge` accessor and validation.
168
211
 
169
- *Marcelo Lauxen*
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.
170
216
 
171
- * Fix `to_json` for `ActiveModel::Dirty` object.
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`).
172
220
 
173
- Exclude `mutations_from_database` attribute from json as it lead to recursion.
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.
174
224
 
175
- *Anil Maurya*
225
+ For example, in the controller, instead of:
176
226
 
177
- * Add `ActiveModel::AttributeSet#values_for_database`.
227
+ ```ruby
228
+ password_params = params.require(:password).permit(
229
+ :password_challenge,
230
+ :password,
231
+ :password_confirmation,
232
+ )
178
233
 
179
- Returns attributes with values for assignment to the database.
234
+ password_challenge = password_params.delete(:password_challenge)
235
+ @password_challenge_failed = !current_user.authenticate(password_challenge)
180
236
 
181
- *Chris Salzberg*
237
+ if !@password_challenge_failed && current_user.update(password_params)
238
+ # ...
239
+ end
240
+ ```
182
241
 
183
- * Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup.
242
+ You can now write:
184
243
 
185
- Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
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
+ ```
186
255
 
187
- *Benoit Daloze*
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`.
188
259
 
189
- * Cache and re-use generated attribute methods.
260
+ *Jonathan Hefner*
190
261
 
191
- Generated methods with identical implementations will now share their instruction sequences
192
- leading to reduced memory retention, and slightly faster load time.
262
+ * Support infinite ranges for `LengthValidator`s `:in`/`:within` options
193
263
 
194
- *Jean Boussier*
264
+ ```ruby
265
+ validates_length_of :first_name, in: ..30
266
+ ```
267
+
268
+ *fatkodima*
195
269
 
196
- * Add `in: range` parameter to `numericality` validator.
270
+ * Add support for beginless ranges to inclusivity/exclusivity validators:
197
271
 
198
- *Michal Papis*
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 }
199
287
 
200
- * Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
201
- `plural`, `route_key` and `singular_route_key` values.
288
+ # After
289
+ validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
290
+ ```
291
+
292
+ *fatkodima*
202
293
 
203
- *Lukas Pokorny*
294
+ * Fix casting long strings to `Date`, `Time` or `DateTime`
204
295
 
205
- * Make ActiveModel::Errors#inspect slimmer for readability
296
+ *fatkodima*
206
297
 
207
- *lulalala*
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*
208
305
 
209
- Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activemodel/CHANGELOG.md) for previous changes.
306
+ Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activemodel/CHANGELOG.md) for previous changes.
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2022 David Heinemeier Hansson
1
+ Copyright (c) David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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://edgeguides.rubyonrails.org/active_model_basics.html] guide.
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 <tt>ActiveModel::Lint::Tests</tt>.
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: <tt>ActiveModel::API</tt>.
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 <tt>ActiveModel::API</tt> for more examples.
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
- <tt>ActiveModel::Serialization</tt> provides a standard interface for your object
159
+ ActiveModel::Serialization provides a standard interface for your object
160
160
  to provide +to_json+ serialization.
161
161
 
162
162
  class SerialPerson
@@ -239,7 +239,7 @@ The latest version of Active Model can be installed with RubyGems:
239
239
 
240
240
  $ gem install activemodel
241
241
 
242
- Source code can be downloaded as part of the Rails project on GitHub
242
+ Source code can be downloaded as part of the \Rails project on GitHub
243
243
 
244
244
  * https://github.com/rails/rails/tree/main/activemodel
245
245
 
@@ -257,7 +257,7 @@ API documentation is at:
257
257
 
258
258
  * https://api.rubyonrails.org
259
259
 
260
- Bug reports for the Ruby on Rails project can be filed here:
260
+ Bug reports for the Ruby on \Rails project can be filed here:
261
261
 
262
262
  * https://github.com/rails/rails/issues
263
263
 
@@ -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
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
- # == Active \Model \API
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, <tt>ActiveModel::API</tt> implements <tt>persisted?</tt>
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 <tt>initialize</tt>, make
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 <tt>ActiveModel::API</tt>
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.serialize(value)
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 <tt>ActiveModel::ForbiddenAttributesError</tt>
13
+ # of this method is +false+ an ActiveModel::ForbiddenAttributesError
14
14
  # exception is raised.
15
15
  #
16
16
  # class Cat