Improve serializers:

* Many nested levels of associations can now insert
  their information alongside the original when
  embed :ids, :include => true is true
* Add support for passing options to serializers
* Fix Array serializers so they don't try to insert
  roots for each child object and so they can be
  provided a root.
* TODO: Array serializers should require a root
* TODO: Make merging associations at the root more
  efficient if possible
This commit is contained in:
Yehuda Katz
2011-12-20 00:03:29 -08:00
parent 8bdb7da272
commit df9ad0ef11
5 changed files with 184 additions and 23 deletions

View File

@@ -15,12 +15,14 @@ class RenderJsonTest < ActionController::TestCase
end
class JsonSerializer
def initialize(object, scope)
@object, @scope = object, scope
def initialize(object, scope, options={})
@object, @scope, @options = object, scope, options
end
def as_json(*)
{ :object => @object.as_json, :scope => @scope.as_json }
hash = { :object => @object.as_json, :scope => @scope.as_json }
hash.merge!(:options => true) if @options[:options]
hash
end
end
@@ -89,6 +91,11 @@ class RenderJsonTest < ActionController::TestCase
render :json => JsonSerializable.new
end
def render_json_with_serializer_and_options
@current_user = Struct.new(:as_json).new(:current_user => true)
render :json => JsonSerializable.new, :options => true
end
def render_json_with_serializer_api_but_without_serializer
@current_user = Struct.new(:as_json).new(:current_user => true)
render :json => JsonSerializable.new(true)
@@ -165,6 +172,13 @@ class RenderJsonTest < ActionController::TestCase
assert_match '"object":{"serializable_object":true}', @response.body
end
def test_render_json_with_serializer_and_options
get :render_json_with_serializer_and_options
assert_match '"scope":{"current_user":true}', @response.body
assert_match '"object":{"serializable_object":true}', @response.body
assert_match '"options":true', @response.body
end
def test_render_json_with_serializer_api_but_without_serializer
get :render_json_with_serializer_api_but_without_serializer
assert_match '{"serializable_object":true}', @response.body

View File

@@ -70,7 +70,7 @@ class SerializerTest < ActiveModel::TestCase
end
class CommentSerializer
def initialize(comment, scope)
def initialize(comment, scope, options={})
@comment, @scope = comment, scope
end
@@ -680,4 +680,133 @@ class SerializerTest < ActiveModel::TestCase
}
}, hash.as_json)
end
def test_root_provided_in_options
author_serializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
end
serializer_class = Class.new(ActiveModel::Serializer) do
root :post
attributes :title, :body
has_one :author, :serializer => author_serializer
end
post_class = Class.new(Model) do
attr_accessor :author
end
author_class = Class.new(Model)
post = post_class.new(:title => "New Post", :body => "It's a new post!")
author = author_class.new(:id => 5, :name => "Tom Dale")
post.author = author
hash = serializer_class.new(post, nil, :root => :blog_post)
assert_equal({
:blog_post => {
:title => "New Post",
:body => "It's a new post!",
:author => { :id => 5, :name => "Tom Dale" }
}
}, hash.as_json)
end
def test_serializer_has_access_to_root_object
hash_object = nil
author_serializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
define_method :serializable_hash do
hash_object = @hash
super()
end
end
serializer_class = Class.new(ActiveModel::Serializer) do
root :post
attributes :title, :body
has_one :author, :serializer => author_serializer
end
post_class = Class.new(Model) do
attr_accessor :author
end
author_class = Class.new(Model)
post = post_class.new(:title => "New Post", :body => "It's a new post!")
author = author_class.new(:id => 5, :name => "Tom Dale")
post.author = author
expected = serializer_class.new(post, nil).as_json
assert_equal expected, hash_object
end
# the point of this test is to illustrate that deeply nested serializers
# still side-load at the root.
def test_embed_with_include_inserts_at_root
tag_serializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
end
comment_serializer = Class.new(ActiveModel::Serializer) do
embed :ids, :include => true
attributes :id, :body
has_many :tags, :serializer => tag_serializer
end
post_serializer = Class.new(ActiveModel::Serializer) do
embed :ids, :include => true
attributes :id, :title, :body
has_many :comments, :serializer => comment_serializer
end
post_class = Class.new(Model) do
attr_accessor :comments
define_method :active_model_serializer do
post_serializer
end
end
comment_class = Class.new(Model) do
attr_accessor :tags
end
tag_class = Class.new(Model)
post = post_class.new(:title => "New Post", :body => "NEW POST", :id => 1)
comment1 = comment_class.new(:body => "EWOT", :id => 1)
comment2 = comment_class.new(:body => "YARLY", :id => 2)
tag1 = tag_class.new(:name => "lolcat", :id => 1)
tag2 = tag_class.new(:name => "nyancat", :id => 2)
tag3 = tag_class.new(:name => "violetcat", :id => 3)
post.comments = [comment1, comment2]
comment1.tags = [tag1, tag3]
comment2.tags = [tag1, tag2]
actual = ActiveModel::ArraySerializer.new([post], nil, :root => :posts).as_json
assert_equal({
:posts => [
{ :title => "New Post", :body => "NEW POST", :id => 1, :comments => [1,2] }
],
:comments => [
{ :body => "EWOT", :id => 1, :tags => [1,3] },
{ :body => "YARLY", :id => 2, :tags => [1,2] }
],
:tags => [
{ :name => "lolcat", :id => 1 },
{ :name => "violetcat", :id => 3 },
{ :name => "nyancat", :id => 2 }
]
}, actual)
end
end

View File

@@ -1,7 +1,5 @@
require "rubygems"
require "bundler"
Bundler.setup
require "bundler/setup"
require "active_model_serializers"
require "active_support/json"