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 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: 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 class PostSerializer < ActiveModel::Serializer
attributes :title, :body attributes :title, :body
def initialize(post, scope)
@post, @scope = post, scope
end
def serializable_hash def serializable_hash
hash = attributes hash = attributes
hash.merge!(super_data) if super? hash.merge!(super_data) if super?

View File

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

View File

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

View File

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

View File

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

View File

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