activemodel 5.2.3 → 6.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -68
- 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 +4 -4
- 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 +0 -4
- data/lib/active_model/type/date_time.rb +3 -7
- data/lib/active_model/type/float.rb +0 -2
- 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 +17 -4
- 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 +1 -5
- data/lib/active_model/validations.rb +0 -2
- data/lib/active_model/validations/acceptance.rb +4 -8
- 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 +2 -2
- data/lib/active_model/validations/validates.rb +2 -2
- data/lib/active_model/validator.rb +1 -1
- metadata +10 -10
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
|
|