activemodel 7.0.8.7 → 7.2.2.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 +30 -263
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  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 +27 -2
  9. data/lib/active_model/attribute_assignment.rb +4 -2
  10. data/lib/active_model/attribute_methods.rb +145 -85
  11. data/lib/active_model/attribute_registration.rb +117 -0
  12. data/lib/active_model/attribute_set.rb +10 -1
  13. data/lib/active_model/attributes.rb +78 -48
  14. data/lib/active_model/callbacks.rb +6 -6
  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 +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 +62 -24
  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 +6 -1
  40. data/lib/active_model/type/helpers/time_value.rb +50 -13
  41. data/lib/active_model/type/helpers/timezone.rb +5 -1
  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/registry.rb +2 -3
  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 +17 -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 +5 -5
  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 +16 -8
  56. data/lib/active_model/validations/format.rb +6 -7
  57. data/lib/active_model/validations/length.rb +10 -8
  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 +4 -4
  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 +5 -1
  67. metadata +12 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f65c2aa43d05d67e900b3b12ac60c411899082b9a0570f86cac06af8f4375abd
4
- data.tar.gz: d0b726b2429632680b2c9d12cdcbf652b536c00e192b983a9c5ccd7da2fdae34
3
+ metadata.gz: 7abaeb86e5e7dc3786831b348d5f8deb514f1e3f2b70de2c2c7e7cd79a4c1511
4
+ data.tar.gz: ecb06547a6de150912b0c81b5296cd68258958f342c9029fb7633a879710e122
5
5
  SHA512:
6
- metadata.gz: bbb5722f3e0928497062d7672d917d845127309decbc209415c4f8785a48d7ec2520441c5acc9ca6dea91a412d3b57423d3d6f88ea1e0903959c9fb108ab6307
7
- data.tar.gz: cbebd0f6c2c12e2cfe7725654a1bd7c88894492aef219fce5c7b168e53ed008f430117cd2e9da2f0e8fd3e229501b6314d3e7320742a792dea5bfa46dd2a994c
6
+ metadata.gz: e2dbf2434ce2471c32f69df40b9ecbec73df015c1100db563bd74ceca953c6feebd94c674ab2fdef7777580f79b9aea1525d976a3ff40aabbc7efa4148b1ac5f
7
+ data.tar.gz: 0c3605f04d15a6d7b7d97546183d8620b80f38d5d15c5b8e452d862721e9776d5249a3c6cd7f3b0c8b3269800db39bff939d93af92990b9680391afa5ae3e3e4
data/CHANGELOG.md CHANGED
@@ -1,299 +1,66 @@
1
- ## Rails 7.0.8.7 (December 10, 2024) ##
1
+ ## Rails 7.2.2.1 (December 10, 2024) ##
2
2
 
3
3
  * No changes.
4
4
 
5
5
 
6
- ## Rails 7.0.8.6 (October 23, 2024) ##
6
+ ## Rails 7.2.2 (October 30, 2024) ##
7
7
 
8
- * No changes.
9
-
10
-
11
- ## Rails 7.0.8.5 (October 15, 2024) ##
12
-
13
- * No changes.
14
-
15
-
16
- ## Rails 7.0.8.4 (June 04, 2024) ##
17
-
18
- * No changes.
19
-
20
-
21
- ## Rails 7.0.8.3 (May 17, 2024) ##
22
-
23
- * No changes.
24
-
25
-
26
- ## Rails 7.0.8.2 (May 16, 2024) ##
27
-
28
- * No changes.
29
-
30
-
31
- ## Rails 7.0.8.1 (February 21, 2024) ##
32
-
33
- * No changes.
34
-
35
-
36
- ## Rails 7.0.8 (September 09, 2023) ##
37
-
38
- * No changes.
39
-
40
-
41
- ## Rails 7.0.7.2 (August 22, 2023) ##
42
-
43
- * No changes.
44
-
45
-
46
- ## Rails 7.0.7.1 (August 22, 2023) ##
47
-
48
- * No changes.
49
-
50
-
51
- ## Rails 7.0.7 (August 09, 2023) ##
52
-
53
- * Error.full_message now strips ":base" from the message.
54
-
55
- *zzak*
56
-
57
- * Add a load hook for `ActiveModel::Model` (named `active_model`) to match the load hook for
58
- `ActiveRecord::Base` and allow for overriding aspects of the `ActiveModel::Model` class.
8
+ * Fix regression in `alias_attribute` to work with user defined methods.
59
9
 
10
+ `alias_attribute` would wrongly assume the attribute accessor was generated by Active Model.
60
11
 
61
- ## Rails 7.0.6 (June 29, 2023) ##
62
-
63
- * No changes.
64
-
65
-
66
- ## Rails 7.0.5.1 (June 26, 2023) ##
67
-
68
- * No changes.
69
-
70
-
71
- ## Rails 7.0.5 (May 24, 2023) ##
72
-
73
- * No changes.
74
-
75
-
76
- ## Rails 7.0.4.3 (March 13, 2023) ##
77
-
78
- * No changes.
79
-
80
-
81
- ## Rails 7.0.4.2 (January 24, 2023) ##
82
-
83
- * No changes.
84
-
85
-
86
- ## Rails 7.0.4.1 (January 17, 2023) ##
87
-
88
- * No changes.
89
-
90
-
91
- ## Rails 7.0.4 (September 09, 2022) ##
92
-
93
- * Handle name clashes in attribute methods code generation cache.
12
+ ```ruby
13
+ class Person
14
+ include ActiveModel::AttributeMethods
94
15
 
95
- When two distinct attribute methods would generate similar names,
96
- the first implementation would be incorrectly re-used.
16
+ define_attribute_methods :name
17
+ attr_accessor :name
97
18
 
98
- ```ruby
99
- class A
100
- attribute_method_suffix "_changed?"
101
- define_attribute_methods :x
19
+ alias_attribute :full_name, :name
102
20
  end
103
21
 
104
- class B
105
- attribute_method_suffix "?"
106
- define_attribute_methods :x_changed
107
- end
22
+ person.full_name # => NoMethodError: undefined method `attribute' for an instance of Person
108
23
  ```
109
24
 
110
25
  *Jean Boussier*
111
26
 
112
- ## Rails 7.0.3.1 (July 12, 2022) ##
113
-
114
- * No changes.
115
-
116
-
117
- ## Rails 7.0.3 (May 09, 2022) ##
118
-
119
- * No changes.
120
-
121
-
122
- ## Rails 7.0.2.4 (April 26, 2022) ##
123
-
124
- * No changes.
125
-
126
-
127
- ## Rails 7.0.2.3 (March 08, 2022) ##
128
-
129
- * No changes.
130
-
131
-
132
- ## Rails 7.0.2.2 (February 11, 2022) ##
133
-
134
- * No changes.
135
-
136
-
137
- ## Rails 7.0.2.1 (February 11, 2022) ##
138
-
139
- * No changes.
140
-
141
-
142
- ## Rails 7.0.2 (February 08, 2022) ##
143
-
144
- * Use different cache namespace for proxy calls
145
-
146
- Models can currently have different attribute bodies for the same method
147
- names, leading to conflicts. Adding a new namespace `:active_model_proxy`
148
- fixes the issue.
149
-
150
- *Chris Salzberg*
151
-
152
-
153
- ## Rails 7.0.1 (January 06, 2022) ##
154
27
 
155
- * No changes.
156
-
157
-
158
- ## Rails 7.0.0 (December 15, 2021) ##
159
-
160
- * No changes.
161
-
162
-
163
- ## Rails 7.0.0.rc3 (December 14, 2021) ##
28
+ ## Rails 7.2.1.2 (October 23, 2024) ##
164
29
 
165
30
  * No changes.
166
31
 
167
32
 
168
- ## Rails 7.0.0.rc2 (December 14, 2021) ##
33
+ ## Rails 7.2.1.1 (October 15, 2024) ##
169
34
 
170
35
  * No changes.
171
36
 
172
- ## Rails 7.0.0.rc1 (December 06, 2021) ##
173
-
174
- * Remove support to Marshal load Rails 5.x `ActiveModel::AttributeSet` format.
175
-
176
- *Rafael Mendonça França*
177
-
178
- * Remove support to Marshal and YAML load Rails 5.x error format.
179
-
180
- *Rafael Mendonça França*
181
-
182
- * Remove deprecated support to use `[]=` in `ActiveModel::Errors#messages`.
183
-
184
- *Rafael Mendonça França*
185
-
186
- * Remove deprecated support to `delete` errors from `ActiveModel::Errors#messages`.
187
-
188
- *Rafael Mendonça França*
189
-
190
- * Remove deprecated support to `clear` errors from `ActiveModel::Errors#messages`.
191
-
192
- *Rafael Mendonça França*
193
-
194
- * Remove deprecated support concat errors to `ActiveModel::Errors#messages`.
195
-
196
- *Rafael Mendonça França*
197
37
 
198
- * Remove deprecated `ActiveModel::Errors#to_xml`.
199
-
200
- *Rafael Mendonça França*
201
-
202
- * Remove deprecated `ActiveModel::Errors#keys`.
203
-
204
- *Rafael Mendonça França*
205
-
206
- * Remove deprecated `ActiveModel::Errors#values`.
207
-
208
- *Rafael Mendonça França*
209
-
210
- * Remove deprecated `ActiveModel::Errors#slice!`.
211
-
212
- *Rafael Mendonça França*
213
-
214
- * Remove deprecated `ActiveModel::Errors#to_h`.
215
-
216
- *Rafael Mendonça França*
217
-
218
- * Remove deprecated enumeration of `ActiveModel::Errors` instances as a Hash.
219
-
220
- *Rafael Mendonça França*
221
-
222
- * Clear secure password cache if password is set to `nil`
223
-
224
- Before:
225
-
226
- user.password = 'something'
227
- user.password = nil
228
-
229
- user.password # => 'something'
230
-
231
- Now:
232
-
233
- user.password = 'something'
234
- user.password = nil
235
-
236
- user.password # => nil
237
-
238
- *Markus Doits*
239
-
240
- ## Rails 7.0.0.alpha2 (September 15, 2021) ##
38
+ ## Rails 7.2.1 (August 22, 2024) ##
241
39
 
242
40
  * No changes.
243
41
 
244
42
 
245
- ## Rails 7.0.0.alpha1 (September 15, 2021) ##
246
-
247
- * Introduce `ActiveModel::API`.
248
-
249
- Make `ActiveModel::API` the minimum API to talk with Action Pack and Action View.
250
- This will allow adding more functionality to `ActiveModel::Model`.
251
-
252
- *Petrik de Heus*, *Nathaniel Watts*
253
-
254
- * Fix dirty check for Float::NaN and BigDecimal::NaN.
255
-
256
- Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720)
257
- and can't be compared with `==`.
258
-
259
- *Marcelo Lauxen*
260
-
261
- * Fix `to_json` for `ActiveModel::Dirty` object.
262
-
263
- Exclude `mutations_from_database` attribute from json as it lead to recursion.
264
-
265
- *Anil Maurya*
43
+ ## Rails 7.2.0 (August 09, 2024) ##
266
44
 
267
- * Add `ActiveModel::AttributeSet#values_for_database`.
268
-
269
- Returns attributes with values for assignment to the database.
270
-
271
- *Chris Salzberg*
272
-
273
- * Fix delegation in ActiveModel::Type::Registry#lookup and ActiveModel::Type.lookup.
274
-
275
- Passing a last positional argument `{}` would be incorrectly considered as keyword argument.
276
-
277
- *Benoit Daloze*
278
-
279
- * Cache and re-use generated attribute methods.
280
-
281
- Generated methods with identical implementations will now share their instruction sequences
282
- leading to reduced memory retention, and slightly faster load time.
283
-
284
- *Jean Boussier*
45
+ * Fix a bug where type casting of string to `Time` and `DateTime` doesn't
46
+ calculate minus minute value in TZ offset correctly.
285
47
 
286
- * Add `in: range` parameter to `numericality` validator.
48
+ *Akira Matsuda*
287
49
 
288
- *Michal Papis*
50
+ * Port the `type_for_attribute` method to Active Model. Classes that include
51
+ `ActiveModel::Attributes` will now provide this method. This method behaves
52
+ the same for Active Model as it does for Active Record.
289
53
 
290
- * Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
291
- `plural`, `route_key` and `singular_route_key` values.
54
+ ```ruby
55
+ class MyModel
56
+ include ActiveModel::Attributes
292
57
 
293
- *Lukas Pokorny*
58
+ attribute :my_attribute, :integer
59
+ end
294
60
 
295
- * Make ActiveModel::Errors#inspect slimmer for readability
61
+ MyModel.type_for_attribute(:my_attribute) # => #<ActiveModel::Type::Integer ...>
62
+ ```
296
63
 
297
- *lulalala*
64
+ *Jonathan Hefner*
298
65
 
299
- Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activemodel/CHANGELOG.md) for previous changes.
66
+ Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-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:
@@ -56,7 +56,7 @@ behavior out of the box:
56
56
  person.clear_name
57
57
  person.clear_age
58
58
 
59
- {Learn more}[link:classes/ActiveModel/AttributeMethods.html]
59
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/AttributeMethods.html]
60
60
 
61
61
  * Callbacks for certain operations
62
62
 
@@ -74,7 +74,7 @@ behavior out of the box:
74
74
  This generates +before_create+, +around_create+ and +after_create+
75
75
  class methods that wrap your create method.
76
76
 
77
- {Learn more}[link:classes/ActiveModel/Callbacks.html]
77
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Callbacks.html]
78
78
 
79
79
  * Tracking value changes
80
80
 
@@ -110,7 +110,7 @@ behavior out of the box:
110
110
  person.save
111
111
  person.previous_changes # => {'name' => ['bob, 'robert']}
112
112
 
113
- {Learn more}[link:classes/ActiveModel/Dirty.html]
113
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Dirty.html]
114
114
 
115
115
  * Adding +errors+ interface to objects
116
116
 
@@ -141,7 +141,7 @@ behavior out of the box:
141
141
  person.errors.full_messages
142
142
  # => ["Name cannot be nil"]
143
143
 
144
- {Learn more}[link:classes/ActiveModel/Errors.html]
144
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Errors.html]
145
145
 
146
146
  * Model name introspection
147
147
 
@@ -152,11 +152,11 @@ behavior out of the box:
152
152
  NamedPerson.model_name.name # => "NamedPerson"
153
153
  NamedPerson.model_name.human # => "Named person"
154
154
 
155
- {Learn more}[link:classes/ActiveModel/Naming.html]
155
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Naming.html]
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
@@ -179,7 +179,7 @@ behavior out of the box:
179
179
  s = SerialPerson.new
180
180
  s.to_json # => "{\"name\":null}"
181
181
 
182
- {Learn more}[link:classes/ActiveModel/Serialization.html]
182
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Serialization.html]
183
183
 
184
184
  * Internationalization (i18n) support
185
185
 
@@ -190,7 +190,7 @@ behavior out of the box:
190
190
  Person.human_attribute_name('my_attribute')
191
191
  # => "My attribute"
192
192
 
193
- {Learn more}[link:classes/ActiveModel/Translation.html]
193
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Translation.html]
194
194
 
195
195
  * Validation support
196
196
 
@@ -208,7 +208,7 @@ behavior out of the box:
208
208
  person.first_name = 'zoolander'
209
209
  person.valid? # => false
210
210
 
211
- {Learn more}[link:classes/ActiveModel/Validations.html]
211
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Validations.html]
212
212
 
213
213
  * Custom validators
214
214
 
@@ -230,7 +230,7 @@ behavior out of the box:
230
230
  p.name = "Bob"
231
231
  p.valid? # => true
232
232
 
233
- {Learn more}[link:classes/ActiveModel/Validator.html]
233
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveModel/Validator.html]
234
234
 
235
235
 
236
236
  == Download and installation
@@ -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)
@@ -150,7 +153,7 @@ module ActiveModel
150
153
  alias :assigned? :original_attribute
151
154
 
152
155
  def initialize_dup(other)
153
- if defined?(@value) && @value.duplicable?
156
+ if @value&.duplicable?
154
157
  @value = @value.dup
155
158
  end
156
159
  end
@@ -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
@@ -45,8 +45,10 @@ module ActiveModel
45
45
 
46
46
  def _assign_attribute(k, v)
47
47
  setter = :"#{k}="
48
+ public_send(setter, v)
49
+ rescue NoMethodError
48
50
  if respond_to?(setter)
49
- public_send(setter, v)
51
+ raise
50
52
  else
51
53
  raise UnknownAttributeError.new(self, k.to_s)
52
54
  end