Pull in upstream

This commit is contained in:
adman65 2011-12-21 00:00:09 +02:00
commit c8c8246e24
7 changed files with 201 additions and 36 deletions

7
.travis.yml Normal file
View File

@ -0,0 +1,7 @@
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- ree
- jruby
- rbx

View File

@ -1,3 +1,6 @@
"!https://secure.travis-ci.org/josevalim/active_model_serializers.png!":http://travis-ci.org/josevalim/active_model_serializers
h2. Rails Serializers
This guide describes how to use Active Model serializers to build non-trivial JSON services in Rails. By reading this guide, you will learn:
@ -193,10 +196,6 @@ JSON. In the above example, the +title+ and +body+ attributes were always includ
class PostSerializer < ActiveModel::Serializer
attributes :title, :body
def initialize(post, scope)
@post, @scope = post, scope
end
def serializable_hash
hash = attributes
hash.merge!(super_data) if super?
@ -355,7 +354,7 @@ h4. Modifying Associations
You can also rename associations if required. Say for example you have an association that
makes sense to be named one thing in your code, but another when data is serialized.
You can use the <code:key</code> option to specify a different name for an association.
You can use the <code:key</code> option to specify a different name for an association.
Here is an exmaple:
<pre lang="ruby">

View File

@ -29,6 +29,7 @@ module ActionController
included do
class_attribute :_serialization_scope
self._serialization_scope = :current_user
end
def serialization_scope
@ -37,7 +38,7 @@ module ActionController
def _render_option_json(json, options)
if json.respond_to?(:active_model_serializer) && (serializer = json.active_model_serializer)
json = serializer.new(json, serialization_scope)
json = serializer.new(json, serialization_scope, options)
end
super
end

View File

@ -11,14 +11,15 @@ module ActiveModel
class ArraySerializer
attr_reader :object, :scope
def initialize(object, scope)
@object, @scope = object, scope
def initialize(object, scope, options={})
@object, @scope, @options = object, scope, options
@hash = options[:hash]
end
def serializable_array
@object.map do |item|
if item.respond_to?(:active_model_serializer) && (serializer = item.active_model_serializer)
serializer.new(item, scope)
serializer.new(item, scope, :hash => @hash)
else
item
end
@ -26,7 +27,14 @@ module ActiveModel
end
def as_json(*args)
serializable_array.as_json(*args)
@hash = {}
array = serializable_array.as_json(*args)
if root = @options[:root]
@hash.merge!(root => array)
else
array
end
end
end
@ -79,9 +87,9 @@ module ActiveModel
end
class HasMany < Config #:nodoc:
def serialize(collection, scope)
def serialize(collection, scope, options)
collection.map do |item|
serializer.new(item, scope).serializable_hash
serializer.new(item, scope, options).serializable_hash
end
end
@ -96,18 +104,18 @@ module ActiveModel
end
class HasOne < Config #:nodoc:
def serialize(object, scope)
def serialize(object, scope, options)
return unless object
if polymorphic?
polymorphic_type = object.class.to_s.demodulize
serializer_class = "#{object.class.to_s}Serializer".constantize
serializer_class.new(object, scope).serializable_hash.merge({
serializer_class.new(object, scope, options).serializable_hash.merge({
"#{name}_type".to_sym => polymorphic_type
})
else
serializer.new(object, scope).serializable_hash
serializer.new(object, scope, options).serializable_hash
end
end
@ -222,8 +230,8 @@ module ActiveModel
columns = klass.columns_hash
attrs = _attributes.inject({}) do |hash, (name,key)|
column = columns[name]
hash.merge key => column[:type]
column = columns[name.to_s]
hash.merge key => column.type
end
associations = _associations.inject({}) do |hash, association|
@ -236,7 +244,7 @@ module ActiveModel
# The model class associated with this serializer.
def model_class
name.sub(/Serializer$/, '')
name.sub(/Serializer$/, '').constantize
end
# Define how associations should be embedded.
@ -269,19 +277,20 @@ module ActiveModel
attr_reader :object, :scope
def initialize(object, scope)
@object, @scope = object, scope
def initialize(object, scope, options={})
@object, @scope, @options = object, scope, options
@hash = options[:hash]
end
# Returns a json representation of the serializable
# object including the root.
def as_json(*)
if _root
hash = { _root => serializable_hash }
hash.merge!(associations) if _root_embed
if root = @options[:root] || _root
@hash = hash = {}
hash.merge!(root => serializable_hash)
hash
else
serializable_hash
@hash = serializable_hash
end
end
@ -289,6 +298,7 @@ module ActiveModel
# object without the root.
def serializable_hash
if _embed == :ids
merge_associations(@hash, associations) if _root_embed
attributes.merge(association_ids)
elsif _embed == :objects
attributes.merge(associations)
@ -297,6 +307,16 @@ module ActiveModel
end
end
def merge_associations(hash, associations)
associations.each do |key, value|
if hash[key]
hash[key] |= value
elsif value
hash[key] = value
end
end
end
# Returns a hash representation of the serializable
# object associations.
def associations
@ -304,10 +324,7 @@ module ActiveModel
_associations.each do |association|
associated_object = send(association.name)
hash[association.key] = association.serialize(associated_object, scope)
if association.polymorphic? && associated_object
end
hash[association.key] = association.serialize(associated_object, scope, :hash => @hash)
end
hash

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
@ -557,7 +557,7 @@ class SerializerTest < ActiveModel::TestCase
Class.new do
class << self
def columns_hash
{ :name => { :type => :string }, :age => { :type => :integer } }
{ "name" => Struct.new(:type).new(:string), "age" => Struct.new(:type).new(:integer) }
end
def reflect_on_association(name)
@ -734,4 +734,133 @@ class SerializerTest < ActiveModel::TestCase
}
}, serializer.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"