mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-25 07:16:49 +00:00
Merge branch 'master' of github.com:josevalim/active_model_serializers
This commit is contained in:
commit
84f8c1c3d5
@ -39,7 +39,7 @@ $ rails g resource post title:string body:string
|
|||||||
|
|
||||||
This will generate a serializer in `app/serializers/post_serializer.rb` for
|
This will generate a serializer in `app/serializers/post_serializer.rb` for
|
||||||
your new model. You can also generate a serializer for an existing model with
|
your new model. You can also generate a serializer for an existing model with
|
||||||
the `serializer generator`:
|
the serializer generator:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rails g serializer post
|
$ rails g serializer post
|
||||||
@ -66,10 +66,108 @@ end
|
|||||||
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
||||||
it exists, use it to serialize the `Post`.
|
it exists, use it to serialize the `Post`.
|
||||||
|
|
||||||
This also works with `render_with`, which uses `to_json` under the hood. Also
|
This also works with `respond_with`, which uses `to_json` under the hood. Also
|
||||||
note that any options passed to `render :json` will be passed to your
|
note that any options passed to `render :json` will be passed to your
|
||||||
serializer and available as `@options` inside.
|
serializer and available as `@options` inside.
|
||||||
|
|
||||||
|
To specify a custom serializer for an object, there are 2 options:
|
||||||
|
|
||||||
|
#### 1. Specify the serializer in your model:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Post < ActiveRecord::Base
|
||||||
|
def active_model_serializer
|
||||||
|
FancyPostSerializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Specify the serializer when you render the object:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
render :json => @post, :serializer => FancyPostSerializer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arrays
|
||||||
|
|
||||||
|
In your controllers, when you use `render :json` for an array of objects, AMS will
|
||||||
|
use `ActiveModel::ArraySerializer` (included in this project) as the base serializer,
|
||||||
|
and the individual `Serializer` for the objects contained in that array.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class PostSerializer < ActiveModel::Serializer
|
||||||
|
attributes :title, :body
|
||||||
|
end
|
||||||
|
|
||||||
|
class PostsController < ApplicationController
|
||||||
|
def index
|
||||||
|
@posts = Post.all
|
||||||
|
render :json => @posts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Given the example above, the index action will return
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"posts":
|
||||||
|
[
|
||||||
|
{ "title": "Post 1", "body": "Hello!" },
|
||||||
|
{ "title": "Post 2", "body": "Goodbye!" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the root element is the name of the controller. For example, `PostsController`
|
||||||
|
generates a root element "posts". To change it:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
render :json => @posts, :root => "some_posts"
|
||||||
|
```
|
||||||
|
|
||||||
|
You may disable the root element for arrays at the top level, which will result in
|
||||||
|
more concise json. To disable the root element for arrays, you have 3 options:
|
||||||
|
|
||||||
|
#### 1. Disable root globally for in `ArraySerializer`. In an initializer:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ActiveModel::ArraySerializer.root = false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Disable root per render call in your controller:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
render :json => @posts, :root => false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Create a custom `ArraySerializer` and render arrays with it:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class CustomArraySerializer < ActiveModel::ArraySerializer
|
||||||
|
self.root = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# controller:
|
||||||
|
render :json => @posts, :serializer => CustomArraySerializer
|
||||||
|
```
|
||||||
|
|
||||||
|
Disabling the root element of the array with any of the above 3 methods
|
||||||
|
will produce
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "title": "Post 1", "body": "Hello!" },
|
||||||
|
{ "title": "Post 2", "body": "Goodbye!" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
To specify a custom serializer for the items within an array:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
render :json => @posts, :each_serializer => FancyPostSerializer
|
||||||
|
```
|
||||||
|
|
||||||
## Getting the old version
|
## Getting the old version
|
||||||
|
|
||||||
If you find that your project is already relying on the old rails to_json
|
If you find that your project is already relying on the old rails to_json
|
||||||
@ -107,6 +205,45 @@ class PostSerializer < ActiveModel::Serializer
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Custom Attributes
|
||||||
|
|
||||||
|
If you would like customize your JSON to include things beyond the simple
|
||||||
|
attributes of the model, you can override its `attributes` method
|
||||||
|
to return anything you need.
|
||||||
|
|
||||||
|
The most common scenario to use this feature is when an attribute
|
||||||
|
depends on a serialization scope. By default, the current user of your
|
||||||
|
application will be available in your serializer under the method
|
||||||
|
`scope`. This allows you to check for permissions before adding
|
||||||
|
an attribute. For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Person < ActiveRecord::Base
|
||||||
|
def full_name
|
||||||
|
"#{first_name} #{last_name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class PersonSerializer < ActiveModel::Serializer
|
||||||
|
attributes :first_name, :last_name
|
||||||
|
|
||||||
|
def attributes
|
||||||
|
hash = super
|
||||||
|
hash["full_name"] = object.full_name if scope.admin?
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
The serialization scope can be customized in your controller by
|
||||||
|
calling `serialization_scope`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
serialization_scope :current_admin
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Associations
|
## Associations
|
||||||
|
|
||||||
For specified associations, the serializer will look up the association and
|
For specified associations, the serializer will look up the association and
|
||||||
@ -126,12 +263,12 @@ class PostSerializer < ActiveModel::Serializer
|
|||||||
|
|
||||||
# only let the user see comments he created.
|
# only let the user see comments he created.
|
||||||
def comments
|
def comments
|
||||||
post.comments.where(:created_by => options[:scope])
|
post.comments.where(:created_by => scope)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
In a serializer, `options[:scope]` is the current authorization scope (usually
|
In a serializer, `scope` is the current authorization scope (usually
|
||||||
`current_user`), which the controller gives to the serializer when you call
|
`current_user`), which the controller gives to the serializer when you call
|
||||||
`render :json`
|
`render :json`
|
||||||
|
|
||||||
@ -1,3 +1,14 @@
|
|||||||
|
# VERSION 0.6 (to be released)
|
||||||
|
|
||||||
|
* Serialize sets properly
|
||||||
|
* Add root option to ArraySerializer
|
||||||
|
* Support polymorphic associations
|
||||||
|
* Support :each_serializer in ArraySerializer
|
||||||
|
* Add `scope` method to easily access the scope in the serializer
|
||||||
|
* Fix regression with Rails 3.2.6
|
||||||
|
* Allow serialization_scope to be disabled with serialization_scope nil
|
||||||
|
* Array serializer should support pure ruby objects besides serializers
|
||||||
|
|
||||||
# VERSION 0.5 (May 16, 2012)
|
# VERSION 0.5 (May 16, 2012)
|
||||||
|
|
||||||
* First tagged version
|
* First tagged version
|
||||||
|
|||||||
@ -40,15 +40,19 @@ module ActionController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def _render_option_json(json, options)
|
def _render_option_json(json, options)
|
||||||
if json.respond_to?(:to_ary)
|
|
||||||
options[:root] ||= controller_name unless options[:root] == false
|
|
||||||
end
|
|
||||||
|
|
||||||
serializer = options.delete(:serializer) ||
|
serializer = options.delete(:serializer) ||
|
||||||
(json.respond_to?(:active_model_serializer) && json.active_model_serializer)
|
(json.respond_to?(:active_model_serializer) && json.active_model_serializer)
|
||||||
|
|
||||||
|
if json.respond_to?(:to_ary)
|
||||||
|
if options[:root] != false && serializer.root != false
|
||||||
|
# default root element for arrays is serializer's root or the controller name
|
||||||
|
# the serializer for an Array is ActiveModel::ArraySerializer
|
||||||
|
options[:root] ||= serializer.root || controller_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if serializer
|
if serializer
|
||||||
options[:scope] = serialization_scope
|
options[:scope] = serialization_scope unless options.has_key?(:scope)
|
||||||
options[:url_options] = url_options
|
options[:url_options] = url_options
|
||||||
json = serializer.new(json, options.merge(default_serializer_options || {}))
|
json = serializer.new(json, options.merge(default_serializer_options || {}))
|
||||||
end
|
end
|
||||||
|
|||||||
@ -29,18 +29,31 @@ module ActiveModel
|
|||||||
|
|
||||||
# Active Model Array Serializer
|
# Active Model Array Serializer
|
||||||
#
|
#
|
||||||
# It serializes an array checking if each element that implements
|
# It serializes an Array, checking if each element that implements
|
||||||
# the +active_model_serializer+ method.
|
# the +active_model_serializer+ method.
|
||||||
|
#
|
||||||
|
# To disable serialization of root elements:
|
||||||
|
#
|
||||||
|
# ActiveModel::ArraySerializer.root = false
|
||||||
|
#
|
||||||
class ArraySerializer
|
class ArraySerializer
|
||||||
attr_reader :object, :options
|
attr_reader :object, :options
|
||||||
|
|
||||||
|
class_attribute :root
|
||||||
|
|
||||||
def initialize(object, options={})
|
def initialize(object, options={})
|
||||||
@object, @options = object, options
|
@object, @options = object, options
|
||||||
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 @options.has_key? :each_serializer
|
||||||
|
serializer = @options[:each_serializer]
|
||||||
|
elsif item.respond_to?(:active_model_serializer)
|
||||||
|
serializer = item.active_model_serializer
|
||||||
|
end
|
||||||
|
|
||||||
|
if serializer
|
||||||
serializer.new(item, @options)
|
serializer.new(item, @options)
|
||||||
else
|
else
|
||||||
item
|
item
|
||||||
@ -72,7 +85,7 @@ module ActiveModel
|
|||||||
#
|
#
|
||||||
# Provides a basic serializer implementation that allows you to easily
|
# Provides a basic serializer implementation that allows you to easily
|
||||||
# control how a given object is going to be serialized. On initialization,
|
# control how a given object is going to be serialized. On initialization,
|
||||||
# it expects to object as arguments, a resource and options. For example,
|
# it expects two objects as arguments, a resource and options. For example,
|
||||||
# one may do in a controller:
|
# one may do in a controller:
|
||||||
#
|
#
|
||||||
# PostSerializer.new(@post, :scope => current_user).to_json
|
# PostSerializer.new(@post, :scope => current_user).to_json
|
||||||
@ -81,7 +94,7 @@ module ActiveModel
|
|||||||
# in for authorization purposes.
|
# in for authorization purposes.
|
||||||
#
|
#
|
||||||
# We use the scope to check if a given attribute should be serialized or not.
|
# We use the scope to check if a given attribute should be serialized or not.
|
||||||
# For example, some attributes maybe only be returned if +current_user+ is the
|
# For example, some attributes may only be returned if +current_user+ is the
|
||||||
# author of the post:
|
# author of the post:
|
||||||
#
|
#
|
||||||
# class PostSerializer < ActiveModel::Serializer
|
# class PostSerializer < ActiveModel::Serializer
|
||||||
@ -97,11 +110,23 @@ module ActiveModel
|
|||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# def author?
|
# def author?
|
||||||
# post.author == options[:scope]
|
# post.author == scope
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
class Serializer
|
class Serializer
|
||||||
|
class IncludeError < StandardError
|
||||||
|
attr_reader :source, :association
|
||||||
|
|
||||||
|
def initialize(source, association)
|
||||||
|
@source, @association = source, association
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"Cannot serialize #{association} when #{source} does not have a root!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
module Associations #:nodoc:
|
module Associations #:nodoc:
|
||||||
class Config #:nodoc:
|
class Config #:nodoc:
|
||||||
class_attribute :options
|
class_attribute :options
|
||||||
@ -176,6 +201,10 @@ module ActiveModel
|
|||||||
option(:include, source_serializer._root_embed)
|
option(:include, source_serializer._root_embed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def embeddable?
|
||||||
|
!associated_object.nil?
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def find_serializable(object)
|
def find_serializable(object)
|
||||||
@ -210,13 +239,41 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
|
|
||||||
class HasOne < Config #:nodoc:
|
class HasOne < Config #:nodoc:
|
||||||
|
def embeddable?
|
||||||
|
if polymorphic? && associated_object.nil?
|
||||||
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def polymorphic?
|
||||||
|
option :polymorphic
|
||||||
|
end
|
||||||
|
|
||||||
|
def polymorphic_key
|
||||||
|
associated_object.class.to_s.demodulize.underscore.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
def plural_key
|
def plural_key
|
||||||
|
if polymorphic?
|
||||||
|
associated_object.class.to_s.pluralize.demodulize.underscore.to_sym
|
||||||
|
else
|
||||||
key.to_s.pluralize.to_sym
|
key.to_s.pluralize.to_sym
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def serialize
|
def serialize
|
||||||
object = associated_object
|
object = associated_object
|
||||||
object && find_serializable(object).serializable_hash
|
|
||||||
|
if object && polymorphic?
|
||||||
|
{
|
||||||
|
:type => polymorphic_key,
|
||||||
|
polymorphic_key => find_serializable(object).serializable_hash
|
||||||
|
}
|
||||||
|
elsif object
|
||||||
|
find_serializable(object).serializable_hash
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_many
|
def serialize_many
|
||||||
@ -226,7 +283,14 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def serialize_ids
|
def serialize_ids
|
||||||
if object = associated_object
|
object = associated_object
|
||||||
|
|
||||||
|
if object && polymorphic?
|
||||||
|
{
|
||||||
|
:type => polymorphic_key,
|
||||||
|
:id => object.read_attribute_for_serialization(:id)
|
||||||
|
}
|
||||||
|
elsif object
|
||||||
object.read_attribute_for_serialization(:id)
|
object.read_attribute_for_serialization(:id)
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
@ -257,10 +321,12 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def attribute(attr, options={})
|
def attribute(attr, options={})
|
||||||
self._attributes = _attributes.merge(attr => options[:key] || attr)
|
self._attributes = _attributes.merge(attr => options[:key] || attr.to_s.gsub(/\?$/, '').to_sym)
|
||||||
|
|
||||||
unless method_defined?(attr)
|
unless method_defined?(attr)
|
||||||
class_eval "def #{attr}() object.read_attribute_for_serialization(:#{attr}) end", __FILE__, __LINE__
|
define_method attr do
|
||||||
|
object.read_attribute_for_serialization(attr.to_sym)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -270,7 +336,9 @@ module ActiveModel
|
|||||||
|
|
||||||
attrs.each do |attr|
|
attrs.each do |attr|
|
||||||
unless method_defined?(attr)
|
unless method_defined?(attr)
|
||||||
class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__
|
define_method attr do
|
||||||
|
object.send attr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self._associations[attr] = klass.refine(attr, options)
|
self._associations[attr] = klass.refine(attr, options)
|
||||||
@ -387,7 +455,7 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def url_options
|
def url_options
|
||||||
@options[:url_options]
|
@options[:url_options] || {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a json representation of the serializable
|
# Returns a json representation of the serializable
|
||||||
@ -475,7 +543,9 @@ module ActiveModel
|
|||||||
if association.embed_ids?
|
if association.embed_ids?
|
||||||
node[association.key] = association.serialize_ids
|
node[association.key] = association.serialize_ids
|
||||||
|
|
||||||
if association.embed_in_root?
|
if association.embed_in_root? && hash.nil?
|
||||||
|
raise IncludeError.new(self.class, association.name)
|
||||||
|
elsif association.embed_in_root? && association.embeddable?
|
||||||
merge_association hash, association.root, association.serialize_many, unique_values
|
merge_association hash, association.root, association.serialize_many, unique_values
|
||||||
end
|
end
|
||||||
elsif association.embed_objects?
|
elsif association.embed_objects?
|
||||||
@ -514,6 +584,11 @@ module ActiveModel
|
|||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns options[:scope]
|
||||||
|
def scope
|
||||||
|
@options[:scope]
|
||||||
|
end
|
||||||
|
|
||||||
alias :read_attribute_for_serialization :send
|
alias :read_attribute_for_serialization :send
|
||||||
|
|
||||||
# Use ActiveSupport::Notifications to send events to external systems.
|
# Use ActiveSupport::Notifications to send events to external systems.
|
||||||
@ -523,10 +598,3 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Array
|
|
||||||
# Array uses ActiveModel::ArraySerializer.
|
|
||||||
def active_model_serializer
|
|
||||||
ActiveModel::ArraySerializer
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ require "active_support/core_ext/string/inflections"
|
|||||||
require "active_support/notifications"
|
require "active_support/notifications"
|
||||||
require "active_model"
|
require "active_model"
|
||||||
require "active_model/serializer"
|
require "active_model/serializer"
|
||||||
|
require "set"
|
||||||
|
|
||||||
if defined?(Rails)
|
if defined?(Rails)
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
@ -57,6 +58,19 @@ ActiveSupport.on_load(:active_record) do
|
|||||||
include ActiveModel::SerializerSupport
|
include ActiveModel::SerializerSupport
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module ActiveModel::ArraySerializerSupport
|
||||||
|
def active_model_serializer
|
||||||
|
ActiveModel::ArraySerializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Array.send(:include, ActiveModel::ArraySerializerSupport)
|
||||||
|
Set.send(:include, ActiveModel::ArraySerializerSupport)
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:active_record) do
|
||||||
|
ActiveRecord::Relation.send(:include, ActiveModel::ArraySerializerSupport)
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
require 'action_controller'
|
require 'action_controller'
|
||||||
require 'action_controller/serialization'
|
require 'action_controller/serialization'
|
||||||
|
|||||||
@ -66,6 +66,10 @@ class RenderJsonTest < ActionController::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class CustomArraySerializer < ActiveModel::ArraySerializer
|
||||||
|
self.root = "items"
|
||||||
|
end
|
||||||
|
|
||||||
class TestController < ActionController::Base
|
class TestController < ActionController::Base
|
||||||
protect_from_forgery
|
protect_from_forgery
|
||||||
|
|
||||||
@ -127,13 +131,25 @@ class RenderJsonTest < ActionController::TestCase
|
|||||||
render :json => JsonSerializable.new, :options => true
|
render :json => JsonSerializable.new, :options => true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_json_with_serializer_and_scope_option
|
||||||
|
@current_user = Struct.new(:as_json).new(:current_user => true)
|
||||||
|
scope = Struct.new(:as_json).new(:current_user => false)
|
||||||
|
render :json => JsonSerializable.new, :scope => scope
|
||||||
|
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)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# To specify a custom serializer for an object, use :serializer.
|
||||||
def render_json_with_custom_serializer
|
def render_json_with_custom_serializer
|
||||||
render :json => [], :serializer => CustomSerializer
|
render :json => Object.new, :serializer => CustomSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
# To specify a custom serializer for each item in the Array, use :each_serializer.
|
||||||
|
def render_json_array_with_custom_serializer
|
||||||
|
render :json => [Object.new], :each_serializer => CustomSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_json_with_links
|
def render_json_with_links
|
||||||
@ -144,6 +160,14 @@ class RenderJsonTest < ActionController::TestCase
|
|||||||
render :json => [], :root => false
|
render :json => [], :root => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_json_empty_array
|
||||||
|
render :json => []
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_json_array_with_custom_array_serializer
|
||||||
|
render :json => [], :serializer => CustomArraySerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def default_serializer_options
|
def default_serializer_options
|
||||||
@ -241,6 +265,11 @@ class RenderJsonTest < ActionController::TestCase
|
|||||||
assert_match '"options":true', @response.body
|
assert_match '"options":true', @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_json_with_serializer_and_scope_option
|
||||||
|
get :render_json_with_serializer_and_scope_option
|
||||||
|
assert_match '"scope":{"current_user":false}', @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
|
||||||
@ -251,6 +280,11 @@ class RenderJsonTest < ActionController::TestCase
|
|||||||
assert_match '{"hello":true}', @response.body
|
assert_match '{"hello":true}', @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_json_array_with_custom_serializer
|
||||||
|
get :render_json_array_with_custom_serializer
|
||||||
|
assert_match '{"test":[{"hello":true}]}', @response.body
|
||||||
|
end
|
||||||
|
|
||||||
def test_render_json_with_links
|
def test_render_json_with_links
|
||||||
get :render_json_with_links
|
get :render_json_with_links
|
||||||
assert_match '{"link":"http://www.nextangle.com/hypermedia"}', @response.body
|
assert_match '{"link":"http://www.nextangle.com/hypermedia"}', @response.body
|
||||||
@ -260,4 +294,23 @@ class RenderJsonTest < ActionController::TestCase
|
|||||||
get :render_json_array_with_no_root
|
get :render_json_array_with_no_root
|
||||||
assert_equal '[]', @response.body
|
assert_equal '[]', @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_json_empty_array
|
||||||
|
get :render_json_empty_array
|
||||||
|
assert_equal '{"test":[]}', @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_json_empty_arry_with_array_serializer_root_false
|
||||||
|
ActiveModel::ArraySerializer.root = false
|
||||||
|
get :render_json_empty_array
|
||||||
|
assert_equal '[]', @response.body
|
||||||
|
ensure # teardown
|
||||||
|
ActiveModel::ArraySerializer.root = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_json_array_with_custom_array_serializer
|
||||||
|
get :render_json_array_with_custom_array_serializer
|
||||||
|
assert_equal '{"items":[]}', @response.body
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -4,8 +4,27 @@ class RandomModel
|
|||||||
include ActiveModel::SerializerSupport
|
include ActiveModel::SerializerSupport
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class RandomModelCollection
|
||||||
|
include ActiveModel::ArraySerializerSupport
|
||||||
|
end
|
||||||
|
|
||||||
|
module ActiveRecord
|
||||||
|
class Relation
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class SerializerSupportTest < ActiveModel::TestCase
|
class SerializerSupportTest < ActiveModel::TestCase
|
||||||
test "it returns nil if no serializer exists" do
|
test "it returns nil if no serializer exists" do
|
||||||
assert_equal nil, RandomModel.new.active_model_serializer
|
assert_equal nil, RandomModel.new.active_model_serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns ArraySerializer for a collection" do
|
||||||
|
assert_equal ActiveModel::ArraySerializer, RandomModelCollection.new.active_model_serializer
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it automatically includes array_serializer in active_record/relation" do
|
||||||
|
ActiveSupport.run_load_hooks(:active_record)
|
||||||
|
assert_equal ActiveModel::ArraySerializer, ActiveRecord::Relation.new.active_model_serializer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -94,6 +94,11 @@ class SerializerTest < ActiveModel::TestCase
|
|||||||
has_many :comments, :serializer => CommentSerializer
|
has_many :comments, :serializer => CommentSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_scope_works_correct
|
||||||
|
serializer = ActiveModel::Serializer.new :foo, :scope => :bar
|
||||||
|
assert_equal serializer.scope, :bar
|
||||||
|
end
|
||||||
|
|
||||||
def test_attributes
|
def test_attributes
|
||||||
user = User.new
|
user = User.new
|
||||||
user_serializer = DefaultUserSerializer.new(user, {})
|
user_serializer = DefaultUserSerializer.new(user, {})
|
||||||
@ -138,6 +143,12 @@ class SerializerTest < ActiveModel::TestCase
|
|||||||
assert_equal({ :host => "test.local" }, user_serializer.url_options)
|
assert_equal({ :host => "test.local" }, user_serializer.url_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_serializer_returns_empty_hash_without_url_options
|
||||||
|
user = User.new
|
||||||
|
user_serializer = UserSerializer.new(user)
|
||||||
|
assert_equal({}, user_serializer.url_options)
|
||||||
|
end
|
||||||
|
|
||||||
def test_pretty_accessors
|
def test_pretty_accessors
|
||||||
user = User.new
|
user = User.new
|
||||||
user.superuser = true
|
user.superuser = true
|
||||||
@ -411,6 +422,40 @@ class SerializerTest < ActiveModel::TestCase
|
|||||||
assert_equal({ :items => [ hash.as_json ]}, serializer.as_json)
|
assert_equal({ :items => [ hash.as_json ]}, serializer.as_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class CustomPostSerializer < ActiveModel::Serializer
|
||||||
|
attributes :title
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_array_serializer_with_specified_seriailizer
|
||||||
|
post1 = Post.new(:title => "Post1", :author => "Author1", :id => 1)
|
||||||
|
post2 = Post.new(:title => "Post2", :author => "Author2", :id => 2)
|
||||||
|
|
||||||
|
array = [ post1, post2 ]
|
||||||
|
|
||||||
|
serializer = array.active_model_serializer.new array, :each_serializer => CustomPostSerializer
|
||||||
|
|
||||||
|
assert_equal([
|
||||||
|
{ :title => "Post1" },
|
||||||
|
{ :title => "Post2" }
|
||||||
|
], serializer.as_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_sets_can_be_serialized
|
||||||
|
post1 = Post.new(:title => "Post1", :author => "Author1", :id => 1)
|
||||||
|
post2 = Post.new(:title => "Post2", :author => "Author2", :id => 2)
|
||||||
|
|
||||||
|
set = Set.new
|
||||||
|
set << post1
|
||||||
|
set << post2
|
||||||
|
|
||||||
|
serializer = set.active_model_serializer.new set, :each_serializer => CustomPostSerializer
|
||||||
|
|
||||||
|
as_json = serializer.as_json
|
||||||
|
assert_equal 2, as_json.size
|
||||||
|
assert as_json.include?({ :title => "Post1" })
|
||||||
|
assert as_json.include?({ :title => "Post2" })
|
||||||
|
end
|
||||||
|
|
||||||
class CustomBlog < Blog
|
class CustomBlog < Blog
|
||||||
attr_accessor :public_posts, :public_user
|
attr_accessor :public_posts, :public_user
|
||||||
end
|
end
|
||||||
@ -890,4 +935,358 @@ class SerializerTest < ActiveModel::TestCase
|
|||||||
end
|
end
|
||||||
assert_equal ActiveModel::Serializer, loaded
|
assert_equal ActiveModel::Serializer, loaded
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tests_query_attributes_strip_question_mark
|
||||||
|
todo = Class.new do
|
||||||
|
def overdue?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_attribute_for_serialization(name)
|
||||||
|
send name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attribute :overdue?
|
||||||
|
end
|
||||||
|
|
||||||
|
actual = serializer.new(todo.new).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:overdue => true
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tests_query_attributes_allow_key_option
|
||||||
|
todo = Class.new do
|
||||||
|
def overdue?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_attribute_for_serialization(name)
|
||||||
|
send name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attribute :overdue?, :key => :foo
|
||||||
|
end
|
||||||
|
|
||||||
|
actual = serializer.new(todo.new).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:foo => true
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set up some classes for polymorphic testing
|
||||||
|
class Attachment < Model
|
||||||
|
def attachable
|
||||||
|
@attributes[:attachable]
|
||||||
|
end
|
||||||
|
|
||||||
|
def readable
|
||||||
|
@attributes[:readable]
|
||||||
|
end
|
||||||
|
|
||||||
|
def edible
|
||||||
|
@attributes[:edible]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def tests_can_handle_polymorphism
|
||||||
|
email_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :subject, :body
|
||||||
|
end
|
||||||
|
|
||||||
|
email_class = Class.new(Model) do
|
||||||
|
def self.to_s
|
||||||
|
"Email"
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
email_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attachment_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :name, :url
|
||||||
|
has_one :attachable, :polymorphic => true
|
||||||
|
end
|
||||||
|
|
||||||
|
email = email_class.new :subject => 'foo', :body => 'bar'
|
||||||
|
|
||||||
|
attachment = Attachment.new :name => 'logo.png', :url => 'http://example.com/logo.png', :attachable => email
|
||||||
|
|
||||||
|
actual = attachment_serializer.new(attachment, {}).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:name => 'logo.png',
|
||||||
|
:url => 'http://example.com/logo.png',
|
||||||
|
:attachable => {
|
||||||
|
:type => :email,
|
||||||
|
:email => { :subject => 'foo', :body => 'bar' }
|
||||||
|
}
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_can_handle_polymoprhic_ids
|
||||||
|
email_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :subject, :body
|
||||||
|
end
|
||||||
|
|
||||||
|
email_class = Class.new(Model) do
|
||||||
|
def self.to_s
|
||||||
|
"Email"
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
email_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attachment_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
embed :ids
|
||||||
|
attributes :name, :url
|
||||||
|
has_one :attachable, :polymorphic => true
|
||||||
|
end
|
||||||
|
|
||||||
|
email = email_class.new :id => 1
|
||||||
|
|
||||||
|
attachment = Attachment.new :name => 'logo.png', :url => 'http://example.com/logo.png', :attachable => email
|
||||||
|
|
||||||
|
actual = attachment_serializer.new(attachment, {}).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:name => 'logo.png',
|
||||||
|
:url => 'http://example.com/logo.png',
|
||||||
|
:attachable => {
|
||||||
|
:type => :email,
|
||||||
|
:id => 1
|
||||||
|
}
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_polymorphic_associations_are_included_at_root
|
||||||
|
email_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :subject, :body, :id
|
||||||
|
end
|
||||||
|
|
||||||
|
email_class = Class.new(Model) do
|
||||||
|
def self.to_s
|
||||||
|
"Email"
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
email_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attachment_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
root :attachment
|
||||||
|
embed :ids, :include => true
|
||||||
|
attributes :name, :url
|
||||||
|
has_one :attachable, :polymorphic => true
|
||||||
|
end
|
||||||
|
|
||||||
|
email = email_class.new :id => 1, :subject => "Hello", :body => "World"
|
||||||
|
|
||||||
|
attachment = Attachment.new :name => 'logo.png', :url => 'http://example.com/logo.png', :attachable => email
|
||||||
|
|
||||||
|
actual = attachment_serializer.new(attachment, {}).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:attachment => {
|
||||||
|
:name => 'logo.png',
|
||||||
|
:url => 'http://example.com/logo.png',
|
||||||
|
:attachable => {
|
||||||
|
:type => :email,
|
||||||
|
:id => 1
|
||||||
|
}},
|
||||||
|
:emails => [{
|
||||||
|
:id => 1,
|
||||||
|
:subject => "Hello",
|
||||||
|
:body => "World"
|
||||||
|
}]
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_multiple_polymorphic_associations
|
||||||
|
email_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :subject, :body, :id
|
||||||
|
end
|
||||||
|
|
||||||
|
orange_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
embed :ids, :include => true
|
||||||
|
|
||||||
|
attributes :plu, :id
|
||||||
|
has_one :readable, :polymorphic => true
|
||||||
|
end
|
||||||
|
|
||||||
|
email_class = Class.new(Model) do
|
||||||
|
def self.to_s
|
||||||
|
"Email"
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
email_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
orange_class = Class.new(Model) do
|
||||||
|
def self.to_s
|
||||||
|
"Orange"
|
||||||
|
end
|
||||||
|
|
||||||
|
def readable
|
||||||
|
@attributes[:readable]
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
orange_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attachment_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
root :attachment
|
||||||
|
embed :ids, :include => true
|
||||||
|
|
||||||
|
attributes :name, :url
|
||||||
|
|
||||||
|
has_one :attachable, :polymorphic => true
|
||||||
|
has_one :readable, :polymorphic => true
|
||||||
|
has_one :edible, :polymorphic => true
|
||||||
|
end
|
||||||
|
|
||||||
|
email = email_class.new :id => 1, :subject => "Hello", :body => "World"
|
||||||
|
orange = orange_class.new :id => 1, :plu => "3027", :readable => email
|
||||||
|
|
||||||
|
attachment = Attachment.new({
|
||||||
|
:name => 'logo.png',
|
||||||
|
:url => 'http://example.com/logo.png',
|
||||||
|
:attachable => email,
|
||||||
|
:readable => email,
|
||||||
|
:edible => orange
|
||||||
|
})
|
||||||
|
|
||||||
|
actual = attachment_serializer.new(attachment, {}).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:emails => [{
|
||||||
|
:subject => "Hello",
|
||||||
|
:body => "World",
|
||||||
|
:id => 1
|
||||||
|
}],
|
||||||
|
|
||||||
|
:oranges => [{
|
||||||
|
:plu => "3027",
|
||||||
|
:id => 1,
|
||||||
|
:readable => { :type => :email, :id => 1 }
|
||||||
|
}],
|
||||||
|
|
||||||
|
:attachment => {
|
||||||
|
:name => 'logo.png',
|
||||||
|
:url => 'http://example.com/logo.png',
|
||||||
|
:attachable => { :type => :email, :id => 1 },
|
||||||
|
:readable => { :type => :email, :id => 1 },
|
||||||
|
:edible => { :type => :orange, :id => 1 }
|
||||||
|
}
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_raises_an_error_when_a_child_serializer_includes_associations_when_the_source_doesnt
|
||||||
|
attachment_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
attributes :name
|
||||||
|
end
|
||||||
|
|
||||||
|
fruit_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
embed :ids, :include => true
|
||||||
|
has_one :attachment, :serializer => attachment_serializer
|
||||||
|
attribute :color
|
||||||
|
end
|
||||||
|
|
||||||
|
banana_class = Class.new Model do
|
||||||
|
def self.to_s
|
||||||
|
'banana'
|
||||||
|
end
|
||||||
|
|
||||||
|
def attachment
|
||||||
|
@attributes[:attachment]
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
fruit_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
strawberry_class = Class.new Model do
|
||||||
|
def self.to_s
|
||||||
|
'strawberry'
|
||||||
|
end
|
||||||
|
|
||||||
|
def attachment
|
||||||
|
@attributes[:attachment]
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :active_model_serializer do
|
||||||
|
fruit_serializer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
smoothie = Class.new do
|
||||||
|
attr_reader :base, :flavor
|
||||||
|
|
||||||
|
def initialize(base, flavor)
|
||||||
|
@base, @flavor = base, flavor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
smoothie_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
root false
|
||||||
|
embed :ids, :include => true
|
||||||
|
|
||||||
|
has_one :base, :polymorphic => true
|
||||||
|
has_one :flavor, :polymorphic => true
|
||||||
|
end
|
||||||
|
|
||||||
|
banana_attachment = Attachment.new({
|
||||||
|
:name => 'banana_blending.md',
|
||||||
|
:id => 3,
|
||||||
|
})
|
||||||
|
|
||||||
|
strawberry_attachment = Attachment.new({
|
||||||
|
:name => 'strawberry_cleaning.doc',
|
||||||
|
:id => 4
|
||||||
|
})
|
||||||
|
|
||||||
|
banana = banana_class.new :color => "yellow", :id => 1, :attachment => banana_attachment
|
||||||
|
strawberry = strawberry_class.new :color => "red", :id => 2, :attachment => strawberry_attachment
|
||||||
|
|
||||||
|
smoothie = smoothie_serializer.new(smoothie.new(banana, strawberry))
|
||||||
|
|
||||||
|
assert_raise ActiveModel::Serializer::IncludeError do
|
||||||
|
smoothie.as_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def tests_includes_does_not_include_nil_polymoprhic_associations
|
||||||
|
post_serializer = Class.new(ActiveModel::Serializer) do
|
||||||
|
root :post
|
||||||
|
embed :ids, :include => true
|
||||||
|
has_one :author, :polymorphic => true
|
||||||
|
attributes :title
|
||||||
|
end
|
||||||
|
|
||||||
|
post = Post.new(:title => 'Foo')
|
||||||
|
|
||||||
|
actual = post_serializer.new(post).as_json
|
||||||
|
|
||||||
|
assert_equal({
|
||||||
|
:post => {
|
||||||
|
:title => 'Foo',
|
||||||
|
:author => nil
|
||||||
|
}
|
||||||
|
}, actual)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user