activemodel 7.0.8.1 → 7.1.0.beta1

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 +132 -196
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/active_model/access.rb +16 -0
  6. data/lib/active_model/api.rb +5 -5
  7. data/lib/active_model/attribute/user_provided_default.rb +4 -0
  8. data/lib/active_model/attribute.rb +26 -1
  9. data/lib/active_model/attribute_assignment.rb +1 -1
  10. data/lib/active_model/attribute_methods.rb +102 -63
  11. data/lib/active_model/attribute_registration.rb +77 -0
  12. data/lib/active_model/attribute_set.rb +9 -0
  13. data/lib/active_model/attributes.rb +62 -45
  14. data/lib/active_model/callbacks.rb +5 -5
  15. data/lib/active_model/conversion.rb +14 -4
  16. data/lib/active_model/deprecator.rb +7 -0
  17. data/lib/active_model/dirty.rb +134 -13
  18. data/lib/active_model/error.rb +4 -3
  19. data/lib/active_model/errors.rb +17 -12
  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 +4 -3
  24. data/lib/active_model/model.rb +26 -2
  25. data/lib/active_model/naming.rb +29 -10
  26. data/lib/active_model/railtie.rb +4 -0
  27. data/lib/active_model/secure_password.rb +61 -23
  28. data/lib/active_model/serialization.rb +3 -3
  29. data/lib/active_model/serializers/json.rb +1 -1
  30. data/lib/active_model/translation.rb +18 -16
  31. data/lib/active_model/type/big_integer.rb +23 -1
  32. data/lib/active_model/type/binary.rb +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 +4 -0
  40. data/lib/active_model/type/helpers/time_value.rb +28 -12
  41. data/lib/active_model/type/immutable_string.rb +37 -1
  42. data/lib/active_model/type/integer.rb +44 -1
  43. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  44. data/lib/active_model/type/string.rb +9 -1
  45. data/lib/active_model/type/time.rb +48 -7
  46. data/lib/active_model/type/value.rb +17 -1
  47. data/lib/active_model/type.rb +1 -0
  48. data/lib/active_model/validations/absence.rb +1 -1
  49. data/lib/active_model/validations/acceptance.rb +1 -1
  50. data/lib/active_model/validations/callbacks.rb +4 -4
  51. data/lib/active_model/validations/clusivity.rb +5 -8
  52. data/lib/active_model/validations/comparability.rb +0 -11
  53. data/lib/active_model/validations/comparison.rb +15 -7
  54. data/lib/active_model/validations/confirmation.rb +1 -1
  55. data/lib/active_model/validations/format.rb +6 -7
  56. data/lib/active_model/validations/length.rb +10 -8
  57. data/lib/active_model/validations/numericality.rb +35 -23
  58. data/lib/active_model/validations/presence.rb +2 -2
  59. data/lib/active_model/validations/resolve_value.rb +26 -0
  60. data/lib/active_model/validations/validates.rb +4 -4
  61. data/lib/active_model/validations/with.rb +9 -2
  62. data/lib/active_model/validations.rb +45 -10
  63. data/lib/active_model/validator.rb +7 -5
  64. data/lib/active_model/version.rb +1 -1
  65. data/lib/active_model.rb +5 -1
  66. metadata +15 -10
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
- # == Active \Model \Conversion
4
+ # = Active \Model \Conversion
5
5
  #
6
6
  # Handles default conversions: to_model, to_key, to_param, and to_partial_path.
7
7
  #
@@ -24,6 +24,14 @@ module ActiveModel
24
24
  module Conversion
25
25
  extend ActiveSupport::Concern
26
26
 
27
+ included do
28
+ ##
29
+ # :singleton-method:
30
+ #
31
+ # Accepts a string that will be used as a delimiter of object's key values in the `to_param` method.
32
+ class_attribute :param_delimiter, instance_reader: false, default: "-"
33
+ end
34
+
27
35
  # If your object is already designed to implement all of the \Active \Model
28
36
  # you can use the default <tt>:to_model</tt> implementation, which simply
29
37
  # returns +self+.
@@ -58,7 +66,7 @@ module ActiveModel
58
66
  # person.to_key # => [1]
59
67
  def to_key
60
68
  key = respond_to?(:id) && id
61
- key ? [key] : nil
69
+ key ? Array(key) : nil
62
70
  end
63
71
 
64
72
  # Returns a +string+ representing the object's key suitable for use in URLs,
@@ -80,7 +88,7 @@ module ActiveModel
80
88
  # person = Person.new(1)
81
89
  # person.to_param # => "1"
82
90
  def to_param
83
- (persisted? && key = to_key) ? key.join("-") : nil
91
+ (persisted? && (key = to_key) && key.all?) ? key.join(self.class.param_delimiter) : nil
84
92
  end
85
93
 
86
94
  # Returns a +string+ identifying the path associated with the object.
@@ -100,7 +108,9 @@ module ActiveModel
100
108
  # Provide a class level cache for #to_partial_path. This is an
101
109
  # internal method and should not be accessed directly.
102
110
  def _to_partial_path # :nodoc:
103
- @_to_partial_path ||= begin
111
+ @_to_partial_path ||= if respond_to?(:model_name)
112
+ "#{model_name.collection}/#{model_name.element}"
113
+ else
104
114
  element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
105
115
  collection = ActiveSupport::Inflector.tableize(name)
106
116
  "#{collection}/#{element}"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ def self.deprecator # :nodoc:
5
+ @deprecator ||= ActiveSupport::Deprecation.new
6
+ end
7
+ end
@@ -3,7 +3,7 @@
3
3
  require "active_model/attribute_mutation_tracker"
4
4
 
5
5
  module ActiveModel
6
- # == Active \Model \Dirty
6
+ # = Active \Model \Dirty
7
7
  #
8
8
  # Provides a way to track changes in your object in the same way as
9
9
  # Active Record does.
@@ -13,8 +13,7 @@ module ActiveModel
13
13
  # * <tt>include ActiveModel::Dirty</tt> in your object.
14
14
  # * Call <tt>define_attribute_methods</tt> passing each method you want to
15
15
  # track.
16
- # * Call <tt>[attr_name]_will_change!</tt> before each change to the tracked
17
- # attribute.
16
+ # * Call <tt>*_will_change!</tt> before each change to the tracked attribute.
18
17
  # * Call <tt>changes_applied</tt> after the changes are persisted.
19
18
  # * Call <tt>clear_changes_information</tt> when you want to reset the changes
20
19
  # information.
@@ -109,20 +108,136 @@ module ActiveModel
109
108
  # person.changes # => {"name" => ["Bill", "Bob"]}
110
109
  #
111
110
  # If an attribute is modified in-place then make use of
112
- # <tt>[attribute_name]_will_change!</tt> to mark that the attribute is changing.
111
+ # {*_will_change!}[rdoc-label:method-i-2A_will_change-21] to mark that the attribute is changing.
113
112
  # Otherwise \Active \Model can't track changes to in-place attributes. Note
114
113
  # that Active Record can detect in-place modifications automatically. You do
115
- # not need to call <tt>[attribute_name]_will_change!</tt> on Active Record models.
114
+ # not need to call <tt>*_will_change!</tt> on Active Record models.
116
115
  #
117
116
  # person.name_will_change!
118
117
  # person.name_change # => ["Bill", "Bill"]
119
118
  # person.name << 'y'
120
119
  # person.name_change # => ["Bill", "Billy"]
120
+ #
121
+ # Methods can be invoked as +name_changed?+ or by passing an argument to the
122
+ # generic method <tt>attribute_changed?("name")</tt>.
121
123
  module Dirty
122
124
  extend ActiveSupport::Concern
123
125
  include ActiveModel::AttributeMethods
124
126
 
125
127
  included do
128
+ ##
129
+ # :method: *_previously_changed?
130
+ #
131
+ # :call-seq: *_previously_changed?(**options)
132
+ #
133
+ # This method is generated for each attribute.
134
+ #
135
+ # Returns true if the attribute previously had unsaved changes.
136
+ #
137
+ # person = Person.new
138
+ # person.name = 'Britanny'
139
+ # person.save
140
+ # person.name_previously_changed? # => true
141
+ # person.name_previously_changed?(from: nil, to: 'Britanny') # => true
142
+
143
+ ##
144
+ # :method: *_changed?
145
+ #
146
+ # This method is generated for each attribute.
147
+ #
148
+ # Returns true if the attribute has unsaved changes.
149
+ #
150
+ # person = Person.new
151
+ # person.name = 'Andrew'
152
+ # person.name_changed? # => true
153
+
154
+ ##
155
+ # :method: *_change
156
+ #
157
+ # This method is generated for each attribute.
158
+ #
159
+ # Returns the old and the new value of the attribute.
160
+ #
161
+ # person = Person.new
162
+ # person.name = 'Nick'
163
+ # person.name_change # => [nil, 'Nick']
164
+
165
+ ##
166
+ # :method: *_will_change!
167
+ #
168
+ # This method is generated for each attribute.
169
+ #
170
+ # If an attribute is modified in-place then make use of
171
+ # <tt>*_will_change!</tt> to mark that the attribute is changing.
172
+ # Otherwise Active Model can’t track changes to in-place attributes. Note
173
+ # that Active Record can detect in-place modifications automatically. You
174
+ # do not need to call <tt>*_will_change!</tt> on Active Record
175
+ # models.
176
+ #
177
+ # person = Person.new('Sandy')
178
+ # person.name_will_change!
179
+ # person.name_change # => ['Sandy', 'Sandy']
180
+
181
+ ##
182
+ # :method: *_was
183
+ #
184
+ # This method is generated for each attribute.
185
+ #
186
+ # Returns the old value of the attribute.
187
+ #
188
+ # person = Person.new(name: 'Steph')
189
+ # person.name = 'Stephanie'
190
+ # person.name_change # => ['Steph', 'Stephanie']
191
+
192
+ ##
193
+ # :method: *_previous_change
194
+ #
195
+ # This method is generated for each attribute.
196
+ #
197
+ # Returns the old and the new value of the attribute before the last save.
198
+ #
199
+ # person = Person.new
200
+ # person.name = 'Emmanuel'
201
+ # person.save
202
+ # person.name_previous_change # => [nil, 'Emmanuel']
203
+
204
+ ##
205
+ # :method: *_previously_was
206
+ #
207
+ # This method is generated for each attribute.
208
+ #
209
+ # Returns the old value of the attribute before the last save.
210
+ #
211
+ # person = Person.new
212
+ # person.name = 'Sage'
213
+ # person.save
214
+ # person.name_previously_was # => nil
215
+
216
+ ##
217
+ # :method: restore_*!
218
+ #
219
+ # This method is generated for each attribute.
220
+ #
221
+ # Restores the attribute to the old value.
222
+ #
223
+ # person = Person.new
224
+ # person.name = 'Amanda'
225
+ # person.restore_name!
226
+ # person.name # => nil
227
+
228
+ ##
229
+ # :method: clear_*_change
230
+ #
231
+ # This method is generated for each attribute.
232
+ #
233
+ # Clears all dirty data of the attribute: current changes and previous changes.
234
+ #
235
+ # person = Person.new(name: 'Chris')
236
+ # person.name = 'Jason'
237
+ # person.name_change # => ['Chris', 'Jason']
238
+ # person.clear_name_change
239
+ # person.name_change # => nil
240
+
126
241
  attribute_method_suffix "_previously_changed?", "_changed?", parameters: "**options"
127
242
  attribute_method_suffix "_change", "_will_change!", "_was", parameters: false
128
243
  attribute_method_suffix "_previous_change", "_previously_was", parameters: false
@@ -174,23 +289,23 @@ module ActiveModel
174
289
  mutations_from_database.changed_attribute_names
175
290
  end
176
291
 
177
- # Dispatch target for <tt>*_changed?</tt> attribute methods.
178
- def attribute_changed?(attr_name, **options) # :nodoc:
292
+ # Dispatch target for {*_changed}[rdoc-label:method-i-2A_changed-3F] attribute methods.
293
+ def attribute_changed?(attr_name, **options)
179
294
  mutations_from_database.changed?(attr_name.to_s, **options)
180
295
  end
181
296
 
182
- # Dispatch target for <tt>*_was</tt> attribute methods.
183
- def attribute_was(attr_name) # :nodoc:
297
+ # Dispatch target for {*_was}[rdoc-label:method-i-2A_was] attribute methods.
298
+ def attribute_was(attr_name)
184
299
  mutations_from_database.original_value(attr_name.to_s)
185
300
  end
186
301
 
187
- # Dispatch target for <tt>*_previously_changed?</tt> attribute methods.
188
- def attribute_previously_changed?(attr_name, **options) # :nodoc:
302
+ # Dispatch target for {*_previously_changed}[rdoc-label:method-i-2A_previously_changed-3F] attribute methods.
303
+ def attribute_previously_changed?(attr_name, **options)
189
304
  mutations_before_last_save.changed?(attr_name.to_s, **options)
190
305
  end
191
306
 
192
- # Dispatch target for <tt>*_previously_was</tt> attribute methods.
193
- def attribute_previously_was(attr_name) # :nodoc:
307
+ # Dispatch target for {*_previously_was}[rdoc-label:method-i-2A_previously_was] attribute methods.
308
+ def attribute_previously_was(attr_name)
194
309
  mutations_before_last_save.original_value(attr_name.to_s)
195
310
  end
196
311
 
@@ -247,6 +362,12 @@ module ActiveModel
247
362
  end
248
363
 
249
364
  private
365
+ def init_internals
366
+ super
367
+ @mutations_before_last_save = nil
368
+ @mutations_from_database = nil
369
+ end
370
+
250
371
  def clear_attribute_change(attr_name)
251
372
  mutations_from_database.forget_change(attr_name.to_s)
252
373
  end
@@ -3,7 +3,7 @@
3
3
  require "active_support/core_ext/class/attribute"
4
4
 
5
5
  module ActiveModel
6
- # == Active \Model \Error
6
+ # = Active \Model \Error
7
7
  #
8
8
  # Represents one single error
9
9
  class Error
@@ -121,9 +121,10 @@ module ActiveModel
121
121
  attr_reader :attribute
122
122
  # The type of error, defaults to +:invalid+ unless specified
123
123
  attr_reader :type
124
- # The raw value provided as the second parameter when calling +errors#add+
124
+ # The raw value provided as the second parameter when calling
125
+ # <tt>errors#add</tt>
125
126
  attr_reader :raw_type
126
- # The options provided when calling +errors#add+
127
+ # The options provided when calling <tt>errors#add</tt>
127
128
  attr_reader :options
128
129
 
129
130
  # Returns the error message.
@@ -3,13 +3,12 @@
3
3
  require "active_support/core_ext/array/conversions"
4
4
  require "active_support/core_ext/string/inflections"
5
5
  require "active_support/core_ext/object/deep_dup"
6
- require "active_support/core_ext/string/filters"
7
6
  require "active_model/error"
8
7
  require "active_model/nested_error"
9
8
  require "forwardable"
10
9
 
11
10
  module ActiveModel
12
- # == Active \Model \Errors
11
+ # = Active \Model \Errors
13
12
  #
14
13
  # Provides error related functionalities you can include in your object
15
14
  # for handling error messages and interacting with Action View helpers.
@@ -215,7 +214,7 @@ module ActiveModel
215
214
 
216
215
  # Returns a Hash that can be used as the JSON representation for this
217
216
  # object. You can pass the <tt>:full_messages</tt> option. This determines
218
- # if the json object should contain full messages or not (false by default).
217
+ # if the JSON object should contain full messages or not (false by default).
219
218
  #
220
219
  # person.errors.as_json # => {:name=>["cannot be nil"]}
221
220
  # person.errors.as_json(full_messages: true) # => {:name=>["name cannot be nil"]}
@@ -285,9 +284,9 @@ module ActiveModel
285
284
  #
286
285
  # person.errors.add(:name, :blank)
287
286
  # person.errors.messages
288
- # # => {:name=>["can't be blank"]}
287
+ # # => {:name=>["cant be blank"]}
289
288
  #
290
- # person.errors.add(:name, :too_long, { count: 25 })
289
+ # person.errors.add(:name, :too_long, count: 25)
291
290
  # person.errors.messages
292
291
  # # => ["is too long (maximum is 25 characters)"]
293
292
  #
@@ -333,12 +332,12 @@ module ActiveModel
333
332
  #
334
333
  # person.errors.add :name, :blank
335
334
  # person.errors.added? :name, :blank # => true
336
- # person.errors.added? :name, "can't be blank" # => true
335
+ # person.errors.added? :name, "cant be blank" # => true
337
336
  #
338
337
  # If the error requires options, then it returns +true+ with
339
338
  # the correct options, or +false+ with incorrect or missing options.
340
339
  #
341
- # person.errors.add :name, :too_long, { count: 25 }
340
+ # person.errors.add :name, :too_long, count: 25
342
341
  # person.errors.added? :name, :too_long, count: 25 # => true
343
342
  # person.errors.added? :name, "is too long (maximum is 25 characters)" # => true
344
343
  # person.errors.added? :name, :too_long, count: 24 # => false
@@ -360,7 +359,7 @@ module ActiveModel
360
359
  # present, or +false+ otherwise. +type+ is treated the same as for +add+.
361
360
  #
362
361
  # person.errors.add :age
363
- # person.errors.add :name, :too_long, { count: 25 }
362
+ # person.errors.add :name, :too_long, count: 25
364
363
  # person.errors.of_kind? :age # => true
365
364
  # person.errors.of_kind? :name # => false
366
365
  # person.errors.of_kind? :name, :too_long # => true
@@ -386,7 +385,7 @@ module ActiveModel
386
385
  #
387
386
  # person = Person.create(address: '123 First St.')
388
387
  # person.errors.full_messages
389
- # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
388
+ # # => ["Name is too short (minimum is 5 characters)", "Name cant be blank", "Email cant be blank"]
390
389
  def full_messages
391
390
  @errors.map(&:full_message)
392
391
  end
@@ -401,7 +400,7 @@ module ActiveModel
401
400
  #
402
401
  # person = Person.create()
403
402
  # person.errors.full_messages_for(:name)
404
- # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
403
+ # # => ["Name is too short (minimum is 5 characters)", "Name cant be blank"]
405
404
  def full_messages_for(attribute)
406
405
  where(attribute).map(&:full_message).freeze
407
406
  end
@@ -415,7 +414,7 @@ module ActiveModel
415
414
  #
416
415
  # person = Person.create()
417
416
  # person.errors.messages_for(:name)
418
- # # => ["is too short (minimum is 5 characters)", "can't be blank"]
417
+ # # => ["is too short (minimum is 5 characters)", "cant be blank"]
419
418
  def messages_for(attribute)
420
419
  where(attribute).map(&:message)
421
420
  end
@@ -472,6 +471,8 @@ module ActiveModel
472
471
  end
473
472
  end
474
473
 
474
+ # = Active \Model \StrictValidationFailed
475
+ #
475
476
  # Raised when a validation cannot be corrected by end users and are considered
476
477
  # exceptional.
477
478
  #
@@ -486,14 +487,18 @@ module ActiveModel
486
487
  # person = Person.new
487
488
  # person.name = nil
488
489
  # person.valid?
489
- # # => ActiveModel::StrictValidationFailed: Name can't be blank
490
+ # # => ActiveModel::StrictValidationFailed: Name cant be blank
490
491
  class StrictValidationFailed < StandardError
491
492
  end
492
493
 
494
+ # = Active \Model \RangeError
495
+ #
493
496
  # Raised when attribute values are out of range.
494
497
  class RangeError < ::RangeError
495
498
  end
496
499
 
500
+ # = Active \Model \UnknownAttributeError
501
+ #
497
502
  # Raised when unknown attributes are supplied via mass assignment.
498
503
  #
499
504
  # class Person
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
+ # = Active \Model \ForbiddenAttributesError
5
+ #
4
6
  # Raised when forbidden attributes are used for mass assignment.
5
7
  #
6
8
  # class Person < ActiveRecord::Base
@@ -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 currently loaded version of \Active \Model as a +Gem::Version+.
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 0
12
- TINY = 8
13
- PRE = "1"
11
+ MINOR = 1
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -5,7 +5,7 @@ module ActiveModel
5
5
  # == Active \Model \Lint \Tests
6
6
  #
7
7
  # You can test whether an object is compliant with the Active \Model API by
8
- # including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will
8
+ # including +ActiveModel::Lint::Tests+ in your TestCase. It will
9
9
  # include tests that tell you whether your object is fully compliant,
10
10
  # or if not, which aspects of the API are not implemented.
11
11
  #
@@ -10,14 +10,15 @@ en:
10
10
  inclusion: "is not included in the list"
11
11
  exclusion: "is reserved"
12
12
  invalid: "is invalid"
13
- confirmation: "doesn't match %{attribute}"
13
+ confirmation: "doesnt match %{attribute}"
14
14
  accepted: "must be accepted"
15
- empty: "can't be empty"
16
- blank: "can't be blank"
15
+ empty: "cant be empty"
16
+ blank: "cant be blank"
17
17
  present: "must be blank"
18
18
  too_long:
19
19
  one: "is too long (maximum is 1 character)"
20
20
  other: "is too long (maximum is %{count} characters)"
21
+ password_too_long: "is too long"
21
22
  too_short:
22
23
  one: "is too short (minimum is 1 character)"
23
24
  other: "is too short (minimum is %{count} characters)"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModel
4
- # == Active \Model \Basic \Model
4
+ # = Active \Model \Basic \Model
5
5
  #
6
6
  # Allows implementing models similar to ActiveRecord::Base.
7
7
  # Includes ActiveModel::API for the required interface for an
@@ -37,10 +37,34 @@ module ActiveModel
37
37
  # person.omg # => true
38
38
  #
39
39
  # For more detailed information on other functionalities available, please
40
- # refer to the specific modules included in <tt>ActiveModel::Model</tt>
40
+ # refer to the specific modules included in +ActiveModel::Model+
41
41
  # (see below).
42
42
  module Model
43
43
  extend ActiveSupport::Concern
44
44
  include ActiveModel::API
45
+ include ActiveModel::Access
46
+
47
+ ##
48
+ # :method: slice
49
+ #
50
+ # :call-seq: slice(*methods)
51
+ #
52
+ # Returns a hash of the given methods with their names as keys and returned
53
+ # values as values.
54
+ #
55
+ #--
56
+ # Implemented by ActiveModel::Access#slice.
57
+
58
+ ##
59
+ # :method: values_at
60
+ #
61
+ # :call-seq: values_at(*methods)
62
+ #
63
+ # Returns an array of the values returned by the given methods.
64
+ #
65
+ #--
66
+ # Implemented by ActiveModel::Access#values_at.
45
67
  end
68
+
69
+ ActiveSupport.run_load_hooks(:active_model, Model)
46
70
  end
@@ -195,18 +195,15 @@ module ActiveModel
195
195
  #
196
196
  # Specify +options+ with additional translating options.
197
197
  def human(options = {})
198
- return @human unless @klass.respond_to?(:lookup_ancestors) &&
199
- @klass.respond_to?(:i18n_scope)
200
-
201
- defaults = @klass.lookup_ancestors.map do |klass|
202
- klass.model_name.i18n_key
203
- end
198
+ return @human if i18n_keys.empty? || i18n_scope.empty?
204
199
 
200
+ key, *defaults = i18n_keys
205
201
  defaults << options[:default] if options[:default]
206
- defaults << @human
202
+ defaults << MISSING_TRANSLATION
207
203
 
208
- options = { scope: [@klass.i18n_scope, :models], count: 1, default: defaults }.merge!(options.except(:default))
209
- I18n.translate(defaults.shift, **options)
204
+ translation = I18n.translate(key, scope: i18n_scope, count: 1, **options, default: defaults)
205
+ translation = @human if translation == MISSING_TRANSLATION
206
+ translation
210
207
  end
211
208
 
212
209
  def uncountable?
@@ -214,12 +211,26 @@ module ActiveModel
214
211
  end
215
212
 
216
213
  private
214
+ MISSING_TRANSLATION = -(2**60) # :nodoc:
215
+
217
216
  def _singularize(string)
218
217
  ActiveSupport::Inflector.underscore(string).tr("/", "_")
219
218
  end
219
+
220
+ def i18n_keys
221
+ @i18n_keys ||= if @klass.respond_to?(:lookup_ancestors)
222
+ @klass.lookup_ancestors.map { |klass| klass.model_name.i18n_key }
223
+ else
224
+ []
225
+ end
226
+ end
227
+
228
+ def i18n_scope
229
+ @i18n_scope ||= @klass.respond_to?(:i18n_scope) ? [@klass.i18n_scope, :models] : []
230
+ end
220
231
  end
221
232
 
222
- # == Active \Model \Naming
233
+ # = Active \Model \Naming
223
234
  #
224
235
  # Creates a +model_name+ method on your object.
225
236
  #
@@ -336,5 +347,13 @@ module ActiveModel
336
347
  end
337
348
  end
338
349
  private_class_method :model_name_from_record_or_class
350
+
351
+ private
352
+ def inherited(base)
353
+ super
354
+ base.class_eval do
355
+ @_model_name = nil
356
+ end
357
+ end
339
358
  end
340
359
  end
@@ -9,6 +9,10 @@ module ActiveModel
9
9
 
10
10
  config.active_model = ActiveSupport::OrderedOptions.new
11
11
 
12
+ initializer "active_model.deprecator", before: :load_environment_config do |app|
13
+ app.deprecators[:active_model] = ActiveModel.deprecator
14
+ end
15
+
12
16
  initializer "active_model.secure_password" do
13
17
  ActiveModel::SecurePassword.min_cost = Rails.env.test?
14
18
  end