mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
change AMS::JSON.include_root_in_json default value to false
Changes: * Update `include_root_in_json` default value to false for default value to false for `ActiveModel::Serializers::JSON`. * Remove unnecessary change to include_root_in_json option in wrap_parameters template. * Update `as_json` documentation. * Fix JSONSerialization tests. Problem: It's confusing that AM serializers behave differently from AR, even when AR objects include AM serializers module. class User < ActiveRecord::Base; end class Person include ActiveModel::Model include ActiveModel::AttributeMethods include ActiveModel::Serializers::JSON attr_accessor :name, :age def attributes instance_values end end user.as_json => {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true} # root is not included person.as_json => {"person"=>{"name"=>"Francesco", "age"=>22}} # root is included ActiveRecord::Base.include_root_in_json => false Person.include_root_in_json => true # different default values for include_root_in_json Proposal: Change the default value of AM serializers to false, update the misleading documentation and remove unnecessary change to false of include_root_in_json option with AR objects. class User < ActiveRecord::Base; end class Person include ActiveModel::Model include ActiveModel::AttributeMethods include ActiveModel::Serializers::JSON attr_accessor :name, :age def attributes instance_values end end user.as_json => {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true} # root is not included person.as_json => {"name"=>"Francesco", "age"=>22} # root is not included ActiveRecord::Base.include_root_in_json => false Person.include_root_in_json => false # same behaviour, more consistent Fixes #6578.
This commit is contained in:
parent
d10eb69964
commit
ab11a2780f
4 changed files with 115 additions and 98 deletions
|
@ -1,5 +1,32 @@
|
||||||
## Rails 4.0.0 (unreleased) ##
|
## Rails 4.0.0 (unreleased) ##
|
||||||
|
|
||||||
|
* Changed `AM::Serializers::JSON.include_root_in_json' default value to false.
|
||||||
|
Now, AM Serializers and AR objects have the same default behaviour. Fixes #6578.
|
||||||
|
|
||||||
|
class User < ActiveRecord::Base; end
|
||||||
|
|
||||||
|
class Person
|
||||||
|
include ActiveModel::Model
|
||||||
|
include ActiveModel::AttributeMethods
|
||||||
|
include ActiveModel::Serializers::JSON
|
||||||
|
|
||||||
|
attr_accessor :name, :age
|
||||||
|
|
||||||
|
def attributes
|
||||||
|
instance_values
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
user.as_json
|
||||||
|
=> {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true}
|
||||||
|
# root is not included
|
||||||
|
|
||||||
|
person.as_json
|
||||||
|
=> {"name"=>"Francesco", "age"=>22}
|
||||||
|
# root is not included
|
||||||
|
|
||||||
|
*Francesco Rodriguez*
|
||||||
|
|
||||||
* Passing false hash values to `validates` will no longer enable the corresponding validators *Steve Purcell*
|
* Passing false hash values to `validates` will no longer enable the corresponding validators *Steve Purcell*
|
||||||
|
|
||||||
* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute` *Brian Cardarella*
|
* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute` *Brian Cardarella*
|
||||||
|
|
|
@ -13,80 +13,80 @@ module ActiveModel
|
||||||
extend ActiveModel::Configuration
|
extend ActiveModel::Configuration
|
||||||
|
|
||||||
config_attribute :include_root_in_json
|
config_attribute :include_root_in_json
|
||||||
self.include_root_in_json = true
|
self.include_root_in_json = false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a hash representing the model. Some configuration can be
|
# Returns a hash representing the model. Some configuration can be
|
||||||
# passed through +options+.
|
# passed through +options+.
|
||||||
#
|
#
|
||||||
# The option <tt>include_root_in_json</tt> controls the top-level behavior
|
# The option <tt>include_root_in_json</tt> controls the top-level behavior
|
||||||
# of +as_json+. If true (the default) +as_json+ will emit a single root
|
# of +as_json+. If true +as_json+ will emit a single root node named after
|
||||||
# node named after the object's type. For example:
|
# the object's type. The default value for <tt>include_root_in_json</tt>
|
||||||
|
# option is +false+.
|
||||||
#
|
#
|
||||||
# user = User.find(1)
|
# user = User.find(1)
|
||||||
# user.as_json
|
# user.as_json
|
||||||
# # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true} }
|
# # "created_at" => "2006/08/01", "awesome" => true}
|
||||||
|
#
|
||||||
|
# ActiveRecord::Base.include_root_in_json = true
|
||||||
#
|
#
|
||||||
# ActiveRecord::Base.include_root_in_json = false
|
|
||||||
# user.as_json
|
# user.as_json
|
||||||
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true}
|
# # "created_at" => "2006/08/01", "awesome" => true } }
|
||||||
#
|
#
|
||||||
# This behavior can also be achieved by setting the <tt>:root</tt> option to +false+ as in:
|
# This behavior can also be achieved by setting the <tt>:root</tt> option
|
||||||
|
# to +true+ as in:
|
||||||
#
|
#
|
||||||
# user = User.find(1)
|
# user = User.find(1)
|
||||||
# user.as_json(root: false)
|
# user.as_json(root: true)
|
||||||
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true}
|
# # "created_at" => "2006/08/01", "awesome" => true } }
|
||||||
#
|
|
||||||
# The remainder of the examples in this section assume include_root_in_json is set to
|
|
||||||
# <tt>false</tt>.
|
|
||||||
#
|
#
|
||||||
# Without any +options+, the returned Hash will include all the model's
|
# Without any +options+, the returned Hash will include all the model's
|
||||||
# attributes. For example:
|
# attributes.
|
||||||
#
|
#
|
||||||
# user = User.find(1)
|
# user = User.find(1)
|
||||||
# user.as_json
|
# user.as_json
|
||||||
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true}
|
# # "created_at" => "2006/08/01", "awesome" => true}
|
||||||
#
|
#
|
||||||
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
|
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
|
||||||
# included, and work similar to the +attributes+ method. For example:
|
# the attributes included, and work similar to the +attributes+ method.
|
||||||
#
|
#
|
||||||
# user.as_json(:only => [ :id, :name ])
|
# user.as_json(only: [:id, :name])
|
||||||
# # => {"id": 1, "name": "Konata Izumi"}
|
# # => { "id" => 1, "name" => "Konata Izumi" }
|
||||||
#
|
#
|
||||||
# user.as_json(:except => [ :id, :created_at, :age ])
|
# user.as_json(except: [:id, :created_at, :age])
|
||||||
# # => {"name": "Konata Izumi", "awesome": true}
|
# # => { "name" => "Konata Izumi", "awesome" => true }
|
||||||
#
|
#
|
||||||
# To include the result of some method calls on the model use <tt>:methods</tt>:
|
# To include the result of some method calls on the model use <tt>:methods</tt>:
|
||||||
#
|
#
|
||||||
# user.as_json(:methods => :permalink)
|
# user.as_json(methods: :permalink)
|
||||||
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true,
|
# # "created_at" => "2006/08/01", "awesome" => true,
|
||||||
# "permalink": "1-konata-izumi"}
|
# # "permalink" => "1-konata-izumi" }
|
||||||
#
|
#
|
||||||
# To include associations use <tt>:include</tt>:
|
# To include associations use <tt>:include</tt>:
|
||||||
#
|
#
|
||||||
# user.as_json(:include => :posts)
|
# user.as_json(include: :posts)
|
||||||
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true,
|
# # "created_at" => "2006/08/01", "awesome" => true,
|
||||||
# "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
|
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
|
||||||
# {"id": 2, author_id: 1, "title": "So I was thinking"}]}
|
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
|
||||||
#
|
#
|
||||||
# Second level and higher order associations work as well:
|
# Second level and higher order associations work as well:
|
||||||
#
|
#
|
||||||
# user.as_json(:include => { :posts => {
|
# user.as_json(include: { posts: {
|
||||||
# :include => { :comments => {
|
# include: { comments: {
|
||||||
# :only => :body } },
|
# only: :body } },
|
||||||
# :only => :title } })
|
# only: :title } })
|
||||||
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
||||||
# "created_at": "2006/08/01", "awesome": true,
|
# # "created_at" => "2006/08/01", "awesome" => true,
|
||||||
# "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
|
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
|
||||||
# "title": "Welcome to the weblog"},
|
# # "title" => "Welcome to the weblog" },
|
||||||
# {"comments": [{"body": "Don't think too hard"}],
|
# # { "comments" => [ { "body" => "Don't think too hard" } ],
|
||||||
# "title": "So I was thinking"}]}
|
# # "title" => "So I was thinking" } ] }
|
||||||
def as_json(options = nil)
|
def as_json(options = nil)
|
||||||
root = include_root_in_json
|
root = include_root_in_json
|
||||||
root = options[:root] if options.try(:key?, :root)
|
root = options[:root] if options.try(:key?, :root)
|
||||||
|
@ -106,4 +106,4 @@ module ActiveModel
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -31,7 +31,24 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
@contact.preferences = { 'shows' => 'anime' }
|
@contact.preferences = { 'shows' => 'anime' }
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should include root in json" do
|
def teardown
|
||||||
|
# set to the default value
|
||||||
|
Contact.include_root_in_json = false
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should not include root in json (class method)" do
|
||||||
|
json = @contact.to_json
|
||||||
|
|
||||||
|
assert_no_match %r{^\{"contact":\{}, json
|
||||||
|
assert_match %r{"name":"Konata Izumi"}, json
|
||||||
|
assert_match %r{"age":16}, json
|
||||||
|
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||||
|
assert_match %r{"awesome":true}, json
|
||||||
|
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should include root in json if include_root_in_json is true" do
|
||||||
|
Contact.include_root_in_json = true
|
||||||
json = @contact.to_json
|
json = @contact.to_json
|
||||||
|
|
||||||
assert_match %r{^\{"contact":\{}, json
|
assert_match %r{^\{"contact":\{}, json
|
||||||
|
@ -42,41 +59,19 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should not include root in json (class method)" do
|
|
||||||
begin
|
|
||||||
Contact.include_root_in_json = false
|
|
||||||
json = @contact.to_json
|
|
||||||
|
|
||||||
assert_no_match %r{^\{"contact":\{}, json
|
|
||||||
assert_match %r{"name":"Konata Izumi"}, json
|
|
||||||
assert_match %r{"age":16}, json
|
|
||||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
|
||||||
assert_match %r{"awesome":true}, json
|
|
||||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
|
||||||
ensure
|
|
||||||
Contact.include_root_in_json = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should include root in json (option) even if the default is set to false" do
|
test "should include root in json (option) even if the default is set to false" do
|
||||||
begin
|
json = @contact.to_json(root: true)
|
||||||
Contact.include_root_in_json = false
|
assert_match %r{^\{"contact":\{}, json
|
||||||
json = @contact.to_json(:root => true)
|
|
||||||
assert_match %r{^\{"contact":\{}, json
|
|
||||||
ensure
|
|
||||||
Contact.include_root_in_json = true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should not include root in json (option)" do
|
test "should not include root in json (option)" do
|
||||||
|
json = @contact.to_json(root: false)
|
||||||
json = @contact.to_json(:root => false)
|
|
||||||
|
|
||||||
assert_no_match %r{^\{"contact":\{}, json
|
assert_no_match %r{^\{"contact":\{}, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should include custom root in json" do
|
test "should include custom root in json" do
|
||||||
json = @contact.to_json(:root => 'json_contact')
|
json = @contact.to_json(root: 'json_contact')
|
||||||
|
|
||||||
assert_match %r{^\{"json_contact":\{}, json
|
assert_match %r{^\{"json_contact":\{}, json
|
||||||
assert_match %r{"name":"Konata Izumi"}, json
|
assert_match %r{"name":"Konata Izumi"}, json
|
||||||
|
@ -107,7 +102,7 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should allow attribute filtering with except" do
|
test "should allow attribute filtering with except" do
|
||||||
json = @contact.to_json(:except => [:name, :age])
|
json = @contact.to_json(except: [:name, :age])
|
||||||
|
|
||||||
assert_no_match %r{"name":"Konata Izumi"}, json
|
assert_no_match %r{"name":"Konata Izumi"}, json
|
||||||
assert_no_match %r{"age":16}, json
|
assert_no_match %r{"age":16}, json
|
||||||
|
@ -122,10 +117,10 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
def @contact.favorite_quote; "Constraints are liberating"; end
|
def @contact.favorite_quote; "Constraints are liberating"; end
|
||||||
|
|
||||||
# Single method.
|
# Single method.
|
||||||
assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
|
assert_match %r{"label":"Has cheezburger"}, @contact.to_json(only: :name, methods: :label)
|
||||||
|
|
||||||
# Both methods.
|
# Both methods.
|
||||||
methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
|
methods_json = @contact.to_json(only: :name, methods: [:label, :favorite_quote])
|
||||||
assert_match %r{"label":"Has cheezburger"}, methods_json
|
assert_match %r{"label":"Has cheezburger"}, methods_json
|
||||||
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
|
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
|
||||||
end
|
end
|
||||||
|
@ -143,14 +138,15 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "serializable_hash should not modify options passed in argument" do
|
test "serializable_hash should not modify options passed in argument" do
|
||||||
options = { :except => :name }
|
options = { except: :name }
|
||||||
@contact.serializable_hash(options)
|
@contact.serializable_hash(options)
|
||||||
|
|
||||||
assert_nil options[:only]
|
assert_nil options[:only]
|
||||||
assert_equal :name, options[:except]
|
assert_equal :name, options[:except]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "as_json should return a hash" do
|
test "as_json should return a hash if include_root_in_json is true" do
|
||||||
|
Contact.include_root_in_json = true
|
||||||
json = @contact.as_json
|
json = @contact.as_json
|
||||||
|
|
||||||
assert_kind_of Hash, json
|
assert_kind_of Hash, json
|
||||||
|
@ -160,7 +156,7 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "from_json should set the object's attributes" do
|
test "from_json should work without a root (class attribute)" do
|
||||||
json = @contact.to_json
|
json = @contact.to_json
|
||||||
result = Contact.new.from_json(json)
|
result = Contact.new.from_json(json)
|
||||||
|
|
||||||
|
@ -172,7 +168,7 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "from_json should work without a root (method parameter)" do
|
test "from_json should work without a root (method parameter)" do
|
||||||
json = @contact.to_json(:root => false)
|
json = @contact.to_json
|
||||||
result = Contact.new.from_json(json, false)
|
result = Contact.new.from_json(json, false)
|
||||||
|
|
||||||
assert_equal result.name, @contact.name
|
assert_equal result.name, @contact.name
|
||||||
|
@ -182,24 +178,19 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
assert_equal result.preferences, @contact.preferences
|
assert_equal result.preferences, @contact.preferences
|
||||||
end
|
end
|
||||||
|
|
||||||
test "from_json should work without a root (class attribute)" do
|
test "from_json should work with a root (method parameter)" do
|
||||||
begin
|
json = @contact.to_json(root: :true)
|
||||||
Contact.include_root_in_json = false
|
result = Contact.new.from_json(json, true)
|
||||||
json = @contact.to_json
|
|
||||||
result = Contact.new.from_json(json)
|
|
||||||
|
|
||||||
assert_equal result.name, @contact.name
|
assert_equal result.name, @contact.name
|
||||||
assert_equal result.age, @contact.age
|
assert_equal result.age, @contact.age
|
||||||
assert_equal Time.parse(result.created_at), @contact.created_at
|
assert_equal Time.parse(result.created_at), @contact.created_at
|
||||||
assert_equal result.awesome, @contact.awesome
|
assert_equal result.awesome, @contact.awesome
|
||||||
assert_equal result.preferences, @contact.preferences
|
assert_equal result.preferences, @contact.preferences
|
||||||
ensure
|
|
||||||
Contact.include_root_in_json = true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "custom as_json should be honored when generating json" do
|
test "custom as_json should be honored when generating json" do
|
||||||
def @contact.as_json(options); { :name => name, :created_at => created_at }; end
|
def @contact.as_json(options); { name: name, created_at: created_at }; end
|
||||||
json = @contact.to_json
|
json = @contact.to_json
|
||||||
|
|
||||||
assert_match %r{"name":"Konata Izumi"}, json
|
assert_match %r{"name":"Konata Izumi"}, json
|
||||||
|
@ -209,7 +200,7 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "custom as_json options should be extendible" do
|
test "custom as_json options should be extendible" do
|
||||||
def @contact.as_json(options = {}); super(options.merge(:only => [:name])); end
|
def @contact.as_json(options = {}); super(options.merge(only: [:name])); end
|
||||||
json = @contact.to_json
|
json = @contact.to_json
|
||||||
|
|
||||||
assert_match %r{"name":"Konata Izumi"}, json
|
assert_match %r{"name":"Konata Izumi"}, json
|
||||||
|
@ -217,5 +208,4 @@ class JsonSerializationTest < ActiveModel::TestCase
|
||||||
assert_no_match %r{"awesome":}, json
|
assert_no_match %r{"awesome":}, json
|
||||||
assert_no_match %r{"preferences":}, json
|
assert_no_match %r{"preferences":}, json
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,8 +9,8 @@ ActiveSupport.on_load(:action_controller) do
|
||||||
end
|
end
|
||||||
|
|
||||||
<%- unless options.skip_active_record? -%>
|
<%- unless options.skip_active_record? -%>
|
||||||
# Disable root element in JSON by default.
|
# To enable root element in JSON for ActiveRecord objects.
|
||||||
ActiveSupport.on_load(:active_record) do
|
# ActiveSupport.on_load(:active_record) do
|
||||||
self.include_root_in_json = false
|
# self.include_root_in_json = true
|
||||||
end
|
# end
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
|
|
Loading…
Reference in a new issue