activemodel 5.1.7 → 5.2.8.1

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 +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