mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-24 23:06:50 +00:00
Pull in upstream
This commit is contained in:
commit
c8c8246e24
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
rvm:
|
||||||
|
- 1.8.7
|
||||||
|
- 1.9.2
|
||||||
|
- 1.9.3
|
||||||
|
- ree
|
||||||
|
- jruby
|
||||||
|
- rbx
|
||||||
@ -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?
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user