activemodel 5.2.2.1 → 6.0.2
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 +144 -51
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -2
- data/lib/active_model.rb +1 -1
- data/lib/active_model/attribute.rb +3 -4
- data/lib/active_model/attribute/user_provided_default.rb +1 -2
- data/lib/active_model/attribute_assignment.rb +1 -1
- data/lib/active_model/attribute_methods.rb +54 -15
- data/lib/active_model/attribute_mutation_tracker.rb +88 -34
- data/lib/active_model/attribute_set.rb +2 -10
- data/lib/active_model/attribute_set/builder.rb +1 -3
- data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
- data/lib/active_model/attributes.rb +60 -33
- data/lib/active_model/callbacks.rb +10 -7
- data/lib/active_model/conversion.rb +1 -1
- data/lib/active_model/dirty.rb +36 -99
- data/lib/active_model/errors.rb +104 -20
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/naming.rb +19 -3
- data/lib/active_model/railtie.rb +6 -0
- data/lib/active_model/secure_password.rb +47 -48
- data/lib/active_model/serializers/json.rb +10 -9
- data/lib/active_model/type/binary.rb +1 -1
- data/lib/active_model/type/boolean.rb +10 -1
- data/lib/active_model/type/date.rb +2 -5
- data/lib/active_model/type/date_time.rb +4 -7
- data/lib/active_model/type/float.rb +0 -2
- data/lib/active_model/type/helpers.rb +1 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +4 -0
- data/lib/active_model/type/helpers/numeric.rb +9 -2
- data/lib/active_model/type/helpers/time_value.rb +16 -15
- data/lib/active_model/type/helpers/timezone.rb +19 -0
- data/lib/active_model/type/integer.rb +7 -19
- data/lib/active_model/type/registry.rb +2 -10
- data/lib/active_model/type/string.rb +2 -2
- data/lib/active_model/type/time.rb +2 -1
- data/lib/active_model/validations.rb +0 -2
- data/lib/active_model/validations/acceptance.rb +33 -25
- data/lib/active_model/validations/clusivity.rb +1 -1
- data/lib/active_model/validations/confirmation.rb +2 -2
- data/lib/active_model/validations/inclusion.rb +1 -1
- data/lib/active_model/validations/length.rb +1 -1
- data/lib/active_model/validations/numericality.rb +20 -11
- data/lib/active_model/validations/validates.rb +2 -2
- data/lib/active_model/validator.rb +1 -1
- metadata +13 -9
data/lib/active_model/errors.rb
CHANGED
@@ -62,6 +62,11 @@ module ActiveModel
|
|
62
62
|
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
|
63
63
|
MESSAGE_OPTIONS = [:message]
|
64
64
|
|
65
|
+
class << self
|
66
|
+
attr_accessor :i18n_customize_full_message # :nodoc:
|
67
|
+
end
|
68
|
+
self.i18n_customize_full_message = false
|
69
|
+
|
65
70
|
attr_reader :messages, :details
|
66
71
|
|
67
72
|
# Pass in the instance of the object that is using the errors object.
|
@@ -107,6 +112,17 @@ module ActiveModel
|
|
107
112
|
@details.merge!(other.details) { |_, ary1, ary2| ary1 + ary2 }
|
108
113
|
end
|
109
114
|
|
115
|
+
# Removes all errors except the given keys. Returns a hash containing the removed errors.
|
116
|
+
#
|
117
|
+
# person.errors.keys # => [:name, :age, :gender, :city]
|
118
|
+
# person.errors.slice!(:age, :gender) # => { :name=>["cannot be nil"], :city=>["cannot be nil"] }
|
119
|
+
# person.errors.keys # => [:age, :gender]
|
120
|
+
def slice!(*keys)
|
121
|
+
keys = keys.map(&:to_sym)
|
122
|
+
@details.slice!(*keys)
|
123
|
+
@messages.slice!(*keys)
|
124
|
+
end
|
125
|
+
|
110
126
|
# Clear the error messages.
|
111
127
|
#
|
112
128
|
# person.errors.full_messages # => ["name cannot be nil"]
|
@@ -312,15 +328,15 @@ module ActiveModel
|
|
312
328
|
# person.errors.added? :name, :blank # => true
|
313
329
|
# person.errors.added? :name, "can't be blank" # => true
|
314
330
|
#
|
315
|
-
# If the error message requires
|
316
|
-
# the correct
|
331
|
+
# If the error message requires options, then it returns +true+ with
|
332
|
+
# the correct options, or +false+ with incorrect or missing options.
|
317
333
|
#
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
334
|
+
# person.errors.add :name, :too_long, { count: 25 }
|
335
|
+
# person.errors.added? :name, :too_long, count: 25 # => true
|
336
|
+
# person.errors.added? :name, "is too long (maximum is 25 characters)" # => true
|
337
|
+
# person.errors.added? :name, :too_long, count: 24 # => false
|
338
|
+
# person.errors.added? :name, :too_long # => false
|
339
|
+
# person.errors.added? :name, "is too long" # => false
|
324
340
|
def added?(attribute, message = :invalid, options = {})
|
325
341
|
message = message.call if message.respond_to?(:call)
|
326
342
|
|
@@ -331,6 +347,27 @@ module ActiveModel
|
|
331
347
|
end
|
332
348
|
end
|
333
349
|
|
350
|
+
# Returns +true+ if an error on the attribute with the given message is
|
351
|
+
# present, or +false+ otherwise. +message+ is treated the same as for +add+.
|
352
|
+
#
|
353
|
+
# person.errors.add :age
|
354
|
+
# person.errors.add :name, :too_long, { count: 25 }
|
355
|
+
# person.errors.of_kind? :age # => true
|
356
|
+
# person.errors.of_kind? :name # => false
|
357
|
+
# person.errors.of_kind? :name, :too_long # => true
|
358
|
+
# person.errors.of_kind? :name, "is too long (maximum is 25 characters)" # => true
|
359
|
+
# person.errors.of_kind? :name, :not_too_long # => false
|
360
|
+
# person.errors.of_kind? :name, "is too long" # => false
|
361
|
+
def of_kind?(attribute, message = :invalid)
|
362
|
+
message = message.call if message.respond_to?(:call)
|
363
|
+
|
364
|
+
if message.is_a? Symbol
|
365
|
+
details[attribute.to_sym].map { |e| e[:error] }.include? message
|
366
|
+
else
|
367
|
+
self[attribute].include? message
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
334
371
|
# Returns all the full error messages in an array.
|
335
372
|
#
|
336
373
|
# class Person
|
@@ -364,12 +401,54 @@ module ActiveModel
|
|
364
401
|
# Returns a full message for a given attribute.
|
365
402
|
#
|
366
403
|
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
|
404
|
+
#
|
405
|
+
# The `"%{attribute} %{message}"` error format can be overridden with either
|
406
|
+
#
|
407
|
+
# * <tt>activemodel.errors.models.person/contacts/addresses.attributes.street.format</tt>
|
408
|
+
# * <tt>activemodel.errors.models.person/contacts/addresses.format</tt>
|
409
|
+
# * <tt>activemodel.errors.models.person.attributes.name.format</tt>
|
410
|
+
# * <tt>activemodel.errors.models.person.format</tt>
|
411
|
+
# * <tt>errors.format</tt>
|
367
412
|
def full_message(attribute, message)
|
368
413
|
return message if attribute == :base
|
369
|
-
|
414
|
+
attribute = attribute.to_s
|
415
|
+
|
416
|
+
if self.class.i18n_customize_full_message && @base.class.respond_to?(:i18n_scope)
|
417
|
+
attribute = attribute.remove(/\[\d\]/)
|
418
|
+
parts = attribute.split(".")
|
419
|
+
attribute_name = parts.pop
|
420
|
+
namespace = parts.join("/") unless parts.empty?
|
421
|
+
attributes_scope = "#{@base.class.i18n_scope}.errors.models"
|
422
|
+
|
423
|
+
if namespace
|
424
|
+
defaults = @base.class.lookup_ancestors.map do |klass|
|
425
|
+
[
|
426
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.attributes.#{attribute_name}.format",
|
427
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.format",
|
428
|
+
]
|
429
|
+
end
|
430
|
+
else
|
431
|
+
defaults = @base.class.lookup_ancestors.map do |klass|
|
432
|
+
[
|
433
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}.attributes.#{attribute_name}.format",
|
434
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}.format",
|
435
|
+
]
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
defaults.flatten!
|
440
|
+
else
|
441
|
+
defaults = []
|
442
|
+
end
|
443
|
+
|
444
|
+
defaults << :"errors.format"
|
445
|
+
defaults << "%{attribute} %{message}"
|
446
|
+
|
447
|
+
attr_name = attribute.tr(".", "_").humanize
|
370
448
|
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
|
371
|
-
|
372
|
-
|
449
|
+
|
450
|
+
I18n.t(defaults.shift,
|
451
|
+
default: defaults,
|
373
452
|
attribute: attr_name,
|
374
453
|
message: message)
|
375
454
|
end
|
@@ -400,6 +479,14 @@ module ActiveModel
|
|
400
479
|
# * <tt>errors.messages.blank</tt>
|
401
480
|
def generate_message(attribute, type = :invalid, options = {})
|
402
481
|
type = options.delete(:message) if options[:message].is_a?(Symbol)
|
482
|
+
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
|
483
|
+
|
484
|
+
options = {
|
485
|
+
model: @base.model_name.human,
|
486
|
+
attribute: @base.class.human_attribute_name(attribute),
|
487
|
+
value: value,
|
488
|
+
object: @base
|
489
|
+
}.merge!(options)
|
403
490
|
|
404
491
|
if @base.class.respond_to?(:i18n_scope)
|
405
492
|
i18n_scope = @base.class.i18n_scope.to_s
|
@@ -408,6 +495,11 @@ module ActiveModel
|
|
408
495
|
:"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
|
409
496
|
end
|
410
497
|
defaults << :"#{i18n_scope}.errors.messages.#{type}"
|
498
|
+
|
499
|
+
catch(:exception) do
|
500
|
+
translation = I18n.translate(defaults.first, options.merge(default: defaults.drop(1), throw: true))
|
501
|
+
return translation unless translation.nil?
|
502
|
+
end unless options[:message]
|
411
503
|
else
|
412
504
|
defaults = []
|
413
505
|
end
|
@@ -417,15 +509,7 @@ module ActiveModel
|
|
417
509
|
|
418
510
|
key = defaults.shift
|
419
511
|
defaults = options.delete(:message) if options[:message]
|
420
|
-
|
421
|
-
|
422
|
-
options = {
|
423
|
-
default: defaults,
|
424
|
-
model: @base.model_name.human,
|
425
|
-
attribute: @base.class.human_attribute_name(attribute),
|
426
|
-
value: value,
|
427
|
-
object: @base
|
428
|
-
}.merge!(options)
|
512
|
+
options[:default] = defaults
|
429
513
|
|
430
514
|
I18n.translate(key, options)
|
431
515
|
end
|
data/lib/active_model/naming.rb
CHANGED
@@ -110,6 +110,22 @@ module ActiveModel
|
|
110
110
|
# BlogPost.model_name.eql?('BlogPost') # => true
|
111
111
|
# BlogPost.model_name.eql?('Blog Post') # => false
|
112
112
|
|
113
|
+
##
|
114
|
+
# :method: match?
|
115
|
+
#
|
116
|
+
# :call-seq:
|
117
|
+
# match?(regexp)
|
118
|
+
#
|
119
|
+
# Equivalent to <tt>String#match?</tt>. Match the class name against the
|
120
|
+
# given regexp. Returns +true+ if there is a match, otherwise +false+.
|
121
|
+
#
|
122
|
+
# class BlogPost
|
123
|
+
# extend ActiveModel::Naming
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# BlogPost.model_name.match?(/Post/) # => true
|
127
|
+
# BlogPost.model_name.match?(/\d/) # => false
|
128
|
+
|
113
129
|
##
|
114
130
|
# :method: to_s
|
115
131
|
#
|
@@ -131,7 +147,7 @@ module ActiveModel
|
|
131
147
|
# to_str()
|
132
148
|
#
|
133
149
|
# Equivalent to +to_s+.
|
134
|
-
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
|
150
|
+
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :match?, :to_s,
|
135
151
|
:to_str, :as_json, to: :name
|
136
152
|
|
137
153
|
# Returns a new ActiveModel::Name instance. By default, the +namespace+
|
@@ -193,7 +209,7 @@ module ActiveModel
|
|
193
209
|
private
|
194
210
|
|
195
211
|
def _singularize(string)
|
196
|
-
ActiveSupport::Inflector.underscore(string).tr("/"
|
212
|
+
ActiveSupport::Inflector.underscore(string).tr("/", "_")
|
197
213
|
end
|
198
214
|
end
|
199
215
|
|
@@ -236,7 +252,7 @@ module ActiveModel
|
|
236
252
|
# Person.model_name.plural # => "people"
|
237
253
|
def model_name
|
238
254
|
@_model_name ||= begin
|
239
|
-
namespace =
|
255
|
+
namespace = module_parents.detect do |n|
|
240
256
|
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
|
241
257
|
end
|
242
258
|
ActiveModel::Name.new(self, namespace)
|
data/lib/active_model/railtie.rb
CHANGED
@@ -7,8 +7,14 @@ module ActiveModel
|
|
7
7
|
class Railtie < Rails::Railtie # :nodoc:
|
8
8
|
config.eager_load_namespaces << ActiveModel
|
9
9
|
|
10
|
+
config.active_model = ActiveSupport::OrderedOptions.new
|
11
|
+
|
10
12
|
initializer "active_model.secure_password" do
|
11
13
|
ActiveModel::SecurePassword.min_cost = Rails.env.test?
|
12
14
|
end
|
15
|
+
|
16
|
+
initializer "active_model.i18n_customize_full_message" do
|
17
|
+
ActiveModel::Errors.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
|
18
|
+
end
|
13
19
|
end
|
14
20
|
end
|
@@ -16,15 +16,16 @@ module ActiveModel
|
|
16
16
|
|
17
17
|
module ClassMethods
|
18
18
|
# Adds methods to set and authenticate against a BCrypt password.
|
19
|
-
# This mechanism requires you to have a +
|
19
|
+
# This mechanism requires you to have a +XXX_digest+ attribute.
|
20
|
+
# Where +XXX+ is the attribute name of your desired password.
|
20
21
|
#
|
21
22
|
# The following validations are added automatically:
|
22
23
|
# * Password must be present on creation
|
23
24
|
# * Password length should be less than or equal to 72 bytes
|
24
|
-
# * Confirmation of password (using a +
|
25
|
+
# * Confirmation of password (using a +XXX_confirmation+ attribute)
|
25
26
|
#
|
26
|
-
# If
|
27
|
-
# value for +
|
27
|
+
# If confirmation validation is not needed, simply leave out the
|
28
|
+
# value for +XXX_confirmation+ (i.e. don't provide a form field for
|
28
29
|
# it). When this attribute has a +nil+ value, the validation will not be
|
29
30
|
# triggered.
|
30
31
|
#
|
@@ -37,9 +38,10 @@ module ActiveModel
|
|
37
38
|
#
|
38
39
|
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
|
39
40
|
#
|
40
|
-
# # Schema: User(name:string, password_digest:string)
|
41
|
+
# # Schema: User(name:string, password_digest:string, recovery_password_digest:string)
|
41
42
|
# class User < ActiveRecord::Base
|
42
43
|
# has_secure_password
|
44
|
+
# has_secure_password :recovery_password, validations: false
|
43
45
|
# end
|
44
46
|
#
|
45
47
|
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
|
@@ -48,11 +50,15 @@ module ActiveModel
|
|
48
50
|
# user.save # => false, confirmation doesn't match
|
49
51
|
# user.password_confirmation = 'mUc3m00RsqyRe'
|
50
52
|
# user.save # => true
|
53
|
+
# user.recovery_password = "42password"
|
54
|
+
# user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
|
55
|
+
# user.save # => true
|
51
56
|
# user.authenticate('notright') # => false
|
52
57
|
# user.authenticate('mUc3m00RsqyRe') # => user
|
58
|
+
# user.authenticate_recovery_password('42password') # => user
|
53
59
|
# User.find_by(name: 'david').try(:authenticate, 'notright') # => false
|
54
60
|
# User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
|
55
|
-
def has_secure_password(
|
61
|
+
def has_secure_password(attribute = :password, validations: true)
|
56
62
|
# Load bcrypt gem only when has_secure_password is used.
|
57
63
|
# This is to avoid ActiveModel (and by extension the entire framework)
|
58
64
|
# being dependent on a binary library.
|
@@ -63,9 +69,9 @@ module ActiveModel
|
|
63
69
|
raise
|
64
70
|
end
|
65
71
|
|
66
|
-
include InstanceMethodsOnActivation
|
72
|
+
include InstanceMethodsOnActivation.new(attribute)
|
67
73
|
|
68
|
-
if
|
74
|
+
if validations
|
69
75
|
include ActiveModel::Validations
|
70
76
|
|
71
77
|
# This ensures the model has a password by checking whether the password_digest
|
@@ -73,56 +79,49 @@ module ActiveModel
|
|
73
79
|
# when there is an error, the message is added to the password attribute instead
|
74
80
|
# so that the error message will make sense to the end-user.
|
75
81
|
validate do |record|
|
76
|
-
record.errors.add(
|
82
|
+
record.errors.add(attribute, :blank) unless record.send("#{attribute}_digest").present?
|
77
83
|
end
|
78
84
|
|
79
|
-
validates_length_of
|
80
|
-
validates_confirmation_of
|
85
|
+
validates_length_of attribute, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
|
86
|
+
validates_confirmation_of attribute, allow_blank: true
|
81
87
|
end
|
82
88
|
end
|
83
89
|
end
|
84
90
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
91
|
+
class InstanceMethodsOnActivation < Module
|
92
|
+
def initialize(attribute)
|
93
|
+
attr_reader attribute
|
94
|
+
|
95
|
+
define_method("#{attribute}=") do |unencrypted_password|
|
96
|
+
if unencrypted_password.nil?
|
97
|
+
self.send("#{attribute}_digest=", nil)
|
98
|
+
elsif !unencrypted_password.empty?
|
99
|
+
instance_variable_set("@#{attribute}", unencrypted_password)
|
100
|
+
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
101
|
+
self.send("#{attribute}_digest=", BCrypt::Password.create(unencrypted_password, cost: cost))
|
102
|
+
end
|
103
|
+
end
|
99
104
|
|
100
|
-
|
105
|
+
define_method("#{attribute}_confirmation=") do |unencrypted_password|
|
106
|
+
instance_variable_set("@#{attribute}_confirmation", unencrypted_password)
|
107
|
+
end
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
if unencrypted_password.nil?
|
116
|
-
self.password_digest = nil
|
117
|
-
elsif !unencrypted_password.empty?
|
118
|
-
@password = unencrypted_password
|
119
|
-
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
120
|
-
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
|
109
|
+
# Returns +self+ if the password is correct, otherwise +false+.
|
110
|
+
#
|
111
|
+
# class User < ActiveRecord::Base
|
112
|
+
# has_secure_password validations: false
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
|
116
|
+
# user.save
|
117
|
+
# user.authenticate_password('notright') # => false
|
118
|
+
# user.authenticate_password('mUc3m00RsqyRe') # => user
|
119
|
+
define_method("authenticate_#{attribute}") do |unencrypted_password|
|
120
|
+
attribute_digest = send("#{attribute}_digest")
|
121
|
+
BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
|
121
122
|
end
|
122
|
-
end
|
123
123
|
|
124
|
-
|
125
|
-
@password_confirmation = unencrypted_password
|
124
|
+
alias_method :authenticate, :authenticate_password if attribute == :password
|
126
125
|
end
|
127
126
|
end
|
128
127
|
end
|
@@ -26,13 +26,13 @@ module ActiveModel
|
|
26
26
|
# user = User.find(1)
|
27
27
|
# user.as_json
|
28
28
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
29
|
-
# # "created_at" => "2006
|
29
|
+
# # "created_at" => "2006-08-01T17:27:133.000Z", "awesome" => true}
|
30
30
|
#
|
31
31
|
# ActiveRecord::Base.include_root_in_json = true
|
32
32
|
#
|
33
33
|
# user.as_json
|
34
34
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
35
|
-
# # "created_at" => "2006
|
35
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
36
36
|
#
|
37
37
|
# This behavior can also be achieved by setting the <tt>:root</tt> option
|
38
38
|
# to +true+ as in:
|
@@ -40,7 +40,7 @@ module ActiveModel
|
|
40
40
|
# user = User.find(1)
|
41
41
|
# user.as_json(root: true)
|
42
42
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
43
|
-
# # "created_at" => "2006
|
43
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
44
44
|
#
|
45
45
|
# Without any +options+, the returned Hash will include all the model's
|
46
46
|
# attributes.
|
@@ -48,7 +48,7 @@ module ActiveModel
|
|
48
48
|
# user = User.find(1)
|
49
49
|
# user.as_json
|
50
50
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
51
|
-
# # "created_at" => "2006
|
51
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true}
|
52
52
|
#
|
53
53
|
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
|
54
54
|
# the attributes included, and work similar to the +attributes+ method.
|
@@ -63,14 +63,14 @@ module ActiveModel
|
|
63
63
|
#
|
64
64
|
# user.as_json(methods: :permalink)
|
65
65
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
66
|
-
# # "created_at" => "2006
|
66
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
67
67
|
# # "permalink" => "1-konata-izumi" }
|
68
68
|
#
|
69
69
|
# To include associations use <tt>:include</tt>:
|
70
70
|
#
|
71
71
|
# user.as_json(include: :posts)
|
72
72
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
73
|
-
# # "created_at" => "2006
|
73
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
74
74
|
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
|
75
75
|
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
|
76
76
|
#
|
@@ -81,7 +81,7 @@ module ActiveModel
|
|
81
81
|
# only: :body } },
|
82
82
|
# only: :title } })
|
83
83
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
84
|
-
# # "created_at" => "2006
|
84
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
85
85
|
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
|
86
86
|
# # "title" => "Welcome to the weblog" },
|
87
87
|
# # { "comments" => [ { "body" => "Don't think too hard" } ],
|
@@ -93,11 +93,12 @@ module ActiveModel
|
|
93
93
|
include_root_in_json
|
94
94
|
end
|
95
95
|
|
96
|
+
hash = serializable_hash(options).as_json
|
96
97
|
if root
|
97
98
|
root = model_name.element if root == true
|
98
|
-
{ root =>
|
99
|
+
{ root => hash }
|
99
100
|
else
|
100
|
-
|
101
|
+
hash
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|