activemodel 5.1.7 → 5.2.8.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +126 -40
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_model/attribute/user_provided_default.rb +52 -0
  6. data/lib/active_model/attribute.rb +248 -0
  7. data/lib/active_model/attribute_assignment.rb +10 -5
  8. data/lib/active_model/attribute_methods.rb +12 -10
  9. data/lib/active_model/attribute_mutation_tracker.rb +124 -0
  10. data/lib/active_model/attribute_set/builder.rb +126 -0
  11. data/lib/active_model/attribute_set/yaml_encoder.rb +41 -0
  12. data/lib/active_model/attribute_set.rb +114 -0
  13. data/lib/active_model/attributes.rb +111 -0
  14. data/lib/active_model/callbacks.rb +7 -2
  15. data/lib/active_model/conversion.rb +2 -0
  16. data/lib/active_model/dirty.rb +128 -57
  17. data/lib/active_model/errors.rb +31 -20
  18. data/lib/active_model/forbidden_attributes_protection.rb +2 -0
  19. data/lib/active_model/gem_version.rb +5 -3
  20. data/lib/active_model/lint.rb +14 -12
  21. data/lib/active_model/model.rb +2 -0
  22. data/lib/active_model/naming.rb +5 -3
  23. data/lib/active_model/railtie.rb +2 -0
  24. data/lib/active_model/secure_password.rb +5 -3
  25. data/lib/active_model/serialization.rb +3 -1
  26. data/lib/active_model/serializers/json.rb +3 -2
  27. data/lib/active_model/translation.rb +2 -0
  28. data/lib/active_model/type/big_integer.rb +2 -0
  29. data/lib/active_model/type/binary.rb +2 -0
  30. data/lib/active_model/type/boolean.rb +16 -1
  31. data/lib/active_model/type/date.rb +5 -2
  32. data/lib/active_model/type/date_time.rb +7 -0
  33. data/lib/active_model/type/decimal.rb +2 -0
  34. data/lib/active_model/type/float.rb +2 -0
  35. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +6 -0
  36. data/lib/active_model/type/helpers/mutable.rb +2 -0
  37. data/lib/active_model/type/helpers/numeric.rb +3 -1
  38. data/lib/active_model/type/helpers/time_value.rb +2 -12
  39. data/lib/active_model/type/helpers/timezone.rb +19 -0
  40. data/lib/active_model/type/helpers.rb +3 -0
  41. data/lib/active_model/type/immutable_string.rb +2 -0
  42. data/lib/active_model/type/integer.rb +3 -1
  43. data/lib/active_model/type/registry.rb +2 -0
  44. data/lib/active_model/type/string.rb +2 -0
  45. data/lib/active_model/type/time.rb +7 -0
  46. data/lib/active_model/type/value.rb +6 -0
  47. data/lib/active_model/type.rb +7 -1
  48. data/lib/active_model/validations/absence.rb +2 -0
  49. data/lib/active_model/validations/acceptance.rb +2 -0
  50. data/lib/active_model/validations/callbacks.rb +12 -8
  51. data/lib/active_model/validations/clusivity.rb +2 -0
  52. data/lib/active_model/validations/confirmation.rb +3 -1
  53. data/lib/active_model/validations/exclusion.rb +2 -0
  54. data/lib/active_model/validations/format.rb +1 -0
  55. data/lib/active_model/validations/helper_methods.rb +2 -0
  56. data/lib/active_model/validations/inclusion.rb +2 -0
  57. data/lib/active_model/validations/length.rb +10 -2
  58. data/lib/active_model/validations/numericality.rb +39 -13
  59. data/lib/active_model/validations/presence.rb +1 -0
  60. data/lib/active_model/validations/validates.rb +5 -4
  61. data/lib/active_model/validations/with.rb +2 -0
  62. data/lib/active_model/validations.rb +10 -6
  63. data/lib/active_model/validator.rb +6 -4
  64. data/lib/active_model/version.rb +2 -0
  65. data/lib/active_model.rb +7 -2
  66. metadata +18 -10
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/hash_with_indifferent_access"
2
4
  require "active_support/core_ext/object/duplicable"
5
+ require "active_model/attribute_mutation_tracker"
3
6
 
4
7
  module ActiveModel
5
8
  # == Active \Model \Dirty
@@ -128,6 +131,28 @@ module ActiveModel
128
131
  attribute_method_affix prefix: "restore_", suffix: "!"
129
132
  end
130
133
 
134
+ def initialize_dup(other) # :nodoc:
135
+ super
136
+ if self.class.respond_to?(:_default_attributes)
137
+ @attributes = self.class._default_attributes.map do |attr|
138
+ attr.with_value_from_user(@attributes.fetch_value(attr.name))
139
+ end
140
+ end
141
+ @mutations_from_database = nil
142
+ end
143
+
144
+ # Clears dirty data and moves +changes+ to +previously_changed+ and
145
+ # +mutations_from_database+ to +mutations_before_last_save+ respectively.
146
+ def changes_applied
147
+ unless defined?(@attributes)
148
+ @previously_changed = changes
149
+ end
150
+ @mutations_before_last_save = mutations_from_database
151
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
152
+ forget_attribute_assignments
153
+ @mutations_from_database = nil
154
+ end
155
+
131
156
  # Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise.
132
157
  #
133
158
  # person.changed? # => false
@@ -146,6 +171,60 @@ module ActiveModel
146
171
  changed_attributes.keys
147
172
  end
148
173
 
174
+ # Handles <tt>*_changed?</tt> for +method_missing+.
175
+ def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc:
176
+ !!changes_include?(attr) &&
177
+ (to == OPTION_NOT_GIVEN || to == _read_attribute(attr)) &&
178
+ (from == OPTION_NOT_GIVEN || from == changed_attributes[attr])
179
+ end
180
+
181
+ # Handles <tt>*_was</tt> for +method_missing+.
182
+ def attribute_was(attr) # :nodoc:
183
+ attribute_changed?(attr) ? changed_attributes[attr] : _read_attribute(attr)
184
+ end
185
+
186
+ # Handles <tt>*_previously_changed?</tt> for +method_missing+.
187
+ def attribute_previously_changed?(attr) #:nodoc:
188
+ previous_changes_include?(attr)
189
+ end
190
+
191
+ # Restore all previous data of the provided attributes.
192
+ def restore_attributes(attributes = changed)
193
+ attributes.each { |attr| restore_attribute! attr }
194
+ end
195
+
196
+ # Clears all dirty data: current changes and previous changes.
197
+ def clear_changes_information
198
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
199
+ @mutations_before_last_save = nil
200
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
201
+ forget_attribute_assignments
202
+ @mutations_from_database = nil
203
+ end
204
+
205
+ def clear_attribute_changes(attr_names)
206
+ attributes_changed_by_setter.except!(*attr_names)
207
+ attr_names.each do |attr_name|
208
+ clear_attribute_change(attr_name)
209
+ end
210
+ end
211
+
212
+ # Returns a hash of the attributes with unsaved changes indicating their original
213
+ # values like <tt>attr => original value</tt>.
214
+ #
215
+ # person.name # => "bob"
216
+ # person.name = 'robert'
217
+ # person.changed_attributes # => {"name" => "bob"}
218
+ def changed_attributes
219
+ # This should only be set by methods which will call changed_attributes
220
+ # multiple times when it is known that the computed value cannot change.
221
+ if defined?(@cached_changed_attributes)
222
+ @cached_changed_attributes
223
+ else
224
+ attributes_changed_by_setter.reverse_merge(mutations_from_database.changed_values).freeze
225
+ end
226
+ end
227
+
149
228
  # Returns a hash of changed attributes indicating their original
150
229
  # and new values like <tt>attr => [original value, new value]</tt>.
151
230
  #
@@ -153,7 +232,9 @@ module ActiveModel
153
232
  # person.name = 'bob'
154
233
  # person.changes # => { "name" => ["bill", "bob"] }
155
234
  def changes
156
- ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
235
+ cache_changed_attributes do
236
+ ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
237
+ end
157
238
  end
158
239
 
159
240
  # Returns a hash of attributes that were changed before the model was saved.
@@ -164,45 +245,51 @@ module ActiveModel
164
245
  # person.previous_changes # => {"name" => ["bob", "robert"]}
165
246
  def previous_changes
166
247
  @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
248
+ @previously_changed.merge(mutations_before_last_save.changes)
167
249
  end
168
250
 
169
- # Returns a hash of the attributes with unsaved changes indicating their original
170
- # values like <tt>attr => original value</tt>.
171
- #
172
- # person.name # => "bob"
173
- # person.name = 'robert'
174
- # person.changed_attributes # => {"name" => "bob"}
175
- def changed_attributes
176
- @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
251
+ def attribute_changed_in_place?(attr_name) # :nodoc:
252
+ mutations_from_database.changed_in_place?(attr_name)
177
253
  end
178
254
 
179
- # Handles <tt>*_changed?</tt> for +method_missing+.
180
- def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc:
181
- !!changes_include?(attr) &&
182
- (to == OPTION_NOT_GIVEN || to == __send__(attr)) &&
183
- (from == OPTION_NOT_GIVEN || from == changed_attributes[attr])
184
- end
255
+ private
256
+ def clear_attribute_change(attr_name)
257
+ mutations_from_database.forget_change(attr_name)
258
+ end
185
259
 
186
- # Handles <tt>*_was</tt> for +method_missing+.
187
- def attribute_was(attr) # :nodoc:
188
- attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
189
- end
260
+ def mutations_from_database
261
+ unless defined?(@mutations_from_database)
262
+ @mutations_from_database = nil
263
+ end
264
+ @mutations_from_database ||= if defined?(@attributes)
265
+ ActiveModel::AttributeMutationTracker.new(@attributes)
266
+ else
267
+ NullMutationTracker.instance
268
+ end
269
+ end
190
270
 
191
- # Handles <tt>*_previously_changed?</tt> for +method_missing+.
192
- def attribute_previously_changed?(attr) #:nodoc:
193
- previous_changes_include?(attr)
194
- end
271
+ def forget_attribute_assignments
272
+ @attributes = @attributes.map(&:forgetting_assignment) if defined?(@attributes)
273
+ end
195
274
 
196
- # Restore all previous data of the provided attributes.
197
- def restore_attributes(attributes = changed)
198
- attributes.each { |attr| restore_attribute! attr }
199
- end
275
+ def mutations_before_last_save
276
+ @mutations_before_last_save ||= ActiveModel::NullMutationTracker.instance
277
+ end
200
278
 
201
- private
279
+ def cache_changed_attributes
280
+ @cached_changed_attributes = changed_attributes
281
+ yield
282
+ ensure
283
+ clear_changed_attributes_cache
284
+ end
285
+
286
+ def clear_changed_attributes_cache
287
+ remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
288
+ end
202
289
 
203
290
  # Returns +true+ if attr_name is changed, +false+ otherwise.
204
291
  def changes_include?(attr_name)
205
- attributes_changed_by_setter.include?(attr_name)
292
+ attributes_changed_by_setter.include?(attr_name) || mutations_from_database.changed?(attr_name)
206
293
  end
207
294
  alias attribute_changed_by_setter? changes_include?
208
295
 
@@ -212,21 +299,9 @@ module ActiveModel
212
299
  previous_changes.include?(attr_name)
213
300
  end
214
301
 
215
- # Removes current changes and makes them accessible through +previous_changes+.
216
- def changes_applied # :doc:
217
- @previously_changed = changes
218
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
219
- end
220
-
221
- # Clears all dirty data: current changes and previous changes.
222
- def clear_changes_information # :doc:
223
- @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
224
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
225
- end
226
-
227
302
  # Handles <tt>*_change</tt> for +method_missing+.
228
303
  def attribute_change(attr)
229
- [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
304
+ [changed_attributes[attr], _read_attribute(attr)] if attribute_changed?(attr)
230
305
  end
231
306
 
232
307
  # Handles <tt>*_previous_change</tt> for +method_missing+.
@@ -236,15 +311,16 @@ module ActiveModel
236
311
 
237
312
  # Handles <tt>*_will_change!</tt> for +method_missing+.
238
313
  def attribute_will_change!(attr)
239
- return if attribute_changed?(attr)
314
+ unless attribute_changed?(attr)
315
+ begin
316
+ value = _read_attribute(attr)
317
+ value = value.duplicable? ? value.clone : value
318
+ rescue TypeError, NoMethodError
319
+ end
240
320
 
241
- begin
242
- value = __send__(attr)
243
- value = value.duplicable? ? value.clone : value
244
- rescue TypeError, NoMethodError
321
+ set_attribute_was(attr, value)
245
322
  end
246
-
247
- set_attribute_was(attr, value)
323
+ mutations_from_database.force_change(attr)
248
324
  end
249
325
 
250
326
  # Handles <tt>restore_*!</tt> for +method_missing+.
@@ -255,18 +331,13 @@ module ActiveModel
255
331
  end
256
332
  end
257
333
 
258
- # This is necessary because `changed_attributes` might be overridden in
259
- # other implementations (e.g. in `ActiveRecord`)
260
- alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc:
334
+ def attributes_changed_by_setter
335
+ @attributes_changed_by_setter ||= ActiveSupport::HashWithIndifferentAccess.new
336
+ end
261
337
 
262
338
  # Force an attribute to have a particular "before" value
263
339
  def set_attribute_was(attr, old_value)
264
340
  attributes_changed_by_setter[attr] = old_value
265
341
  end
266
-
267
- # Remove changes information for the provided attributes.
268
- def clear_attribute_changes(attributes) # :doc:
269
- attributes_changed_by_setter.except!(*attributes)
270
- end
271
342
  end
272
343
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/array/conversions"
2
4
  require "active_support/core_ext/string/inflections"
3
5
  require "active_support/core_ext/object/deep_dup"
@@ -93,6 +95,18 @@ module ActiveModel
93
95
  @details = other.details.dup
94
96
  end
95
97
 
98
+ # Merges the errors from <tt>other</tt>.
99
+ #
100
+ # other - The ActiveModel::Errors instance.
101
+ #
102
+ # Examples
103
+ #
104
+ # person.errors.merge!(other)
105
+ def merge!(other)
106
+ @messages.merge!(other.messages) { |_, ary1, ary2| ary1 + ary2 }
107
+ @details.merge!(other.details) { |_, ary1, ary2| ary1 + ary2 }
108
+ end
109
+
96
110
  # Clear the error messages.
97
111
  #
98
112
  # person.errors.full_messages # => ["name cannot be nil"]
@@ -132,15 +146,6 @@ module ActiveModel
132
146
  #
133
147
  # person.errors[:name] # => ["cannot be nil"]
134
148
  # person.errors['name'] # => ["cannot be nil"]
135
- #
136
- # Note that, if you try to get errors of an attribute which has
137
- # no errors associated with it, this method will instantiate
138
- # an empty error list for it and +keys+ will return an array
139
- # of error keys which includes this attribute.
140
- #
141
- # person.errors.keys # => []
142
- # person.errors[:name] # => []
143
- # person.errors.keys # => [:name]
144
149
  def [](attribute)
145
150
  messages[attribute.to_sym]
146
151
  end
@@ -181,7 +186,9 @@ module ActiveModel
181
186
  # person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
182
187
  # person.errors.values # => [["cannot be nil", "must be specified"]]
183
188
  def values
184
- messages.values
189
+ messages.select do |key, value|
190
+ !value.empty?
191
+ end.values
185
192
  end
186
193
 
187
194
  # Returns all message keys.
@@ -189,7 +196,9 @@ module ActiveModel
189
196
  # person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
190
197
  # person.errors.keys # => [:name]
191
198
  def keys
192
- messages.keys
199
+ messages.select do |key, value|
200
+ !value.empty?
201
+ end.keys
193
202
  end
194
203
 
195
204
  # Returns +true+ if no errors are found, +false+ otherwise.
@@ -314,8 +323,12 @@ module ActiveModel
314
323
  # person.errors.added? :name, "is too long" # => false
315
324
  def added?(attribute, message = :invalid, options = {})
316
325
  message = message.call if message.respond_to?(:call)
317
- message = normalize_message(attribute, message, options)
318
- self[attribute].include? message
326
+
327
+ if message.is_a? Symbol
328
+ details[attribute.to_sym].include? normalize_detail(message, options)
329
+ else
330
+ self[attribute].include? message
331
+ end
319
332
  end
320
333
 
321
334
  # Returns all the full error messages in an array.
@@ -389,21 +402,19 @@ module ActiveModel
389
402
  type = options.delete(:message) if options[:message].is_a?(Symbol)
390
403
 
391
404
  if @base.class.respond_to?(:i18n_scope)
392
- defaults = @base.class.lookup_ancestors.map do |klass|
393
- [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
394
- :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
405
+ i18n_scope = @base.class.i18n_scope.to_s
406
+ defaults = @base.class.lookup_ancestors.flat_map do |klass|
407
+ [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
408
+ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
395
409
  end
410
+ defaults << :"#{i18n_scope}.errors.messages.#{type}"
396
411
  else
397
412
  defaults = []
398
413
  end
399
414
 
400
- defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
401
415
  defaults << :"errors.attributes.#{attribute}.#{type}"
402
416
  defaults << :"errors.messages.#{type}"
403
417
 
404
- defaults.compact!
405
- defaults.flatten!
406
-
407
418
  key = defaults.shift
408
419
  defaults = options.delete(:message) if options[:message]
409
420
  value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  # Raised when forbidden attributes are used for mass assignment.
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  # Returns the version of the currently loaded \Active \Model as a <tt>Gem::Version</tt>
3
5
  def self.gem_version
@@ -6,9 +8,9 @@ module ActiveModel
6
8
 
7
9
  module VERSION
8
10
  MAJOR = 5
9
- MINOR = 1
10
- TINY = 7
11
- PRE = nil
11
+ MINOR = 2
12
+ TINY = 8
13
+ PRE = "1"
12
14
 
13
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
16
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Lint
3
5
  # == Active \Model \Lint \Tests
@@ -27,7 +29,7 @@ module ActiveModel
27
29
  # <tt>to_key</tt> returns an Enumerable of all (primary) key attributes
28
30
  # of the model, and is used to a generate unique DOM id for the object.
29
31
  def test_to_key
30
- assert model.respond_to?(:to_key), "The model should respond to to_key"
32
+ assert_respond_to model, :to_key
31
33
  def model.persisted?() false end
32
34
  assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false"
33
35
  end
@@ -42,7 +44,7 @@ module ActiveModel
42
44
  # tests for this behavior in lint because it doesn't make sense to force
43
45
  # any of the possible implementation strategies on the implementer.
44
46
  def test_to_param
45
- assert model.respond_to?(:to_param), "The model should respond to to_param"
47
+ assert_respond_to model, :to_param
46
48
  def model.to_key() [1] end
47
49
  def model.persisted?() false end
48
50
  assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false"
@@ -54,7 +56,7 @@ module ActiveModel
54
56
  # <tt>to_partial_path</tt> is used for looking up partials. For example,
55
57
  # a BlogPost model might return "blog_posts/blog_post".
56
58
  def test_to_partial_path
57
- assert model.respond_to?(:to_partial_path), "The model should respond to to_partial_path"
59
+ assert_respond_to model, :to_partial_path
58
60
  assert_kind_of String, model.to_partial_path
59
61
  end
60
62
 
@@ -66,7 +68,7 @@ module ActiveModel
66
68
  # will route to the create action. If it is persisted, a form for the
67
69
  # object will route to the update action.
68
70
  def test_persisted?
69
- assert model.respond_to?(:persisted?), "The model should respond to persisted?"
71
+ assert_respond_to model, :persisted?
70
72
  assert_boolean model.persisted?, "persisted?"
71
73
  end
72
74
 
@@ -77,14 +79,14 @@ module ActiveModel
77
79
  #
78
80
  # Check ActiveModel::Naming for more information.
79
81
  def test_model_naming
80
- assert model.class.respond_to?(:model_name), "The model class should respond to model_name"
82
+ assert_respond_to model.class, :model_name
81
83
  model_name = model.class.model_name
82
- assert model_name.respond_to?(:to_str)
83
- assert model_name.human.respond_to?(:to_str)
84
- assert model_name.singular.respond_to?(:to_str)
85
- assert model_name.plural.respond_to?(:to_str)
84
+ assert_respond_to model_name, :to_str
85
+ assert_respond_to model_name.human, :to_str
86
+ assert_respond_to model_name.singular, :to_str
87
+ assert_respond_to model_name.plural, :to_str
86
88
 
87
- assert model.respond_to?(:model_name), "The model instance should respond to model_name"
89
+ assert_respond_to model, :model_name
88
90
  assert_equal model.model_name, model.class.model_name
89
91
  end
90
92
 
@@ -98,13 +100,13 @@ module ActiveModel
98
100
  # If localization is used, the strings should be localized for the current
99
101
  # locale. If no error is present, the method should return an empty array.
100
102
  def test_errors_aref
101
- assert model.respond_to?(:errors), "The model should respond to errors"
103
+ assert_respond_to model, :errors
102
104
  assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
103
105
  end
104
106
 
105
107
  private
106
108
  def model
107
- assert @model.respond_to?(:to_model), "The object should respond to to_model"
109
+ assert_respond_to @model, :to_model
108
110
  @model.to_model
109
111
  end
110
112
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  # == Active \Model \Basic \Model
3
5
  #
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/except"
2
4
  require "active_support/core_ext/module/introspection"
3
- require "active_support/core_ext/module/remove_method"
5
+ require "active_support/core_ext/module/redefine_method"
4
6
 
5
7
  module ActiveModel
6
8
  class Name
@@ -47,7 +49,7 @@ module ActiveModel
47
49
  # :method: <=>
48
50
  #
49
51
  # :call-seq:
50
- # ==(other)
52
+ # <=>(other)
51
53
  #
52
54
  # Equivalent to <tt>String#<=></tt>.
53
55
  #
@@ -216,7 +218,7 @@ module ActiveModel
216
218
  # provided method below, or rolling your own is required.
217
219
  module Naming
218
220
  def self.extended(base) #:nodoc:
219
- base.remove_possible_method :model_name
221
+ base.silence_redefinition_of_method :model_name
220
222
  base.delegate :model_name, to: :class
221
223
  end
222
224
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_model"
2
4
  require "rails"
3
5
 
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module SecurePassword
3
5
  extend ActiveSupport::Concern
4
6
 
5
- # BCrypt hash function can handle maximum 72 characters, and if we pass
6
- # password of length more than 72 characters it ignores extra characters.
7
+ # BCrypt hash function can handle maximum 72 bytes, and if we pass
8
+ # password of length more than 72 bytes it ignores extra characters.
7
9
  # Hence need to put a restriction on password length.
8
10
  MAX_PASSWORD_LENGTH_ALLOWED = 72
9
11
 
@@ -18,7 +20,7 @@ module ActiveModel
18
20
  #
19
21
  # The following validations are added automatically:
20
22
  # * Password must be present on creation
21
- # * Password length should be less than or equal to 72 characters
23
+ # * Password length should be less than or equal to 72 bytes
22
24
  # * Confirmation of password (using a +password_confirmation+ attribute)
23
25
  #
24
26
  # If password confirmation validation is not needed, simply leave out the
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/except"
2
4
  require "active_support/core_ext/hash/slice"
3
5
 
@@ -177,7 +179,7 @@ module ActiveModel
177
179
  return unless includes = options[:include]
178
180
 
179
181
  unless includes.is_a?(Hash)
180
- includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
182
+ includes = Hash[Array(includes).flat_map { |n| n.is_a?(Hash) ? n.to_a : [[n, {}]] }]
181
183
  end
182
184
 
183
185
  includes.each do |association, opts|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/json"
2
4
 
3
5
  module ActiveModel
@@ -10,8 +12,7 @@ module ActiveModel
10
12
  included do
11
13
  extend ActiveModel::Naming
12
14
 
13
- class_attribute :include_root_in_json, instance_writer: false
14
- self.include_root_in_json = false
15
+ class_attribute :include_root_in_json, instance_writer: false, default: false
15
16
  end
16
17
 
17
18
  # Returns a hash representing the model. Some configuration can be
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  # == Active \Model \Translation
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_model/type/integer"
2
4
 
3
5
  module ActiveModel
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Type
3
5
  class Binary < Value # :nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Type
3
5
  # == Active \Model \Type \Boolean
@@ -12,12 +14,25 @@ module ActiveModel
12
14
  # - Empty strings are coerced to +nil+
13
15
  # - All other values will be coerced to +true+
14
16
  class Boolean < Value
15
- FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"].to_set
17
+ FALSE_VALUES = [
18
+ false, 0,
19
+ "0", :"0",
20
+ "f", :f,
21
+ "F", :F,
22
+ "false", :false,
23
+ "FALSE", :FALSE,
24
+ "off", :off,
25
+ "OFF", :OFF,
26
+ ].to_set.freeze
16
27
 
17
28
  def type # :nodoc:
18
29
  :boolean
19
30
  end
20
31
 
32
+ def serialize(value) # :nodoc:
33
+ cast(value)
34
+ end
35
+
21
36
  private
22
37
 
23
38
  def cast_value(value)
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Type
3
5
  class Date < Value # :nodoc:
6
+ include Helpers::Timezone
4
7
  include Helpers::AcceptsMultiparameterTime.new
5
8
 
6
9
  def type
@@ -40,14 +43,14 @@ module ActiveModel
40
43
  end
41
44
 
42
45
  def new_date(year, mon, mday)
43
- if year && year != 0
46
+ unless year.nil? || (year == 0 && mon == 0 && mday == 0)
44
47
  ::Date.new(year, mon, mday) rescue nil
45
48
  end
46
49
  end
47
50
 
48
51
  def value_from_multiparameter_assignment(*)
49
52
  time = super
50
- time && time.to_date
53
+ time && new_date(time.year, time.mon, time.mday)
51
54
  end
52
55
  end
53
56
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Type
3
5
  class DateTime < Value # :nodoc:
6
+ include Helpers::Timezone
4
7
  include Helpers::TimeValue
5
8
  include Helpers::AcceptsMultiparameterTime.new(
6
9
  defaults: { 4 => 0, 5 => 0 }
@@ -10,6 +13,10 @@ module ActiveModel
10
13
  :datetime
11
14
  end
12
15
 
16
+ def serialize(value)
17
+ super(cast(value))
18
+ end
19
+
13
20
  private
14
21
 
15
22
  def cast_value(value)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bigdecimal/util"
2
4
 
3
5
  module ActiveModel