activemodel 7.0.10 → 7.1.0.beta1

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -236
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  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 +102 -63
  11. data/lib/active_model/attribute_registration.rb +77 -0
  12. data/lib/active_model/attribute_set.rb +9 -0
  13. data/lib/active_model/attributes.rb +62 -45
  14. data/lib/active_model/callbacks.rb +5 -5
  15. data/lib/active_model/conversion.rb +14 -4
  16. data/lib/active_model/deprecator.rb +7 -0
  17. data/lib/active_model/dirty.rb +134 -13
  18. data/lib/active_model/error.rb +4 -3
  19. data/lib/active_model/errors.rb +17 -38
  20. data/lib/active_model/forbidden_attributes_protection.rb +2 -0
  21. data/lib/active_model/gem_version.rb +4 -4
  22. data/lib/active_model/lint.rb +1 -1
  23. data/lib/active_model/locale/en.yml +4 -3
  24. data/lib/active_model/model.rb +26 -2
  25. data/lib/active_model/naming.rb +29 -10
  26. data/lib/active_model/railtie.rb +4 -0
  27. data/lib/active_model/secure_password.rb +61 -23
  28. data/lib/active_model/serialization.rb +3 -3
  29. data/lib/active_model/serializers/json.rb +1 -1
  30. data/lib/active_model/translation.rb +18 -16
  31. data/lib/active_model/type/big_integer.rb +23 -1
  32. data/lib/active_model/type/binary.rb +7 -1
  33. data/lib/active_model/type/boolean.rb +11 -9
  34. data/lib/active_model/type/date.rb +28 -2
  35. data/lib/active_model/type/date_time.rb +45 -3
  36. data/lib/active_model/type/decimal.rb +39 -1
  37. data/lib/active_model/type/float.rb +30 -1
  38. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +5 -1
  39. data/lib/active_model/type/helpers/numeric.rb +5 -2
  40. data/lib/active_model/type/helpers/time_value.rb +28 -12
  41. data/lib/active_model/type/immutable_string.rb +37 -1
  42. data/lib/active_model/type/integer.rb +44 -1
  43. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  44. data/lib/active_model/type/string.rb +9 -1
  45. data/lib/active_model/type/time.rb +48 -7
  46. data/lib/active_model/type/value.rb +17 -1
  47. data/lib/active_model/type.rb +1 -0
  48. data/lib/active_model/validations/absence.rb +1 -1
  49. data/lib/active_model/validations/acceptance.rb +1 -1
  50. data/lib/active_model/validations/callbacks.rb +4 -4
  51. data/lib/active_model/validations/clusivity.rb +5 -8
  52. data/lib/active_model/validations/comparability.rb +0 -11
  53. data/lib/active_model/validations/comparison.rb +15 -7
  54. data/lib/active_model/validations/confirmation.rb +1 -1
  55. data/lib/active_model/validations/format.rb +6 -7
  56. data/lib/active_model/validations/length.rb +10 -8
  57. data/lib/active_model/validations/numericality.rb +35 -23
  58. data/lib/active_model/validations/presence.rb +2 -2
  59. data/lib/active_model/validations/resolve_value.rb +26 -0
  60. data/lib/active_model/validations/validates.rb +5 -5
  61. data/lib/active_model/validations/with.rb +9 -2
  62. data/lib/active_model/validations.rb +45 -10
  63. data/lib/active_model/validator.rb +7 -5
  64. data/lib/active_model/version.rb +1 -1
  65. data/lib/active_model.rb +5 -1
  66. metadata +18 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad56f444d69f19ff5f6edb13f609b195f7181ec638d34190cbd1adc2f00785cf
4
- data.tar.gz: 91d230cd33a25bb5667db1a6a6586b1245caa79f03cf4cf8d45e59f4a7ab17ae
3
+ metadata.gz: 7bead2532e6ee2356d90a899c3f4b72a130b214f5c98b7db0cc37fe1f2fffbb7
4
+ data.tar.gz: fb9bd5f3c104cf093adfe01097f5032a60605d8d933b32acf4f11575d0857172
5
5
  SHA512:
6
- metadata.gz: d15c2f7cb11e877e7d7bd5fd423a2884c71878cc60d14d9edb67947ff19ac518b63f3989a8c97ed2e5745dca344a2cb09d014f1a513700d7c81a79d5814421c4
7
- data.tar.gz: 3a252fc7643b6059f41a80126150aa1b65b3c6ad432a3612443e33d487d75a4dbab082491e112fa8af0d4a6341729faa9dcd5a477f6d77057edf7f0a1cb0a153
6
+ metadata.gz: 21ca777c14c33a0383e08ca5e3d62e478d7d62d104119675be305b5c1365624d3676bd6e21f6d9dee081cfc3d6c44c9e9e2b5dd362b6a3ecd108f3430c0b58ed
7
+ data.tar.gz: 1f251ea6da1c7d4cf7f0e6a404fd724d534488518e5c528eeaa6b31843655290ea181d6adcc47e6477863a19543af426bef904b2f6b3835826ad6d9545710876
data/CHANGELOG.md CHANGED
@@ -1,64 +1,18 @@
1
- ## Rails 7.0.10 (October 28, 2025) ##
1
+ ## Rails 7.1.0.beta1 (September 13, 2023) ##
2
2
 
3
- * No changes.
3
+ * Support composite identifiers in `to_key`
4
4
 
5
+ `to_key` avoids wrapping `#id` value into an `Array` if `#id` already an array
5
6
 
6
- ## Rails 7.0.9 (October 28, 2025) ##
7
+ *Nikita Vasilevsky*
7
8
 
8
- * No changes.
9
+ * Add `ActiveModel::Conversion.param_delimiter` to configure delimiter being used in `to_param`
9
10
 
11
+ *Nikita Vasilevsky*
10
12
 
11
- ## Rails 7.0.8.7 (December 10, 2024) ##
13
+ * `undefine_attribute_methods` undefines alias attribute methods along with attribute methods.
12
14
 
13
- * No changes.
14
-
15
-
16
- ## Rails 7.0.8.6 (October 23, 2024) ##
17
-
18
- * No changes.
19
-
20
-
21
- ## Rails 7.0.8.5 (October 15, 2024) ##
22
-
23
- * No changes.
24
-
25
-
26
- ## Rails 7.0.8.4 (June 04, 2024) ##
27
-
28
- * No changes.
29
-
30
-
31
- ## Rails 7.0.8.3 (May 17, 2024) ##
32
-
33
- * No changes.
34
-
35
-
36
- ## Rails 7.0.8.2 (May 16, 2024) ##
37
-
38
- * No changes.
39
-
40
-
41
- ## Rails 7.0.8.1 (February 21, 2024) ##
42
-
43
- * No changes.
44
-
45
-
46
- ## Rails 7.0.8 (September 09, 2023) ##
47
-
48
- * No changes.
49
-
50
-
51
- ## Rails 7.0.7.2 (August 22, 2023) ##
52
-
53
- * No changes.
54
-
55
-
56
- ## Rails 7.0.7.1 (August 22, 2023) ##
57
-
58
- * No changes.
59
-
60
-
61
- ## Rails 7.0.7 (August 09, 2023) ##
15
+ *Nikita Vasilevsky*
62
16
 
63
17
  * Error.full_message now strips ":base" from the message.
64
18
 
@@ -67,243 +21,185 @@
67
21
  * Add a load hook for `ActiveModel::Model` (named `active_model`) to match the load hook for
68
22
  `ActiveRecord::Base` and allow for overriding aspects of the `ActiveModel::Model` class.
69
23
 
24
+ *Lewis Buckley*
70
25
 
71
- ## Rails 7.0.6 (June 29, 2023) ##
72
-
73
- * No changes.
74
-
75
-
76
- ## Rails 7.0.5.1 (June 26, 2023) ##
26
+ * Improve password length validation in ActiveModel::SecurePassword to consider byte size for BCrypt
27
+ compatibility.
77
28
 
78
- * No changes.
29
+ The previous password length validation only considered the character count, which may not
30
+ accurately reflect the 72-byte size limit imposed by BCrypt. This change updates the validation
31
+ to consider both character count and byte size while keeping the character length validation in place.
79
32
 
80
-
81
- ## Rails 7.0.5 (May 24, 2023) ##
82
-
83
- * No changes.
84
-
85
-
86
- ## Rails 7.0.4.3 (March 13, 2023) ##
87
-
88
- * No changes.
89
-
90
-
91
- ## Rails 7.0.4.2 (January 24, 2023) ##
92
-
93
- * No changes.
94
-
95
-
96
- ## Rails 7.0.4.1 (January 17, 2023) ##
97
-
98
- * No changes.
33
+ ```ruby
34
+ user = User.new(password: "a" * 73) # 73 characters
35
+ user.valid? # => false
36
+ user.errors[:password] # => ["is too long"]
99
37
 
100
38
 
101
- ## Rails 7.0.4 (September 09, 2022) ##
39
+ user = User.new(password: "あ" * 25) # 25 characters, 75 bytes
40
+ user.valid? # => false
41
+ user.errors[:password] # => ["is too long"]
42
+ ```
102
43
 
103
- * Handle name clashes in attribute methods code generation cache.
44
+ *ChatGPT*, *Guillermo Iguaran*
104
45
 
105
- When two distinct attribute methods would generate similar names,
106
- the first implementation would be incorrectly re-used.
46
+ * `has_secure_password` now generates an `#{attribute}_salt` method that returns the salt
47
+ used to compute the password digest. The salt will change whenever the password is changed,
48
+ so it can be used to create single-use password reset tokens with `generates_token_for`:
107
49
 
108
50
  ```ruby
109
- class A
110
- attribute_method_suffix "_changed?"
111
- define_attribute_methods :x
112
- end
51
+ class User < ActiveRecord::Base
52
+ has_secure_password
113
53
 
114
- class B
115
- attribute_method_suffix "?"
116
- define_attribute_methods :x_changed
54
+ generates_token_for :password_reset, expires_in: 15.minutes do
55
+ password_salt&.last(10)
56
+ end
117
57
  end
118
58
  ```
119
59
 
120
- *Jean Boussier*
121
-
122
- ## Rails 7.0.3.1 (July 12, 2022) ##
123
-
124
- * No changes.
60
+ *Lázaro Nixon*
125
61
 
62
+ * Improve typography of user facing error messages. In English contractions,
63
+ the Unicode APOSTROPHE (`U+0027`) is now RIGHT SINGLE QUOTATION MARK
64
+ (`U+2019`). For example, "can't be blank" is now "can’t be blank".
126
65
 
127
- ## Rails 7.0.3 (May 09, 2022) ##
66
+ *Jon Dufresne*
128
67
 
129
- * No changes.
68
+ * Add class to `ActiveModel::MissingAttributeError` error message.
130
69
 
70
+ Show which class is missing the attribute in the error message:
131
71
 
132
- ## Rails 7.0.2.4 (April 26, 2022) ##
133
-
134
- * No changes.
135
-
136
-
137
- ## Rails 7.0.2.3 (March 08, 2022) ##
138
-
139
- * No changes.
140
-
141
-
142
- ## Rails 7.0.2.2 (February 11, 2022) ##
143
-
144
- * No changes.
145
-
146
-
147
- ## Rails 7.0.2.1 (February 11, 2022) ##
148
-
149
- * No changes.
150
-
151
-
152
- ## Rails 7.0.2 (February 08, 2022) ##
153
-
154
- * Use different cache namespace for proxy calls
155
-
156
- Models can currently have different attribute bodies for the same method
157
- names, leading to conflicts. Adding a new namespace `:active_model_proxy`
158
- fixes the issue.
159
-
160
- *Chris Salzberg*
161
-
162
-
163
- ## Rails 7.0.1 (January 06, 2022) ##
164
-
165
- * No changes.
166
-
167
-
168
- ## Rails 7.0.0 (December 15, 2021) ##
169
-
170
- * No changes.
171
-
172
-
173
- ## Rails 7.0.0.rc3 (December 14, 2021) ##
174
-
175
- * No changes.
176
-
177
-
178
- ## Rails 7.0.0.rc2 (December 14, 2021) ##
179
-
180
- * No changes.
181
-
182
- ## Rails 7.0.0.rc1 (December 06, 2021) ##
183
-
184
- * Remove support to Marshal load Rails 5.x `ActiveModel::AttributeSet` format.
185
-
186
- *Rafael Mendonça França*
187
-
188
- * Remove support to Marshal and YAML load Rails 5.x error format.
189
-
190
- *Rafael Mendonça França*
191
-
192
- * Remove deprecated support to use `[]=` in `ActiveModel::Errors#messages`.
193
-
194
- *Rafael Mendonça França*
195
-
196
- * Remove deprecated support to `delete` errors from `ActiveModel::Errors#messages`.
197
-
198
- *Rafael Mendonça França*
199
-
200
- * Remove deprecated support to `clear` errors from `ActiveModel::Errors#messages`.
201
-
202
- *Rafael Mendonça França*
72
+ ```ruby
73
+ user = User.first
74
+ user.pets.select(:id).first.user_id
75
+ # => ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
76
+ ```
203
77
 
204
- * Remove deprecated support concat errors to `ActiveModel::Errors#messages`.
78
+ *Petrik de Heus*
205
79
 
206
- *Rafael Mendonça França*
80
+ * Raise `NoMethodError` in `ActiveModel::Type::Value#as_json` to avoid unpredictable
81
+ results.
207
82
 
208
- * Remove deprecated `ActiveModel::Errors#to_xml`.
83
+ *Vasiliy Ermolovich*
209
84
 
210
- *Rafael Mendonça França*
85
+ * Custom attribute types that inherit from Active Model built-in types and do
86
+ not override the `serialize` method will now benefit from an optimization
87
+ when serializing attribute values for the database.
211
88
 
212
- * Remove deprecated `ActiveModel::Errors#keys`.
89
+ For example, with a custom type like the following:
213
90
 
214
- *Rafael Mendonça França*
91
+ ```ruby
92
+ class DowncasedString < ActiveModel::Type::String
93
+ def cast(value)
94
+ super&.downcase
95
+ end
96
+ end
215
97
 
216
- * Remove deprecated `ActiveModel::Errors#values`.
98
+ ActiveRecord::Type.register(:downcased_string, DowncasedString)
217
99
 
218
- *Rafael Mendonça França*
100
+ class User < ActiveRecord::Base
101
+ attribute :email, :downcased_string
102
+ end
219
103
 
220
- * Remove deprecated `ActiveModel::Errors#slice!`.
104
+ user = User.new(email: "FooBar@example.com")
105
+ ```
221
106
 
222
- *Rafael Mendonça França*
107
+ Serializing the `email` attribute for the database will be roughly twice as
108
+ fast. More expensive `cast` operations will likely see greater improvements.
223
109
 
224
- * Remove deprecated `ActiveModel::Errors#to_h`.
110
+ *Jonathan Hefner*
225
111
 
226
- *Rafael Mendonça França*
112
+ * `has_secure_password` now supports password challenges via a
113
+ `password_challenge` accessor and validation.
227
114
 
228
- * Remove deprecated enumeration of `ActiveModel::Errors` instances as a Hash.
115
+ A password challenge is a safeguard to verify that the current user is
116
+ actually the password owner. It can be used when changing sensitive model
117
+ fields, such as the password itself. It is different than a password
118
+ confirmation, which is used to prevent password typos.
229
119
 
230
- *Rafael Mendonça França*
120
+ When `password_challenge` is set, the validation checks that the value's
121
+ digest matches the *currently persisted* `password_digest` (i.e.
122
+ `password_digest_was`).
231
123
 
232
- * Clear secure password cache if password is set to `nil`
124
+ This allows a password challenge to be done as part of a typical `update`
125
+ call, just like a password confirmation. It also allows a password
126
+ challenge error to be handled in the same way as other validation errors.
233
127
 
234
- Before:
128
+ For example, in the controller, instead of:
235
129
 
236
- user.password = 'something'
237
- user.password = nil
130
+ ```ruby
131
+ password_params = params.require(:password).permit(
132
+ :password_challenge,
133
+ :password,
134
+ :password_confirmation,
135
+ )
238
136
 
239
- user.password # => 'something'
137
+ password_challenge = password_params.delete(:password_challenge)
138
+ @password_challenge_failed = !current_user.authenticate(password_challenge)
240
139
 
241
- Now:
140
+ if !@password_challenge_failed && current_user.update(password_params)
141
+ # ...
142
+ end
143
+ ```
242
144
 
243
- user.password = 'something'
244
- user.password = nil
145
+ You can now write:
245
146
 
246
- user.password # => nil
147
+ ```ruby
148
+ password_params = params.require(:password).permit(
149
+ :password_challenge,
150
+ :password,
151
+ :password_confirmation,
152
+ ).with_defaults(password_challenge: "")
153
+
154
+ if current_user.update(password_params)
155
+ # ...
156
+ end
157
+ ```
247
158
 
248
- *Markus Doits*
159
+ And, in the view, instead of checking `@password_challenge_failed`, you can
160
+ render an error for the `password_challenge` field just as you would for
161
+ other form fields, including utilizing `config.action_view.field_error_proc`.
249
162
 
250
- ## Rails 7.0.0.alpha2 (September 15, 2021) ##
163
+ *Jonathan Hefner*
251
164
 
252
- * No changes.
165
+ * Support infinite ranges for `LengthValidator`s `:in`/`:within` options
253
166
 
167
+ ```ruby
168
+ validates_length_of :first_name, in: ..30
169
+ ```
254
170
 
255
- ## Rails 7.0.0.alpha1 (September 15, 2021) ##
171
+ *fatkodima*
256
172
 
257
- * Introduce `ActiveModel::API`.
173
+ * Add support for beginless ranges to inclusivity/exclusivity validators:
258
174
 
259
- Make `ActiveModel::API` the minimum API to talk with Action Pack and Action View.
260
- This will allow adding more functionality to `ActiveModel::Model`.
175
+ ```ruby
176
+ validates_inclusion_of :birth_date, in: -> { (..Date.today) }
177
+ ```
261
178
 
262
- *Petrik de Heus*, *Nathaniel Watts*
179
+ *Bo Jeanes*
263
180
 
264
- * Fix dirty check for Float::NaN and BigDecimal::NaN.
181
+ * Make validators accept lambdas without record argument
265
182
 
266
- Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720)
267
- and can't be compared with `==`.
183
+ ```ruby
184
+ # Before
185
+ validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today }
268
186
 
269
- *Marcelo Lauxen*
187
+ # After
188
+ validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
189
+ ```
270
190
 
271
- * Fix `to_json` for `ActiveModel::Dirty` object.
191
+ *fatkodima*
272
192
 
273
- Exclude `mutations_from_database` attribute from json as it lead to recursion.
193
+ * Fix casting long strings to `Date`, `Time` or `DateTime`
274
194
 
275
- *Anil Maurya*
195
+ *fatkodima*
276
196
 
277
- * Add `ActiveModel::AttributeSet#values_for_database`.
197
+ * Use different cache namespace for proxy calls
278
198
 
279
- Returns attributes with values for assignment to the database.
199
+ Models can currently have different attribute bodies for the same method
200
+ names, leading to conflicts. Adding a new namespace `:active_model_proxy`
201
+ fixes the issue.
280
202
 
281
203
  *Chris Salzberg*
282
204
 
283
- * Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup.
284
-
285
- Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
286
-
287
- *Benoit Daloze*
288
-
289
- * Cache and re-use generated attribute methods.
290
-
291
- Generated methods with identical implementations will now share their instruction sequences
292
- leading to reduced memory retention, and slightly faster load time.
293
-
294
- *Jean Boussier*
295
-
296
- * Add `in: range` parameter to `numericality` validator.
297
-
298
- *Michal Papis*
299
-
300
- * Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
301
- `plural`, `route_key` and `singular_route_key` values.
302
-
303
- *Lukas Pokorny*
304
-
305
- * Make ActiveModel::Errors#inspect slimmer for readability
306
-
307
- *lulalala*
308
-
309
- Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activemodel/CHANGELOG.md) for previous changes.
205
+ 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
@@ -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 simply dup this attribute to avoid
182
+ # 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
+ dup
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