activemodel 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +172 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +266 -0
- data/lib/active_model.rb +77 -0
- data/lib/active_model/attribute.rb +247 -0
- data/lib/active_model/attribute/user_provided_default.rb +51 -0
- data/lib/active_model/attribute_assignment.rb +57 -0
- data/lib/active_model/attribute_methods.rb +517 -0
- data/lib/active_model/attribute_mutation_tracker.rb +178 -0
- data/lib/active_model/attribute_set.rb +106 -0
- data/lib/active_model/attribute_set/builder.rb +124 -0
- data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
- data/lib/active_model/attributes.rb +138 -0
- data/lib/active_model/callbacks.rb +156 -0
- data/lib/active_model/conversion.rb +111 -0
- data/lib/active_model/dirty.rb +280 -0
- data/lib/active_model/errors.rb +601 -0
- data/lib/active_model/forbidden_attributes_protection.rb +31 -0
- data/lib/active_model/gem_version.rb +17 -0
- data/lib/active_model/lint.rb +118 -0
- data/lib/active_model/locale/en.yml +36 -0
- data/lib/active_model/model.rb +99 -0
- data/lib/active_model/naming.rb +334 -0
- data/lib/active_model/railtie.rb +20 -0
- data/lib/active_model/secure_password.rb +128 -0
- data/lib/active_model/serialization.rb +192 -0
- data/lib/active_model/serializers/json.rb +147 -0
- data/lib/active_model/translation.rb +70 -0
- data/lib/active_model/type.rb +53 -0
- data/lib/active_model/type/big_integer.rb +15 -0
- data/lib/active_model/type/binary.rb +52 -0
- data/lib/active_model/type/boolean.rb +47 -0
- data/lib/active_model/type/date.rb +53 -0
- data/lib/active_model/type/date_time.rb +47 -0
- data/lib/active_model/type/decimal.rb +70 -0
- data/lib/active_model/type/float.rb +34 -0
- data/lib/active_model/type/helpers.rb +7 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +45 -0
- data/lib/active_model/type/helpers/mutable.rb +20 -0
- data/lib/active_model/type/helpers/numeric.rb +44 -0
- data/lib/active_model/type/helpers/time_value.rb +81 -0
- data/lib/active_model/type/helpers/timezone.rb +19 -0
- data/lib/active_model/type/immutable_string.rb +32 -0
- data/lib/active_model/type/integer.rb +58 -0
- data/lib/active_model/type/registry.rb +62 -0
- data/lib/active_model/type/string.rb +26 -0
- data/lib/active_model/type/time.rb +47 -0
- data/lib/active_model/type/value.rb +126 -0
- data/lib/active_model/validations.rb +437 -0
- data/lib/active_model/validations/absence.rb +33 -0
- data/lib/active_model/validations/acceptance.rb +102 -0
- data/lib/active_model/validations/callbacks.rb +122 -0
- data/lib/active_model/validations/clusivity.rb +54 -0
- data/lib/active_model/validations/confirmation.rb +80 -0
- data/lib/active_model/validations/exclusion.rb +49 -0
- data/lib/active_model/validations/format.rb +114 -0
- data/lib/active_model/validations/helper_methods.rb +15 -0
- data/lib/active_model/validations/inclusion.rb +47 -0
- data/lib/active_model/validations/length.rb +129 -0
- data/lib/active_model/validations/numericality.rb +189 -0
- data/lib/active_model/validations/presence.rb +39 -0
- data/lib/active_model/validations/validates.rb +174 -0
- data/lib/active_model/validations/with.rb +147 -0
- data/lib/active_model/validator.rb +183 -0
- data/lib/active_model/version.rb +10 -0
- metadata +125 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model"
|
4
|
+
require "rails"
|
5
|
+
|
6
|
+
module ActiveModel
|
7
|
+
class Railtie < Rails::Railtie # :nodoc:
|
8
|
+
config.eager_load_namespaces << ActiveModel
|
9
|
+
|
10
|
+
config.active_model = ActiveSupport::OrderedOptions.new
|
11
|
+
|
12
|
+
initializer "active_model.secure_password" do
|
13
|
+
ActiveModel::SecurePassword.min_cost = Rails.env.test?
|
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
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module SecurePassword
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
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.
|
9
|
+
# Hence need to put a restriction on password length.
|
10
|
+
MAX_PASSWORD_LENGTH_ALLOWED = 72
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :min_cost # :nodoc:
|
14
|
+
end
|
15
|
+
self.min_cost = false
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Adds methods to set and authenticate against a BCrypt password.
|
19
|
+
# This mechanism requires you to have a +XXX_digest+ attribute.
|
20
|
+
# Where +XXX+ is the attribute name of your desired password.
|
21
|
+
#
|
22
|
+
# The following validations are added automatically:
|
23
|
+
# * Password must be present on creation
|
24
|
+
# * Password length should be less than or equal to 72 bytes
|
25
|
+
# * Confirmation of password (using a +XXX_confirmation+ attribute)
|
26
|
+
#
|
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
|
29
|
+
# it). When this attribute has a +nil+ value, the validation will not be
|
30
|
+
# triggered.
|
31
|
+
#
|
32
|
+
# For further customizability, it is possible to suppress the default
|
33
|
+
# validations by passing <tt>validations: false</tt> as an argument.
|
34
|
+
#
|
35
|
+
# Add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password:
|
36
|
+
#
|
37
|
+
# gem 'bcrypt', '~> 3.1.7'
|
38
|
+
#
|
39
|
+
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
|
40
|
+
#
|
41
|
+
# # Schema: User(name:string, password_digest:string, recovery_password_digest:string)
|
42
|
+
# class User < ActiveRecord::Base
|
43
|
+
# has_secure_password
|
44
|
+
# has_secure_password :recovery_password, validations: false
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
|
48
|
+
# user.save # => false, password required
|
49
|
+
# user.password = 'mUc3m00RsqyRe'
|
50
|
+
# user.save # => false, confirmation doesn't match
|
51
|
+
# user.password_confirmation = 'mUc3m00RsqyRe'
|
52
|
+
# user.save # => true
|
53
|
+
# user.recovery_password = "42password"
|
54
|
+
# user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
|
55
|
+
# user.save # => true
|
56
|
+
# user.authenticate('notright') # => false
|
57
|
+
# user.authenticate('mUc3m00RsqyRe') # => user
|
58
|
+
# user.authenticate_recovery_password('42password') # => user
|
59
|
+
# User.find_by(name: 'david').try(:authenticate, 'notright') # => false
|
60
|
+
# User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
|
61
|
+
def has_secure_password(attribute = :password, validations: true)
|
62
|
+
# Load bcrypt gem only when has_secure_password is used.
|
63
|
+
# This is to avoid ActiveModel (and by extension the entire framework)
|
64
|
+
# being dependent on a binary library.
|
65
|
+
begin
|
66
|
+
require "bcrypt"
|
67
|
+
rescue LoadError
|
68
|
+
$stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install"
|
69
|
+
raise
|
70
|
+
end
|
71
|
+
|
72
|
+
include InstanceMethodsOnActivation.new(attribute)
|
73
|
+
|
74
|
+
if validations
|
75
|
+
include ActiveModel::Validations
|
76
|
+
|
77
|
+
# This ensures the model has a password by checking whether the password_digest
|
78
|
+
# is present, so that this works with both new and existing records. However,
|
79
|
+
# when there is an error, the message is added to the password attribute instead
|
80
|
+
# so that the error message will make sense to the end-user.
|
81
|
+
validate do |record|
|
82
|
+
record.errors.add(attribute, :blank) unless record.send("#{attribute}_digest").present?
|
83
|
+
end
|
84
|
+
|
85
|
+
validates_length_of attribute, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
|
86
|
+
validates_confirmation_of attribute, allow_blank: true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
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
|
104
|
+
|
105
|
+
define_method("#{attribute}_confirmation=") do |unencrypted_password|
|
106
|
+
instance_variable_set("@#{attribute}_confirmation", unencrypted_password)
|
107
|
+
end
|
108
|
+
|
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
|
122
|
+
end
|
123
|
+
|
124
|
+
alias_method :authenticate, :authenticate_password if attribute == :password
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/except"
|
4
|
+
require "active_support/core_ext/hash/slice"
|
5
|
+
|
6
|
+
module ActiveModel
|
7
|
+
# == Active \Model \Serialization
|
8
|
+
#
|
9
|
+
# Provides a basic serialization to a serializable_hash for your objects.
|
10
|
+
#
|
11
|
+
# A minimal implementation could be:
|
12
|
+
#
|
13
|
+
# class Person
|
14
|
+
# include ActiveModel::Serialization
|
15
|
+
#
|
16
|
+
# attr_accessor :name
|
17
|
+
#
|
18
|
+
# def attributes
|
19
|
+
# {'name' => nil}
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Which would provide you with:
|
24
|
+
#
|
25
|
+
# person = Person.new
|
26
|
+
# person.serializable_hash # => {"name"=>nil}
|
27
|
+
# person.name = "Bob"
|
28
|
+
# person.serializable_hash # => {"name"=>"Bob"}
|
29
|
+
#
|
30
|
+
# An +attributes+ hash must be defined and should contain any attributes you
|
31
|
+
# need to be serialized. Attributes must be strings, not symbols.
|
32
|
+
# When called, serializable hash will use instance methods that match the name
|
33
|
+
# of the attributes hash's keys. In order to override this behavior, take a look
|
34
|
+
# at the private method +read_attribute_for_serialization+.
|
35
|
+
#
|
36
|
+
# ActiveModel::Serializers::JSON module automatically includes
|
37
|
+
# the <tt>ActiveModel::Serialization</tt> module, so there is no need to
|
38
|
+
# explicitly include <tt>ActiveModel::Serialization</tt>.
|
39
|
+
#
|
40
|
+
# A minimal implementation including JSON would be:
|
41
|
+
#
|
42
|
+
# class Person
|
43
|
+
# include ActiveModel::Serializers::JSON
|
44
|
+
#
|
45
|
+
# attr_accessor :name
|
46
|
+
#
|
47
|
+
# def attributes
|
48
|
+
# {'name' => nil}
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Which would provide you with:
|
53
|
+
#
|
54
|
+
# person = Person.new
|
55
|
+
# person.serializable_hash # => {"name"=>nil}
|
56
|
+
# person.as_json # => {"name"=>nil}
|
57
|
+
# person.to_json # => "{\"name\":null}"
|
58
|
+
#
|
59
|
+
# person.name = "Bob"
|
60
|
+
# person.serializable_hash # => {"name"=>"Bob"}
|
61
|
+
# person.as_json # => {"name"=>"Bob"}
|
62
|
+
# person.to_json # => "{\"name\":\"Bob\"}"
|
63
|
+
#
|
64
|
+
# Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and
|
65
|
+
# <tt>:include</tt>. The following are all valid examples:
|
66
|
+
#
|
67
|
+
# person.serializable_hash(only: 'name')
|
68
|
+
# person.serializable_hash(include: :address)
|
69
|
+
# person.serializable_hash(include: { address: { only: 'city' }})
|
70
|
+
module Serialization
|
71
|
+
# Returns a serialized hash of your object.
|
72
|
+
#
|
73
|
+
# class Person
|
74
|
+
# include ActiveModel::Serialization
|
75
|
+
#
|
76
|
+
# attr_accessor :name, :age
|
77
|
+
#
|
78
|
+
# def attributes
|
79
|
+
# {'name' => nil, 'age' => nil}
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# def capitalized_name
|
83
|
+
# name.capitalize
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# person = Person.new
|
88
|
+
# person.name = 'bob'
|
89
|
+
# person.age = 22
|
90
|
+
# person.serializable_hash # => {"name"=>"bob", "age"=>22}
|
91
|
+
# person.serializable_hash(only: :name) # => {"name"=>"bob"}
|
92
|
+
# person.serializable_hash(except: :name) # => {"age"=>22}
|
93
|
+
# person.serializable_hash(methods: :capitalized_name)
|
94
|
+
# # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
|
95
|
+
#
|
96
|
+
# Example with <tt>:include</tt> option
|
97
|
+
#
|
98
|
+
# class User
|
99
|
+
# include ActiveModel::Serializers::JSON
|
100
|
+
# attr_accessor :name, :notes # Emulate has_many :notes
|
101
|
+
# def attributes
|
102
|
+
# {'name' => nil}
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# class Note
|
107
|
+
# include ActiveModel::Serializers::JSON
|
108
|
+
# attr_accessor :title, :text
|
109
|
+
# def attributes
|
110
|
+
# {'title' => nil, 'text' => nil}
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# note = Note.new
|
115
|
+
# note.title = 'Battle of Austerlitz'
|
116
|
+
# note.text = 'Some text here'
|
117
|
+
#
|
118
|
+
# user = User.new
|
119
|
+
# user.name = 'Napoleon'
|
120
|
+
# user.notes = [note]
|
121
|
+
#
|
122
|
+
# user.serializable_hash
|
123
|
+
# # => {"name" => "Napoleon"}
|
124
|
+
# user.serializable_hash(include: { notes: { only: 'title' }})
|
125
|
+
# # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
|
126
|
+
def serializable_hash(options = nil)
|
127
|
+
options ||= {}
|
128
|
+
|
129
|
+
attribute_names = attributes.keys
|
130
|
+
if only = options[:only]
|
131
|
+
attribute_names &= Array(only).map(&:to_s)
|
132
|
+
elsif except = options[:except]
|
133
|
+
attribute_names -= Array(except).map(&:to_s)
|
134
|
+
end
|
135
|
+
|
136
|
+
hash = {}
|
137
|
+
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
|
138
|
+
|
139
|
+
Array(options[:methods]).each { |m| hash[m.to_s] = send(m) }
|
140
|
+
|
141
|
+
serializable_add_includes(options) do |association, records, opts|
|
142
|
+
hash[association.to_s] = if records.respond_to?(:to_ary)
|
143
|
+
records.to_ary.map { |a| a.serializable_hash(opts) }
|
144
|
+
else
|
145
|
+
records.serializable_hash(opts)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
hash
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Hook method defining how an attribute value should be retrieved for
|
155
|
+
# serialization. By default this is assumed to be an instance named after
|
156
|
+
# the attribute. Override this method in subclasses should you need to
|
157
|
+
# retrieve the value for a given attribute differently:
|
158
|
+
#
|
159
|
+
# class MyClass
|
160
|
+
# include ActiveModel::Serialization
|
161
|
+
#
|
162
|
+
# def initialize(data = {})
|
163
|
+
# @data = data
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# def read_attribute_for_serialization(key)
|
167
|
+
# @data[key]
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
alias :read_attribute_for_serialization :send
|
171
|
+
|
172
|
+
# Add associations specified via the <tt>:include</tt> option.
|
173
|
+
#
|
174
|
+
# Expects a block that takes as arguments:
|
175
|
+
# +association+ - name of the association
|
176
|
+
# +records+ - the association record(s) to be serialized
|
177
|
+
# +opts+ - options for the association records
|
178
|
+
def serializable_add_includes(options = {}) #:nodoc:
|
179
|
+
return unless includes = options[:include]
|
180
|
+
|
181
|
+
unless includes.is_a?(Hash)
|
182
|
+
includes = Hash[Array(includes).flat_map { |n| n.is_a?(Hash) ? n.to_a : [[n, {}]] }]
|
183
|
+
end
|
184
|
+
|
185
|
+
includes.each do |association, opts|
|
186
|
+
if records = send(association)
|
187
|
+
yield association, records, opts
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/json"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module Serializers
|
7
|
+
# == Active \Model \JSON \Serializer
|
8
|
+
module JSON
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
include ActiveModel::Serialization
|
11
|
+
|
12
|
+
included do
|
13
|
+
extend ActiveModel::Naming
|
14
|
+
|
15
|
+
class_attribute :include_root_in_json, instance_writer: false, default: false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a hash representing the model. Some configuration can be
|
19
|
+
# passed through +options+.
|
20
|
+
#
|
21
|
+
# The option <tt>include_root_in_json</tt> controls the top-level behavior
|
22
|
+
# of +as_json+. If +true+, +as_json+ will emit a single root node named
|
23
|
+
# after the object's type. The default value for <tt>include_root_in_json</tt>
|
24
|
+
# option is +false+.
|
25
|
+
#
|
26
|
+
# user = User.find(1)
|
27
|
+
# user.as_json
|
28
|
+
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
29
|
+
# # "created_at" => "2006-08-01T17:27:133.000Z", "awesome" => true}
|
30
|
+
#
|
31
|
+
# ActiveRecord::Base.include_root_in_json = true
|
32
|
+
#
|
33
|
+
# user.as_json
|
34
|
+
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
35
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
36
|
+
#
|
37
|
+
# This behavior can also be achieved by setting the <tt>:root</tt> option
|
38
|
+
# to +true+ as in:
|
39
|
+
#
|
40
|
+
# user = User.find(1)
|
41
|
+
# user.as_json(root: true)
|
42
|
+
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
43
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
44
|
+
#
|
45
|
+
# Without any +options+, the returned Hash will include all the model's
|
46
|
+
# attributes.
|
47
|
+
#
|
48
|
+
# user = User.find(1)
|
49
|
+
# user.as_json
|
50
|
+
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
51
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true}
|
52
|
+
#
|
53
|
+
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
|
54
|
+
# the attributes included, and work similar to the +attributes+ method.
|
55
|
+
#
|
56
|
+
# user.as_json(only: [:id, :name])
|
57
|
+
# # => { "id" => 1, "name" => "Konata Izumi" }
|
58
|
+
#
|
59
|
+
# user.as_json(except: [:id, :created_at, :age])
|
60
|
+
# # => { "name" => "Konata Izumi", "awesome" => true }
|
61
|
+
#
|
62
|
+
# To include the result of some method calls on the model use <tt>:methods</tt>:
|
63
|
+
#
|
64
|
+
# user.as_json(methods: :permalink)
|
65
|
+
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
66
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
67
|
+
# # "permalink" => "1-konata-izumi" }
|
68
|
+
#
|
69
|
+
# To include associations use <tt>:include</tt>:
|
70
|
+
#
|
71
|
+
# user.as_json(include: :posts)
|
72
|
+
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
73
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
74
|
+
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
|
75
|
+
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
|
76
|
+
#
|
77
|
+
# Second level and higher order associations work as well:
|
78
|
+
#
|
79
|
+
# user.as_json(include: { posts: {
|
80
|
+
# include: { comments: {
|
81
|
+
# only: :body } },
|
82
|
+
# only: :title } })
|
83
|
+
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
84
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
85
|
+
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
|
86
|
+
# # "title" => "Welcome to the weblog" },
|
87
|
+
# # { "comments" => [ { "body" => "Don't think too hard" } ],
|
88
|
+
# # "title" => "So I was thinking" } ] }
|
89
|
+
def as_json(options = nil)
|
90
|
+
root = if options && options.key?(:root)
|
91
|
+
options[:root]
|
92
|
+
else
|
93
|
+
include_root_in_json
|
94
|
+
end
|
95
|
+
|
96
|
+
hash = serializable_hash(options).as_json
|
97
|
+
if root
|
98
|
+
root = model_name.element if root == true
|
99
|
+
{ root => hash }
|
100
|
+
else
|
101
|
+
hash
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Sets the model +attributes+ from a JSON string. Returns +self+.
|
106
|
+
#
|
107
|
+
# class Person
|
108
|
+
# include ActiveModel::Serializers::JSON
|
109
|
+
#
|
110
|
+
# attr_accessor :name, :age, :awesome
|
111
|
+
#
|
112
|
+
# def attributes=(hash)
|
113
|
+
# hash.each do |key, value|
|
114
|
+
# send("#{key}=", value)
|
115
|
+
# end
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# def attributes
|
119
|
+
# instance_values
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# json = { name: 'bob', age: 22, awesome:true }.to_json
|
124
|
+
# person = Person.new
|
125
|
+
# person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
|
126
|
+
# person.name # => "bob"
|
127
|
+
# person.age # => 22
|
128
|
+
# person.awesome # => true
|
129
|
+
#
|
130
|
+
# The default value for +include_root+ is +false+. You can change it to
|
131
|
+
# +true+ if the given JSON string includes a single root node.
|
132
|
+
#
|
133
|
+
# json = { person: { name: 'bob', age: 22, awesome:true } }.to_json
|
134
|
+
# person = Person.new
|
135
|
+
# person.from_json(json, true) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
|
136
|
+
# person.name # => "bob"
|
137
|
+
# person.age # => 22
|
138
|
+
# person.awesome # => true
|
139
|
+
def from_json(json, include_root = include_root_in_json)
|
140
|
+
hash = ActiveSupport::JSON.decode(json)
|
141
|
+
hash = hash.values.first if include_root
|
142
|
+
self.attributes = hash
|
143
|
+
self
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|