activemodel 7.0.5 → 7.1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +158 -123
  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 +103 -64
  11. data/lib/active_model/attribute_registration.rb +77 -0
  12. data/lib/active_model/attribute_set.rb +10 -1
  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 +5 -4
  19. data/lib/active_model/errors.rb +37 -6
  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 +1 -0
  24. data/lib/active_model/model.rb +34 -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 +10 -2
  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/mutable.rb +4 -4
  40. data/lib/active_model/type/helpers/numeric.rb +6 -1
  41. data/lib/active_model/type/helpers/time_value.rb +28 -12
  42. data/lib/active_model/type/immutable_string.rb +37 -1
  43. data/lib/active_model/type/integer.rb +44 -1
  44. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  45. data/lib/active_model/type/string.rb +9 -1
  46. data/lib/active_model/type/time.rb +48 -7
  47. data/lib/active_model/type/value.rb +23 -3
  48. data/lib/active_model/type.rb +1 -0
  49. data/lib/active_model/validations/absence.rb +1 -1
  50. data/lib/active_model/validations/acceptance.rb +1 -1
  51. data/lib/active_model/validations/callbacks.rb +4 -4
  52. data/lib/active_model/validations/clusivity.rb +5 -8
  53. data/lib/active_model/validations/comparability.rb +0 -11
  54. data/lib/active_model/validations/comparison.rb +15 -7
  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 +1 -1
  59. data/lib/active_model/validations/resolve_value.rb +26 -0
  60. data/lib/active_model/validations/validates.rb +4 -4
  61. data/lib/active_model/validations/with.rb +9 -2
  62. data/lib/active_model/validations.rb +44 -9
  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 +16 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1e260ee8a07b5c46bd04d84013855604516f92bd9d2305cd4108582963551e6
4
- data.tar.gz: '030858442b97869a031487982dbd29c709af654e01e0e63df48b6b95e20f900a'
3
+ metadata.gz: 62a8e27551c3779ec7676e6d2e0e66c13c2a8f95c88562722bfdcb8d9d319f2f
4
+ data.tar.gz: 2cd6f1f9bcf63a13c2688fbc6854d1173f657bfecbfca5ef9a24bd06b5c61f32
5
5
  SHA512:
6
- metadata.gz: fef75f5d7769575131c5409a80bc17b955b43e2695f3d6425a47f99a363c49a7991f2d5ab4d7eea8908f45e2af8033ce59cf6bd163c5c9193f345040aed9f646
7
- data.tar.gz: c1f480b1de01351da6701d5cd5452837de8a44ab01f7d5f196eaab5cb5a76b6bda7666cb4c8a79fa37743f087157431a71b2b9cd63e4609d4475c19d11d4d23f
6
+ metadata.gz: 1e2132e134510005376a33192f81e408fdc3e5813f368c7c8a0ce2b00328d963c94970549219a7e9b2352136a8c223da5bb2e753e9cae9e43ce2b0f6304ad8df
7
+ data.tar.gz: bb81aaefa19e40042623a008d8e4d0e062f35590f4150eabb6349dd0b394b3b0d58276b2acdf17137a09c4f25a97caf011069590ff09cebb43581a3d7549919b
data/CHANGELOG.md CHANGED
@@ -1,229 +1,264 @@
1
- ## Rails 7.0.5 (May 24, 2023) ##
1
+ ## Rails 7.1.3.4 (June 04, 2024) ##
2
2
 
3
3
  * No changes.
4
4
 
5
5
 
6
- ## Rails 7.0.4.3 (March 13, 2023) ##
6
+ ## Rails 7.1.3.3 (May 16, 2024) ##
7
7
 
8
8
  * No changes.
9
9
 
10
10
 
11
- ## Rails 7.0.4.2 (January 24, 2023) ##
11
+ ## Rails 7.1.3.2 (February 21, 2024) ##
12
12
 
13
13
  * No changes.
14
14
 
15
15
 
16
- ## Rails 7.0.4.1 (January 17, 2023) ##
16
+ ## Rails 7.1.3.1 (February 21, 2024) ##
17
17
 
18
18
  * No changes.
19
19
 
20
20
 
21
- ## Rails 7.0.4 (September 09, 2022) ##
22
-
23
- * Handle name clashes in attribute methods code generation cache.
24
-
25
- When two distinct attribute methods would generate similar names,
26
- the first implementation would be incorrectly re-used.
27
-
28
- ```ruby
29
- class A
30
- attribute_method_suffix "_changed?"
31
- define_attribute_methods :x
32
- end
33
-
34
- class B
35
- attribute_method_suffix "?"
36
- define_attribute_methods :x_changed
37
- end
38
- ```
39
-
40
- *Jean Boussier*
41
-
42
- ## Rails 7.0.3.1 (July 12, 2022) ##
21
+ ## Rails 7.1.3 (January 16, 2024) ##
43
22
 
44
23
  * No changes.
45
24
 
46
25
 
47
- ## Rails 7.0.3 (May 09, 2022) ##
48
-
49
- * No changes.
26
+ ## Rails 7.1.2 (November 10, 2023) ##
50
27
 
28
+ * Make `==(other)` method of AttributeSet safe.
51
29
 
52
- ## Rails 7.0.2.4 (April 26, 2022) ##
30
+ *Dmitry Pogrebnoy*
53
31
 
54
- * No changes.
55
32
 
56
-
57
- ## Rails 7.0.2.3 (March 08, 2022) ##
33
+ ## Rails 7.1.1 (October 11, 2023) ##
58
34
 
59
35
  * No changes.
60
36
 
61
37
 
62
- ## Rails 7.0.2.2 (February 11, 2022) ##
38
+ ## Rails 7.1.0 (October 05, 2023) ##
63
39
 
64
40
  * No changes.
65
41
 
66
42
 
67
- ## Rails 7.0.2.1 (February 11, 2022) ##
43
+ ## Rails 7.1.0.rc2 (October 01, 2023) ##
68
44
 
69
45
  * No changes.
70
46
 
71
47
 
72
- ## Rails 7.0.2 (February 08, 2022) ##
73
-
74
- * Use different cache namespace for proxy calls
75
-
76
- Models can currently have different attribute bodies for the same method
77
- names, leading to conflicts. Adding a new namespace `:active_model_proxy`
78
- fixes the issue.
79
-
80
- *Chris Salzberg*
81
-
48
+ ## Rails 7.1.0.rc1 (September 27, 2023) ##
82
49
 
83
- ## Rails 7.0.1 (January 06, 2022) ##
50
+ * Remove change in the typography of user facing error messages.
51
+ For example, “can’t be blank” is again “can't be blank”.
84
52
 
85
- * No changes.
53
+ *Rafael Mendonça França*
86
54
 
87
55
 
88
- ## Rails 7.0.0 (December 15, 2021) ##
56
+ ## Rails 7.1.0.beta1 (September 13, 2023) ##
89
57
 
90
- * No changes.
58
+ * Support composite identifiers in `to_key`
91
59
 
60
+ `to_key` avoids wrapping `#id` value into an `Array` if `#id` already an array
92
61
 
93
- ## Rails 7.0.0.rc3 (December 14, 2021) ##
62
+ *Nikita Vasilevsky*
94
63
 
95
- * No changes.
64
+ * Add `ActiveModel::Conversion.param_delimiter` to configure delimiter being used in `to_param`
96
65
 
66
+ *Nikita Vasilevsky*
97
67
 
98
- ## Rails 7.0.0.rc2 (December 14, 2021) ##
68
+ * `undefine_attribute_methods` undefines alias attribute methods along with attribute methods.
99
69
 
100
- * No changes.
70
+ *Nikita Vasilevsky*
101
71
 
102
- ## Rails 7.0.0.rc1 (December 06, 2021) ##
72
+ * Error.full_message now strips ":base" from the message.
103
73
 
104
- * Remove support to Marshal load Rails 5.x `ActiveModel::AttributeSet` format.
74
+ *zzak*
105
75
 
106
- *Rafael Mendonça França*
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.
107
78
 
108
- * Remove support to Marshal and YAML load Rails 5.x error format.
79
+ *Lewis Buckley*
109
80
 
110
- *Rafael Mendonça França*
111
-
112
- * Remove deprecated support to use `[]=` in `ActiveModel::Errors#messages`.
81
+ * Improve password length validation in ActiveModel::SecurePassword to consider byte size for BCrypt
82
+ compatibility.
113
83
 
114
- *Rafael Mendonça França*
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.
115
87
 
116
- * Remove deprecated support to `delete` errors from `ActiveModel::Errors#messages`.
117
-
118
- *Rafael Mendonça França*
88
+ ```ruby
89
+ user = User.new(password: "a" * 73) # 73 characters
90
+ user.valid? # => false
91
+ user.errors[:password] # => ["is too long"]
119
92
 
120
- * Remove deprecated support to `clear` errors from `ActiveModel::Errors#messages`.
121
93
 
122
- *Rafael Mendonça França*
94
+ user = User.new(password: "あ" * 25) # 25 characters, 75 bytes
95
+ user.valid? # => false
96
+ user.errors[:password] # => ["is too long"]
97
+ ```
123
98
 
124
- * Remove deprecated support concat errors to `ActiveModel::Errors#messages`.
99
+ *ChatGPT*, *Guillermo Iguaran*
125
100
 
126
- *Rafael Mendonça França*
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`:
127
104
 
128
- * Remove deprecated `ActiveModel::Errors#to_xml`.
105
+ ```ruby
106
+ class User < ActiveRecord::Base
107
+ has_secure_password
129
108
 
130
- *Rafael Mendonça França*
109
+ generates_token_for :password_reset, expires_in: 15.minutes do
110
+ password_salt&.last(10)
111
+ end
112
+ end
113
+ ```
131
114
 
132
- * Remove deprecated `ActiveModel::Errors#keys`.
115
+ *Lázaro Nixon*
133
116
 
134
- *Rafael Mendonça França*
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".
135
120
 
136
- * Remove deprecated `ActiveModel::Errors#values`.
121
+ *Jon Dufresne*
137
122
 
138
- *Rafael Mendonça França*
123
+ * Add class to `ActiveModel::MissingAttributeError` error message.
139
124
 
140
- * Remove deprecated `ActiveModel::Errors#slice!`.
125
+ Show which class is missing the attribute in the error message:
141
126
 
142
- *Rafael Mendonça França*
127
+ ```ruby
128
+ user = User.first
129
+ user.pets.select(:id).first.user_id
130
+ # => ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
131
+ ```
143
132
 
144
- * Remove deprecated `ActiveModel::Errors#to_h`.
133
+ *Petrik de Heus*
145
134
 
146
- *Rafael Mendonça França*
135
+ * Raise `NoMethodError` in `ActiveModel::Type::Value#as_json` to avoid unpredictable
136
+ results.
147
137
 
148
- * Remove deprecated enumeration of `ActiveModel::Errors` instances as a Hash.
138
+ *Vasiliy Ermolovich*
149
139
 
150
- *Rafael Mendonça França*
151
-
152
- * Clear secure password cache if password is set to `nil`
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.
153
143
 
154
- Before:
144
+ For example, with a custom type like the following:
155
145
 
156
- user.password = 'something'
157
- user.password = nil
146
+ ```ruby
147
+ class DowncasedString < ActiveModel::Type::String
148
+ def cast(value)
149
+ super&.downcase
150
+ end
151
+ end
158
152
 
159
- user.password # => 'something'
153
+ ActiveRecord::Type.register(:downcased_string, DowncasedString)
160
154
 
161
- Now:
155
+ class User < ActiveRecord::Base
156
+ attribute :email, :downcased_string
157
+ end
162
158
 
163
- user.password = 'something'
164
- user.password = nil
159
+ user = User.new(email: "FooBar@example.com")
160
+ ```
165
161
 
166
- user.password # => nil
162
+ Serializing the `email` attribute for the database will be roughly twice as
163
+ fast. More expensive `cast` operations will likely see greater improvements.
167
164
 
168
- *Markus Doits*
165
+ *Jonathan Hefner*
169
166
 
170
- ## Rails 7.0.0.alpha2 (September 15, 2021) ##
167
+ * `has_secure_password` now supports password challenges via a
168
+ `password_challenge` accessor and validation.
171
169
 
172
- * No changes.
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.
173
174
 
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`).
174
178
 
175
- ## Rails 7.0.0.alpha1 (September 15, 2021) ##
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.
176
182
 
177
- * Introduce `ActiveModel::API`.
183
+ For example, in the controller, instead of:
178
184
 
179
- Make `ActiveModel::API` the minimum API to talk with Action Pack and Action View.
180
- This will allow adding more functionality to `ActiveModel::Model`.
185
+ ```ruby
186
+ password_params = params.require(:password).permit(
187
+ :password_challenge,
188
+ :password,
189
+ :password_confirmation,
190
+ )
181
191
 
182
- *Petrik de Heus*, *Nathaniel Watts*
192
+ password_challenge = password_params.delete(:password_challenge)
193
+ @password_challenge_failed = !current_user.authenticate(password_challenge)
183
194
 
184
- * Fix dirty check for Float::NaN and BigDecimal::NaN.
195
+ if !@password_challenge_failed && current_user.update(password_params)
196
+ # ...
197
+ end
198
+ ```
185
199
 
186
- Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720)
187
- and can't be compared with `==`.
200
+ You can now write:
188
201
 
189
- *Marcelo Lauxen*
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
+ ```
190
213
 
191
- * Fix `to_json` for `ActiveModel::Dirty` object.
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`.
192
217
 
193
- Exclude `mutations_from_database` attribute from json as it lead to recursion.
218
+ *Jonathan Hefner*
194
219
 
195
- *Anil Maurya*
220
+ * Support infinite ranges for `LengthValidator`s `:in`/`:within` options
196
221
 
197
- * Add `ActiveModel::AttributeSet#values_for_database`.
222
+ ```ruby
223
+ validates_length_of :first_name, in: ..30
224
+ ```
198
225
 
199
- Returns attributes with values for assignment to the database.
226
+ *fatkodima*
200
227
 
201
- *Chris Salzberg*
228
+ * Add support for beginless ranges to inclusivity/exclusivity validators:
202
229
 
203
- * Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup.
230
+ ```ruby
231
+ validates_inclusion_of :birth_date, in: -> { (..Date.today) }
232
+ ```
204
233
 
205
- Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
234
+ ```ruby
235
+ validates_exclusion_of :birth_date, in: -> { (..Date.today) }
236
+ ```
206
237
 
207
- *Benoit Daloze*
238
+ *Bo Jeanes*
208
239
 
209
- * Cache and re-use generated attribute methods.
240
+ * Make validators accept lambdas without record argument
210
241
 
211
- Generated methods with identical implementations will now share their instruction sequences
212
- leading to reduced memory retention, and slightly faster load time.
242
+ ```ruby
243
+ # Before
244
+ validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today }
213
245
 
214
- *Jean Boussier*
246
+ # After
247
+ validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
248
+ ```
215
249
 
216
- * Add `in: range` parameter to `numericality` validator.
250
+ *fatkodima*
217
251
 
218
- *Michal Papis*
252
+ * Fix casting long strings to `Date`, `Time` or `DateTime`
219
253
 
220
- * Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
221
- `plural`, `route_key` and `singular_route_key` values.
254
+ *fatkodima*
222
255
 
223
- *Lukas Pokorny*
256
+ * Use different cache namespace for proxy calls
224
257
 
225
- * Make ActiveModel::Errors#inspect slimmer for readability
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.
226
261
 
227
- *lulalala*
262
+ *Chris Salzberg*
228
263
 
229
- 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
@@ -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