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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +126 -40
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_model/attribute/user_provided_default.rb +52 -0
- data/lib/active_model/attribute.rb +248 -0
- data/lib/active_model/attribute_assignment.rb +10 -5
- data/lib/active_model/attribute_methods.rb +12 -10
- data/lib/active_model/attribute_mutation_tracker.rb +124 -0
- data/lib/active_model/attribute_set/builder.rb +126 -0
- data/lib/active_model/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_model/attribute_set.rb +114 -0
- data/lib/active_model/attributes.rb +111 -0
- data/lib/active_model/callbacks.rb +7 -2
- data/lib/active_model/conversion.rb +2 -0
- data/lib/active_model/dirty.rb +128 -57
- data/lib/active_model/errors.rb +31 -20
- data/lib/active_model/forbidden_attributes_protection.rb +2 -0
- data/lib/active_model/gem_version.rb +5 -3
- data/lib/active_model/lint.rb +14 -12
- data/lib/active_model/model.rb +2 -0
- data/lib/active_model/naming.rb +5 -3
- data/lib/active_model/railtie.rb +2 -0
- data/lib/active_model/secure_password.rb +5 -3
- data/lib/active_model/serialization.rb +3 -1
- data/lib/active_model/serializers/json.rb +3 -2
- data/lib/active_model/translation.rb +2 -0
- data/lib/active_model/type/big_integer.rb +2 -0
- data/lib/active_model/type/binary.rb +2 -0
- data/lib/active_model/type/boolean.rb +16 -1
- data/lib/active_model/type/date.rb +5 -2
- data/lib/active_model/type/date_time.rb +7 -0
- data/lib/active_model/type/decimal.rb +2 -0
- data/lib/active_model/type/float.rb +2 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +6 -0
- data/lib/active_model/type/helpers/mutable.rb +2 -0
- data/lib/active_model/type/helpers/numeric.rb +3 -1
- data/lib/active_model/type/helpers/time_value.rb +2 -12
- data/lib/active_model/type/helpers/timezone.rb +19 -0
- data/lib/active_model/type/helpers.rb +3 -0
- data/lib/active_model/type/immutable_string.rb +2 -0
- data/lib/active_model/type/integer.rb +3 -1
- data/lib/active_model/type/registry.rb +2 -0
- data/lib/active_model/type/string.rb +2 -0
- data/lib/active_model/type/time.rb +7 -0
- data/lib/active_model/type/value.rb +6 -0
- data/lib/active_model/type.rb +7 -1
- data/lib/active_model/validations/absence.rb +2 -0
- data/lib/active_model/validations/acceptance.rb +2 -0
- data/lib/active_model/validations/callbacks.rb +12 -8
- data/lib/active_model/validations/clusivity.rb +2 -0
- data/lib/active_model/validations/confirmation.rb +3 -1
- data/lib/active_model/validations/exclusion.rb +2 -0
- data/lib/active_model/validations/format.rb +1 -0
- data/lib/active_model/validations/helper_methods.rb +2 -0
- data/lib/active_model/validations/inclusion.rb +2 -0
- data/lib/active_model/validations/length.rb +10 -2
- data/lib/active_model/validations/numericality.rb +39 -13
- data/lib/active_model/validations/presence.rb +1 -0
- data/lib/active_model/validations/validates.rb +5 -4
- data/lib/active_model/validations/with.rb +2 -0
- data/lib/active_model/validations.rb +10 -6
- data/lib/active_model/validator.rb +6 -4
- data/lib/active_model/version.rb +2 -0
- data/lib/active_model.rb +7 -2
- metadata +18 -10
data/lib/active_model/dirty.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
170
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
271
|
+
def forget_attribute_assignments
|
272
|
+
@attributes = @attributes.map(&:forgetting_assignment) if defined?(@attributes)
|
273
|
+
end
|
195
274
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
end
|
275
|
+
def mutations_before_last_save
|
276
|
+
@mutations_before_last_save ||= ActiveModel::NullMutationTracker.instance
|
277
|
+
end
|
200
278
|
|
201
|
-
|
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],
|
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
|
-
|
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
|
-
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
data/lib/active_model/errors.rb
CHANGED
@@ -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.
|
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.
|
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
|
-
|
318
|
-
|
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
|
-
|
393
|
-
|
394
|
-
|
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
|
# 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 =
|
10
|
-
TINY =
|
11
|
-
PRE =
|
11
|
+
MINOR = 2
|
12
|
+
TINY = 8
|
13
|
+
PRE = "1"
|
12
14
|
|
13
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
14
16
|
end
|
data/lib/active_model/lint.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
82
|
+
assert_respond_to model.class, :model_name
|
81
83
|
model_name = model.class.model_name
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
109
|
+
assert_respond_to @model, :to_model
|
108
110
|
@model.to_model
|
109
111
|
end
|
110
112
|
|
data/lib/active_model/model.rb
CHANGED
data/lib/active_model/naming.rb
CHANGED
@@ -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/
|
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
|
-
#
|
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.
|
221
|
+
base.silence_redefinition_of_method :model_name
|
220
222
|
base.delegate :model_name, to: :class
|
221
223
|
end
|
222
224
|
|
data/lib/active_model/railtie.rb
CHANGED
@@ -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
|
6
|
-
# password of length more than 72
|
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
|
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).
|
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
|
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 = [
|
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
|
-
|
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.
|
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)
|