activemodel 7.0.4 → 6.1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -103
  3. data/MIT-LICENSE +0 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_model/attribute.rb +0 -4
  6. data/lib/active_model/attribute_methods.rb +82 -67
  7. data/lib/active_model/attribute_set/builder.rb +10 -1
  8. data/lib/active_model/attribute_set.rb +1 -4
  9. data/lib/active_model/attributes.rb +12 -15
  10. data/lib/active_model/callbacks.rb +3 -3
  11. data/lib/active_model/conversion.rb +2 -2
  12. data/lib/active_model/dirty.rb +4 -5
  13. data/lib/active_model/error.rb +2 -2
  14. data/lib/active_model/errors.rb +248 -55
  15. data/lib/active_model/gem_version.rb +5 -5
  16. data/lib/active_model/locale/en.yml +0 -1
  17. data/lib/active_model/model.rb +59 -6
  18. data/lib/active_model/naming.rb +8 -15
  19. data/lib/active_model/secure_password.rb +2 -25
  20. data/lib/active_model/serialization.rb +2 -6
  21. data/lib/active_model/translation.rb +2 -2
  22. data/lib/active_model/type/date.rb +1 -1
  23. data/lib/active_model/type/helpers/numeric.rb +1 -9
  24. data/lib/active_model/type/helpers/time_value.rb +3 -3
  25. data/lib/active_model/type/integer.rb +1 -4
  26. data/lib/active_model/type/registry.rb +38 -8
  27. data/lib/active_model/type/time.rb +1 -1
  28. data/lib/active_model/type.rb +6 -6
  29. data/lib/active_model/validations/absence.rb +2 -2
  30. data/lib/active_model/validations/acceptance.rb +1 -1
  31. data/lib/active_model/validations/callbacks.rb +1 -1
  32. data/lib/active_model/validations/clusivity.rb +1 -1
  33. data/lib/active_model/validations/confirmation.rb +5 -5
  34. data/lib/active_model/validations/exclusion.rb +3 -3
  35. data/lib/active_model/validations/format.rb +1 -1
  36. data/lib/active_model/validations/inclusion.rb +3 -3
  37. data/lib/active_model/validations/length.rb +2 -2
  38. data/lib/active_model/validations/numericality.rb +22 -29
  39. data/lib/active_model/validations/presence.rb +1 -1
  40. data/lib/active_model/validations/validates.rb +3 -3
  41. data/lib/active_model/validations/with.rb +4 -4
  42. data/lib/active_model/validations.rb +12 -12
  43. data/lib/active_model/validator.rb +5 -5
  44. data/lib/active_model/version.rb +1 -1
  45. data/lib/active_model.rb +0 -1
  46. metadata +9 -12
  47. data/lib/active_model/api.rb +0 -99
  48. data/lib/active_model/validations/comparability.rb +0 -29
  49. data/lib/active_model/validations/comparison.rb +0 -82
@@ -4,26 +4,25 @@ require "active_model/attribute_set"
4
4
  require "active_model/attribute/user_provided_default"
5
5
 
6
6
  module ActiveModel
7
- module Attributes # :nodoc:
7
+ module Attributes #:nodoc:
8
8
  extend ActiveSupport::Concern
9
9
  include ActiveModel::AttributeMethods
10
10
 
11
11
  included do
12
- attribute_method_suffix "=", parameters: "value"
12
+ attribute_method_suffix "="
13
13
  class_attribute :attribute_types, :_default_attributes, instance_accessor: false
14
14
  self.attribute_types = Hash.new(Type.default_value)
15
15
  self._default_attributes = AttributeSet.new({})
16
16
  end
17
17
 
18
18
  module ClassMethods
19
- def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
19
+ def attribute(name, type = Type::Value.new, **options)
20
20
  name = name.to_s
21
-
22
- cast_type = Type.lookup(cast_type, **options) if Symbol === cast_type
23
- cast_type ||= attribute_types[name]
24
-
25
- self.attribute_types = attribute_types.merge(name => cast_type)
26
- define_default_attribute(name, default, cast_type)
21
+ if type.is_a?(Symbol)
22
+ type = ActiveModel::Type.lookup(type, **options.except(:default))
23
+ end
24
+ self.attribute_types = attribute_types.merge(name => type)
25
+ define_default_attribute(name, options.fetch(:default, NO_DEFAULT_PROVIDED), type)
27
26
  define_attribute_method(name)
28
27
  end
29
28
 
@@ -47,12 +46,10 @@ module ActiveModel
47
46
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
48
47
  owner, name, writer: true,
49
48
  ) do |temp_method_name, attr_name_expr|
50
- owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_model) do |batch|
51
- batch <<
52
- "def #{temp_method_name}(value)" <<
53
- " _write_attribute(#{attr_name_expr}, value)" <<
54
- "end"
55
- end
49
+ owner <<
50
+ "def #{temp_method_name}(value)" <<
51
+ " _write_attribute(#{attr_name_expr}, value)" <<
52
+ "end"
56
53
  end
57
54
  end
58
55
 
@@ -32,7 +32,7 @@ module ActiveModel
32
32
  # end
33
33
  # end
34
34
  #
35
- # Then in your class, you can use the +before_create+, +after_create+, and
35
+ # Then in your class, you can use the +before_create+, +after_create+ and
36
36
  # +around_create+ methods, just as you would in an Active Record model.
37
37
  #
38
38
  # before_create :action_before_create
@@ -63,7 +63,7 @@ module ActiveModel
63
63
  # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
64
64
  #
65
65
  module Callbacks
66
- def self.extended(base) # :nodoc:
66
+ def self.extended(base) #:nodoc:
67
67
  base.class_eval do
68
68
  include ActiveSupport::Callbacks
69
69
  end
@@ -84,7 +84,7 @@ module ActiveModel
84
84
  # define_model_callbacks :update, only: :before
85
85
  # define_model_callbacks :destroy, only: :around
86
86
  #
87
- # Would create +after_create+, +before_update+, and +around_destroy+ methods
87
+ # Would create +after_create+, +before_update+ and +around_destroy+ methods
88
88
  # only.
89
89
  #
90
90
  # You can pass in a class to before_<type>, after_<type> and around_<type>,
@@ -96,10 +96,10 @@ module ActiveModel
96
96
  self.class._to_partial_path
97
97
  end
98
98
 
99
- module ClassMethods # :nodoc:
99
+ module ClassMethods #:nodoc:
100
100
  # Provide a class level cache for #to_partial_path. This is an
101
101
  # internal method and should not be accessed directly.
102
- def _to_partial_path # :nodoc:
102
+ def _to_partial_path #:nodoc:
103
103
  @_to_partial_path ||= begin
104
104
  element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
105
105
  collection = ActiveSupport::Inflector.tableize(name)
@@ -123,11 +123,10 @@ module ActiveModel
123
123
  include ActiveModel::AttributeMethods
124
124
 
125
125
  included do
126
- attribute_method_suffix "_previously_changed?", "_changed?", parameters: "**options"
127
- attribute_method_suffix "_change", "_will_change!", "_was", parameters: false
128
- attribute_method_suffix "_previous_change", "_previously_was", parameters: false
129
- attribute_method_affix prefix: "restore_", suffix: "!", parameters: false
130
- attribute_method_affix prefix: "clear_", suffix: "_change", parameters: false
126
+ attribute_method_suffix "_changed?", "_change", "_will_change!", "_was"
127
+ attribute_method_suffix "_previously_changed?", "_previous_change", "_previously_was"
128
+ attribute_method_affix prefix: "restore_", suffix: "!"
129
+ attribute_method_affix prefix: "clear_", suffix: "_change"
131
130
  end
132
131
 
133
132
  def initialize_dup(other) # :nodoc:
@@ -159,7 +159,7 @@ module ActiveModel
159
159
  self.class.full_message(attribute, message, @base)
160
160
  end
161
161
 
162
- # See if error matches provided +attribute+, +type+, and +options+.
162
+ # See if error matches provided +attribute+, +type+ and +options+.
163
163
  #
164
164
  # Omitted params are not checked for a match.
165
165
  def match?(attribute, type = nil, **options)
@@ -176,7 +176,7 @@ module ActiveModel
176
176
  true
177
177
  end
178
178
 
179
- # See if error matches provided +attribute+, +type+, and +options+ exactly.
179
+ # See if error matches provided +attribute+, +type+ and +options+ exactly.
180
180
  #
181
181
  # All params must be equal to Error's own attributes to be considered a
182
182
  # strict match.
@@ -48,9 +48,9 @@ module ActiveModel
48
48
  #
49
49
  # The last three methods are required in your object for +Errors+ to be
50
50
  # able to generate error messages correctly and also handle multiple
51
- # languages. Of course, if you extend your object with ActiveModel::Translation
51
+ # languages. Of course, if you extend your object with <tt>ActiveModel::Translation</tt>
52
52
  # you will not need to implement the last two. Likewise, using
53
- # ActiveModel::Validations will handle the validation related methods
53
+ # <tt>ActiveModel::Validations</tt> will handle the validation related methods
54
54
  # for you.
55
55
  #
56
56
  # The above allows you to do:
@@ -63,19 +63,12 @@ module ActiveModel
63
63
  include Enumerable
64
64
 
65
65
  extend Forwardable
66
+ def_delegators :@errors, :size, :clear, :blank?, :empty?, :uniq!, :any?
67
+ # TODO: forward all enumerable methods after `each` deprecation is removed.
68
+ def_delegators :@errors, :count
66
69
 
67
- # :method: each
68
- #
69
- # :call-seq: each(&block)
70
- #
71
- # Iterates through each error object.
72
- #
73
- # person.errors.add(:name, :too_short, count: 2)
74
- # person.errors.each do |error|
75
- # # Will yield <#ActiveModel::Error attribute=name, type=too_short,
76
- # options={:count=>3}>
77
- # end
78
- def_delegators :@errors, :each, :clear, :empty?, :size, :uniq!
70
+ LEGACY_ATTRIBUTES = [:messages, :details].freeze
71
+ private_constant :LEGACY_ATTRIBUTES
79
72
 
80
73
  # The actual array of +Error+ objects
81
74
  # This method is aliased to <tt>objects</tt>.
@@ -102,14 +95,11 @@ module ActiveModel
102
95
  # Copies the errors from <tt>other</tt>.
103
96
  # For copying errors but keep <tt>@base</tt> as is.
104
97
  #
105
- # ==== Parameters
106
- #
107
- # * +other+ - The ActiveModel::Errors instance.
98
+ # other - The ActiveModel::Errors instance.
108
99
  #
109
- # ==== Examples
100
+ # Examples
110
101
  #
111
102
  # person.errors.copy!(other)
112
- #
113
103
  def copy!(other) # :nodoc:
114
104
  @errors = other.errors.deep_dup
115
105
  @errors.each { |error|
@@ -117,15 +107,14 @@ module ActiveModel
117
107
  }
118
108
  end
119
109
 
120
- # Imports one error.
110
+ # Imports one error
121
111
  # Imported errors are wrapped as a NestedError,
122
112
  # providing access to original error object.
123
113
  # If attribute or type needs to be overridden, use +override_options+.
124
114
  #
125
- # ==== Options
126
- #
127
- # * +:attribute+ - Override the attribute the error belongs to.
128
- # * +:type+ - Override type of the error.
115
+ # override_options - Hash
116
+ # @option override_options [Symbol] :attribute Override the attribute the error belongs to
117
+ # @option override_options [Symbol] :type Override type of the error.
129
118
  def import(error, override_options = {})
130
119
  [:attribute, :type].each do |key|
131
120
  if override_options.key?(key)
@@ -136,25 +125,39 @@ module ActiveModel
136
125
  end
137
126
 
138
127
  # Merges the errors from <tt>other</tt>,
139
- # each Error wrapped as NestedError.
140
- #
141
- # ==== Parameters
128
+ # each <tt>Error</tt> wrapped as <tt>NestedError</tt>.
142
129
  #
143
- # * +other+ - The ActiveModel::Errors instance.
130
+ # other - The ActiveModel::Errors instance.
144
131
  #
145
- # ==== Examples
132
+ # Examples
146
133
  #
147
134
  # person.errors.merge!(other)
148
- #
149
135
  def merge!(other)
150
- return errors if equal?(other)
151
-
152
136
  other.errors.each { |error|
153
137
  import(error)
154
138
  }
155
139
  end
156
140
 
157
- # Search for errors matching +attribute+, +type+, or +options+.
141
+ # Removes all errors except the given keys. Returns a hash containing the removed errors.
142
+ #
143
+ # person.errors.keys # => [:name, :age, :gender, :city]
144
+ # person.errors.slice!(:age, :gender) # => { :name=>["cannot be nil"], :city=>["cannot be nil"] }
145
+ # person.errors.keys # => [:age, :gender]
146
+ def slice!(*keys)
147
+ deprecation_removal_warning(:slice!)
148
+
149
+ keys = keys.map(&:to_sym)
150
+
151
+ results = messages.dup.slice!(*keys)
152
+
153
+ @errors.keep_if do |error|
154
+ keys.include?(error.attribute)
155
+ end
156
+
157
+ results
158
+ end
159
+
160
+ # Search for errors matching +attribute+, +type+ or +options+.
158
161
  #
159
162
  # Only supplied params will be matched.
160
163
  #
@@ -202,7 +205,76 @@ module ActiveModel
202
205
  # person.errors[:name] # => ["cannot be nil"]
203
206
  # person.errors['name'] # => ["cannot be nil"]
204
207
  def [](attribute)
205
- messages_for(attribute)
208
+ DeprecationHandlingMessageArray.new(messages_for(attribute), self, attribute)
209
+ end
210
+
211
+ # Iterates through each error object.
212
+ #
213
+ # person.errors.add(:name, :too_short, count: 2)
214
+ # person.errors.each do |error|
215
+ # # Will yield <#ActiveModel::Error attribute=name, type=too_short,
216
+ # options={:count=>3}>
217
+ # end
218
+ #
219
+ # To be backward compatible with past deprecated hash-like behavior,
220
+ # when block accepts two parameters instead of one, it
221
+ # iterates through each error key, value pair in the error messages hash.
222
+ # Yields the attribute and the error for that attribute. If the attribute
223
+ # has more than one error message, yields once for each error message.
224
+ #
225
+ # person.errors.add(:name, :blank, message: "can't be blank")
226
+ # person.errors.each do |attribute, message|
227
+ # # Will yield :name and "can't be blank"
228
+ # end
229
+ #
230
+ # person.errors.add(:name, :not_specified, message: "must be specified")
231
+ # person.errors.each do |attribute, message|
232
+ # # Will yield :name and "can't be blank"
233
+ # # then yield :name and "must be specified"
234
+ # end
235
+ def each(&block)
236
+ if block.arity <= 1
237
+ @errors.each(&block)
238
+ else
239
+ ActiveSupport::Deprecation.warn(<<~MSG)
240
+ Enumerating ActiveModel::Errors as a hash has been deprecated.
241
+ In Rails 6.1, `errors` is an array of Error objects,
242
+ therefore it should be accessed by a block with a single block
243
+ parameter like this:
244
+
245
+ person.errors.each do |error|
246
+ attribute = error.attribute
247
+ message = error.message
248
+ end
249
+
250
+ You are passing a block expecting two parameters,
251
+ so the old hash behavior is simulated. As this is deprecated,
252
+ this will result in an ArgumentError in Rails 7.0.
253
+ MSG
254
+ @errors.
255
+ sort { |a, b| a.attribute <=> b.attribute }.
256
+ each { |error| yield error.attribute, error.message }
257
+ end
258
+ end
259
+
260
+ # Returns all message values.
261
+ #
262
+ # person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
263
+ # person.errors.values # => [["cannot be nil", "must be specified"]]
264
+ def values
265
+ deprecation_removal_warning(:values, "errors.map { |error| error.message }")
266
+ @errors.map(&:message).freeze
267
+ end
268
+
269
+ # Returns all message keys.
270
+ #
271
+ # person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
272
+ # person.errors.keys # => [:name]
273
+ def keys
274
+ deprecation_removal_warning(:keys, "errors.attribute_names")
275
+ keys = @errors.map(&:attribute)
276
+ keys.uniq!
277
+ keys.freeze
206
278
  end
207
279
 
208
280
  # Returns all error attribute names
@@ -213,6 +285,22 @@ module ActiveModel
213
285
  @errors.map(&:attribute).uniq.freeze
214
286
  end
215
287
 
288
+ # Returns an xml formatted representation of the Errors hash.
289
+ #
290
+ # person.errors.add(:name, :blank, message: "can't be blank")
291
+ # person.errors.add(:name, :not_specified, message: "must be specified")
292
+ # person.errors.to_xml
293
+ # # =>
294
+ # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
295
+ # # <errors>
296
+ # # <error>name can't be blank</error>
297
+ # # <error>name must be specified</error>
298
+ # # </errors>
299
+ def to_xml(options = {})
300
+ deprecation_removal_warning(:to_xml)
301
+ to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
302
+ end
303
+
216
304
  # Returns a Hash that can be used as the JSON representation for this
217
305
  # object. You can pass the <tt>:full_messages</tt> option. This determines
218
306
  # if the json object should contain full messages or not (false by default).
@@ -235,26 +323,33 @@ module ActiveModel
235
323
  end
236
324
  end
237
325
 
238
- undef :to_h
326
+ def to_h
327
+ ActiveSupport::Deprecation.warn(<<~EOM)
328
+ ActiveModel::Errors#to_h is deprecated and will be removed in Rails 7.0.
329
+ Please use `ActiveModel::Errors.to_hash` instead. The values in the hash
330
+ returned by `ActiveModel::Errors.to_hash` is an array of error messages.
331
+ EOM
239
332
 
240
- EMPTY_ARRAY = [].freeze # :nodoc:
333
+ to_hash.transform_values { |values| values.last }
334
+ end
241
335
 
242
336
  # Returns a Hash of attributes with an array of their error messages.
337
+ #
338
+ # Updating this hash would still update errors state for backward
339
+ # compatibility, but this behavior is deprecated.
243
340
  def messages
244
- hash = to_hash
245
- hash.default = EMPTY_ARRAY
246
- hash.freeze
247
- hash
341
+ DeprecationHandlingMessageHash.new(self)
248
342
  end
249
343
 
250
344
  # Returns a Hash of attributes with an array of their error details.
345
+ #
346
+ # Updating this hash would still update errors state for backward
347
+ # compatibility, but this behavior is deprecated.
251
348
  def details
252
349
  hash = group_by_attribute.transform_values do |errors|
253
350
  errors.map(&:details)
254
351
  end
255
- hash.default = EMPTY_ARRAY
256
- hash.freeze
257
- hash
352
+ DeprecationHandlingDetailsHash.new(hash)
258
353
  end
259
354
 
260
355
  # Returns a Hash of attributes with an array of their Error objects.
@@ -283,14 +378,6 @@ module ActiveModel
283
378
  # If +type+ is a symbol, it will be translated using the appropriate
284
379
  # scope (see +generate_message+).
285
380
  #
286
- # person.errors.add(:name, :blank)
287
- # person.errors.messages
288
- # # => {:name=>["can't be blank"]}
289
- #
290
- # person.errors.add(:name, :too_long, { count: 25 })
291
- # person.errors.messages
292
- # # => ["is too long (maximum is 25 characters)"]
293
- #
294
381
  # If +type+ is a proc, it will be called, allowing for things like
295
382
  # <tt>Time.now</tt> to be used within an error.
296
383
  #
@@ -434,7 +521,7 @@ module ActiveModel
434
521
  # if it's not there, it's looked up in <tt>activemodel.errors.models.MODEL.MESSAGE</tt> and if
435
522
  # that is not there also, it returns the translation of the default message
436
523
  # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
437
- # name, translated attribute name, and the value are available for
524
+ # name, translated attribute name and the value are available for
438
525
  # interpolation.
439
526
  #
440
527
  # When using inheritance in your models, it will check all the inherited
@@ -455,10 +542,25 @@ module ActiveModel
455
542
  Error.generate_message(attribute, type, @base, options)
456
543
  end
457
544
 
458
- def inspect # :nodoc:
459
- inspection = @errors.inspect
545
+ def marshal_load(array) # :nodoc:
546
+ # Rails 5
547
+ @errors = []
548
+ @base = array[0]
549
+ add_from_legacy_details_hash(array[2])
550
+ end
551
+
552
+ def init_with(coder) # :nodoc:
553
+ data = coder.map
554
+
555
+ data.each { |k, v|
556
+ next if LEGACY_ATTRIBUTES.include?(k.to_sym)
557
+ instance_variable_set(:"@#{k}", v)
558
+ }
559
+
560
+ @errors ||= []
460
561
 
461
- "#<#{self.class.name} #{inspection}>"
562
+ # Legacy support Rails 5.x details hash
563
+ add_from_legacy_details_hash(data["details"]) if data.key?("details")
462
564
  end
463
565
 
464
566
  private
@@ -470,6 +572,97 @@ module ActiveModel
470
572
 
471
573
  [attribute.to_sym, type, options]
472
574
  end
575
+
576
+ def add_from_legacy_details_hash(details)
577
+ details.each { |attribute, errors|
578
+ errors.each { |error|
579
+ type = error.delete(:error)
580
+ add(attribute, type, **error)
581
+ }
582
+ }
583
+ end
584
+
585
+ def deprecation_removal_warning(method_name, alternative_message = nil)
586
+ message = +"ActiveModel::Errors##{method_name} is deprecated and will be removed in Rails 7.0."
587
+ if alternative_message
588
+ message << "\n\nTo achieve the same use:\n\n "
589
+ message << alternative_message
590
+ end
591
+ ActiveSupport::Deprecation.warn(message)
592
+ end
593
+
594
+ def deprecation_rename_warning(old_method_name, new_method_name)
595
+ ActiveSupport::Deprecation.warn("ActiveModel::Errors##{old_method_name} is deprecated. Please call ##{new_method_name} instead.")
596
+ end
597
+ end
598
+
599
+ class DeprecationHandlingMessageHash < SimpleDelegator
600
+ def initialize(errors)
601
+ @errors = errors
602
+ super(prepare_content)
603
+ end
604
+
605
+ def []=(attribute, value)
606
+ ActiveSupport::Deprecation.warn("Calling `[]=` to an ActiveModel::Errors is deprecated. Please call `ActiveModel::Errors#add` instead.")
607
+
608
+ @errors.delete(attribute)
609
+ Array(value).each do |message|
610
+ @errors.add(attribute, message)
611
+ end
612
+
613
+ __setobj__ prepare_content
614
+ end
615
+
616
+ def delete(attribute)
617
+ ActiveSupport::Deprecation.warn("Calling `delete` to an ActiveModel::Errors messages hash is deprecated. Please call `ActiveModel::Errors#delete` instead.")
618
+
619
+ @errors.delete(attribute)
620
+ end
621
+
622
+ private
623
+ def prepare_content
624
+ content = @errors.to_hash
625
+ content.each do |attribute, value|
626
+ content[attribute] = DeprecationHandlingMessageArray.new(value, @errors, attribute)
627
+ end
628
+ content.default_proc = proc do |hash, attribute|
629
+ hash = hash.dup
630
+ hash[attribute] = DeprecationHandlingMessageArray.new([], @errors, attribute)
631
+ __setobj__ hash.freeze
632
+ hash[attribute]
633
+ end
634
+ content.freeze
635
+ end
636
+ end
637
+
638
+ class DeprecationHandlingMessageArray < SimpleDelegator
639
+ def initialize(content, errors, attribute)
640
+ @errors = errors
641
+ @attribute = attribute
642
+ super(content.freeze)
643
+ end
644
+
645
+ def <<(message)
646
+ ActiveSupport::Deprecation.warn("Calling `<<` to an ActiveModel::Errors message array in order to add an error is deprecated. Please call `ActiveModel::Errors#add` instead.")
647
+
648
+ @errors.add(@attribute, message)
649
+ __setobj__ @errors.messages_for(@attribute)
650
+ self
651
+ end
652
+
653
+ def clear
654
+ ActiveSupport::Deprecation.warn("Calling `clear` to an ActiveModel::Errors message array in order to delete all errors is deprecated. Please call `ActiveModel::Errors#delete` instead.")
655
+
656
+ @errors.delete(@attribute)
657
+ end
658
+ end
659
+
660
+ class DeprecationHandlingDetailsHash < SimpleDelegator
661
+ def initialize(details)
662
+ details.default = []
663
+ details.freeze
664
+ super(details)
665
+ end
473
666
  end
474
667
 
475
668
  # Raised when a validation cannot be corrected by end users and are considered
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
- # Returns the currently loaded version of \Active \Model as a <tt>Gem::Version</tt>.
4
+ # Returns the version of the currently loaded \Active \Model as a <tt>Gem::Version</tt>
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 7
11
- MINOR = 0
12
- TINY = 4
13
- PRE = nil
10
+ MAJOR = 6
11
+ MINOR = 1
12
+ TINY = 7
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -32,6 +32,5 @@ en:
32
32
  less_than: "must be less than %{count}"
33
33
  less_than_or_equal_to: "must be less than or equal to %{count}"
34
34
  other_than: "must be other than %{count}"
35
- in: "must be in %{count}"
36
35
  odd: "must be odd"
37
36
  even: "must be even"
@@ -3,10 +3,11 @@
3
3
  module ActiveModel
4
4
  # == Active \Model \Basic \Model
5
5
  #
6
- # Allows implementing models similar to ActiveRecord::Base.
7
- # Includes ActiveModel::API for the required interface for an
8
- # object to interact with Action Pack and Action View, but can be
9
- # extended with other functionalities.
6
+ # Includes the required interface for an object to interact with
7
+ # Action Pack and Action View, using different Active Model modules.
8
+ # It includes model name introspections, conversions, translations and
9
+ # validations. Besides that, it allows you to initialize the object with a
10
+ # hash of attributes, pretty much like Active Record does.
10
11
  #
11
12
  # A minimal implementation could be:
12
13
  #
@@ -19,7 +20,23 @@ module ActiveModel
19
20
  # person.name # => "bob"
20
21
  # person.age # => "18"
21
22
  #
22
- # If for some reason you need to run code on <tt>initialize</tt>, make
23
+ # Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
24
+ # to return +false+, which is the most common case. You may want to override
25
+ # it in your class to simulate a different scenario:
26
+ #
27
+ # class Person
28
+ # include ActiveModel::Model
29
+ # attr_accessor :id, :name
30
+ #
31
+ # def persisted?
32
+ # self.id == 1
33
+ # end
34
+ # end
35
+ #
36
+ # person = Person.new(id: 1, name: 'bob')
37
+ # person.persisted? # => true
38
+ #
39
+ # Also, if for some reason you need to run code on <tt>initialize</tt>, make
23
40
  # sure you call +super+ if you want the attributes hash initialization to
24
41
  # happen.
25
42
  #
@@ -41,6 +58,42 @@ module ActiveModel
41
58
  # (see below).
42
59
  module Model
43
60
  extend ActiveSupport::Concern
44
- include ActiveModel::API
61
+ include ActiveModel::AttributeAssignment
62
+ include ActiveModel::Validations
63
+ include ActiveModel::Conversion
64
+
65
+ included do
66
+ extend ActiveModel::Naming
67
+ extend ActiveModel::Translation
68
+ end
69
+
70
+ # Initializes a new model with the given +params+.
71
+ #
72
+ # class Person
73
+ # include ActiveModel::Model
74
+ # attr_accessor :name, :age
75
+ # end
76
+ #
77
+ # person = Person.new(name: 'bob', age: '18')
78
+ # person.name # => "bob"
79
+ # person.age # => "18"
80
+ def initialize(attributes = {})
81
+ assign_attributes(attributes) if attributes
82
+
83
+ super()
84
+ end
85
+
86
+ # Indicates if the model is persisted. Default is +false+.
87
+ #
88
+ # class Person
89
+ # include ActiveModel::Model
90
+ # attr_accessor :id, :name
91
+ # end
92
+ #
93
+ # person = Person.new(id: 1, name: 'bob')
94
+ # person.persisted? # => false
95
+ def persisted?
96
+ false
97
+ end
45
98
  end
46
99
  end