mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +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
|
||||
your new model. You can also generate a serializer for an existing model with
|
||||
the `serializer generator`:
|
||||
the serializer generator:
|
||||
|
||||
```
|
||||
$ rails g serializer post
|
||||
@ -66,10 +66,108 @@ end
|
||||
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
||||
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
|
||||
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
|
||||
|
||||
If you find that your project is already relying on the old rails to_json
|
||||
@ -107,6 +205,45 @@ class PostSerializer < ActiveModel::Serializer
|
||||
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
|
||||
|
||||
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.
|
||||
def comments
|
||||
post.comments.where(:created_by => options[:scope])
|
||||
post.comments.where(:created_by => scope)
|
||||
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
|
||||
`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)
|
||||
|
||||
* First tagged version
|
||||
|
||||
@ -40,15 +40,19 @@ module ActionController
|
||||
end
|
||||
|
||||
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) ||
|
||||
(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
|
||||
options[:scope] = serialization_scope
|
||||
options[:scope] = serialization_scope unless options.has_key?(:scope)
|
||||
options[:url_options] = url_options
|
||||
json = serializer.new(json, options.merge(default_serializer_options || {}))
|
||||
end
|
||||
|
||||
@ -29,18 +29,31 @@ module ActiveModel
|
||||
|
||||
# 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.
|
||||
#
|
||||
# To disable serialization of root elements:
|
||||
#
|
||||
# ActiveModel::ArraySerializer.root = false
|
||||
#
|
||||
class ArraySerializer
|
||||
attr_reader :object, :options
|
||||
|
||||
class_attribute :root
|
||||
|
||||
def initialize(object, options={})
|
||||
@object, @options = object, options
|
||||
end
|
||||
|
||||
def serializable_array
|
||||
@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)
|
||||
else
|
||||
item
|
||||
@ -72,7 +85,7 @@ module ActiveModel
|
||||
#
|
||||
# Provides a basic serializer implementation that allows you to easily
|
||||
# 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:
|
||||
#
|
||||
# PostSerializer.new(@post, :scope => current_user).to_json
|
||||
@ -81,7 +94,7 @@ module ActiveModel
|
||||
# in for authorization purposes.
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# class PostSerializer < ActiveModel::Serializer
|
||||
@ -97,11 +110,23 @@ module ActiveModel
|
||||
# end
|
||||
#
|
||||
# def author?
|
||||
# post.author == options[:scope]
|
||||
# post.author == scope
|
||||
# end
|
||||
# end
|
||||
#
|
||||
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:
|
||||
class Config #:nodoc:
|
||||
class_attribute :options
|
||||
@ -176,6 +201,10 @@ module ActiveModel
|
||||
option(:include, source_serializer._root_embed)
|
||||
end
|
||||
|
||||
def embeddable?
|
||||
!associated_object.nil?
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_serializable(object)
|
||||
@ -210,13 +239,41 @@ module ActiveModel
|
||||
end
|
||||
|
||||
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
|
||||
key.to_s.pluralize.to_sym
|
||||
if polymorphic?
|
||||
associated_object.class.to_s.pluralize.demodulize.underscore.to_sym
|
||||
else
|
||||
key.to_s.pluralize.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
def serialize
|
||||
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
|
||||
|
||||
def serialize_many
|
||||
@ -226,7 +283,14 @@ module ActiveModel
|
||||
end
|
||||
|
||||
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)
|
||||
else
|
||||
nil
|
||||
@ -257,10 +321,12 @@ module ActiveModel
|
||||
end
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
@ -270,7 +336,9 @@ module ActiveModel
|
||||
|
||||
attrs.each do |attr|
|
||||
unless method_defined?(attr)
|
||||
class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__
|
||||
define_method attr do
|
||||
object.send attr
|
||||
end
|
||||
end
|
||||
|
||||
self._associations[attr] = klass.refine(attr, options)
|
||||
@ -387,7 +455,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
def url_options
|
||||
@options[:url_options]
|
||||
@options[:url_options] || {}
|
||||
end
|
||||
|
||||
# Returns a json representation of the serializable
|
||||
@ -475,7 +543,9 @@ module ActiveModel
|
||||
if association.embed_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
|
||||
end
|
||||
elsif association.embed_objects?
|
||||
@ -514,6 +584,11 @@ module ActiveModel
|
||||
hash
|
||||
end
|
||||
|
||||
# Returns options[:scope]
|
||||
def scope
|
||||
@options[:scope]
|
||||
end
|
||||
|
||||
alias :read_attribute_for_serialization :send
|
||||
|
||||
# Use ActiveSupport::Notifications to send events to external systems.
|
||||
@ -523,10 +598,3 @@ module ActiveModel
|
||||
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_model"
|
||||
require "active_model/serializer"
|
||||
require "set"
|
||||
|
||||
if defined?(Rails)
|
||||
module ActiveModel
|
||||
@ -57,6 +58,19 @@ ActiveSupport.on_load(:active_record) do
|
||||
include ActiveModel::SerializerSupport
|
||||
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
|
||||
require 'action_controller'
|
||||
require 'action_controller/serialization'
|
||||
|
||||
@ -66,6 +66,10 @@ class RenderJsonTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class CustomArraySerializer < ActiveModel::ArraySerializer
|
||||
self.root = "items"
|
||||
end
|
||||
|
||||
class TestController < ActionController::Base
|
||||
protect_from_forgery
|
||||
|
||||
@ -127,13 +131,25 @@ class RenderJsonTest < ActionController::TestCase
|
||||
render :json => JsonSerializable.new, :options => true
|
||||
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
|
||||
@current_user = Struct.new(:as_json).new(:current_user => true)
|
||||
render :json => JsonSerializable.new(true)
|
||||
end
|
||||
|
||||
# To specify a custom serializer for an object, use :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
|
||||
|
||||
def render_json_with_links
|
||||
@ -144,6 +160,14 @@ class RenderJsonTest < ActionController::TestCase
|
||||
render :json => [], :root => false
|
||||
end
|
||||
|
||||
def render_json_empty_array
|
||||
render :json => []
|
||||
end
|
||||
|
||||
def render_json_array_with_custom_array_serializer
|
||||
render :json => [], :serializer => CustomArraySerializer
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def default_serializer_options
|
||||
@ -241,6 +265,11 @@ class RenderJsonTest < ActionController::TestCase
|
||||
assert_match '"options":true', @response.body
|
||||
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
|
||||
get :render_json_with_serializer_api_but_without_serializer
|
||||
assert_match '{"serializable_object":true}', @response.body
|
||||
@ -251,6 +280,11 @@ class RenderJsonTest < ActionController::TestCase
|
||||
assert_match '{"hello":true}', @response.body
|
||||
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
|
||||
get :render_json_with_links
|
||||
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
|
||||
assert_equal '[]', @response.body
|
||||
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
|
||||
|
||||
@ -4,8 +4,27 @@ class RandomModel
|
||||
include ActiveModel::SerializerSupport
|
||||
end
|
||||
|
||||
class RandomModelCollection
|
||||
include ActiveModel::ArraySerializerSupport
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
class Relation
|
||||
end
|
||||
end
|
||||
|
||||
class SerializerSupportTest < ActiveModel::TestCase
|
||||
test "it returns nil if no serializer exists" do
|
||||
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
|
||||
|
||||
|
||||
@ -94,6 +94,11 @@ class SerializerTest < ActiveModel::TestCase
|
||||
has_many :comments, :serializer => CommentSerializer
|
||||
end
|
||||
|
||||
def test_scope_works_correct
|
||||
serializer = ActiveModel::Serializer.new :foo, :scope => :bar
|
||||
assert_equal serializer.scope, :bar
|
||||
end
|
||||
|
||||
def test_attributes
|
||||
user = User.new
|
||||
user_serializer = DefaultUserSerializer.new(user, {})
|
||||
@ -138,6 +143,12 @@ class SerializerTest < ActiveModel::TestCase
|
||||
assert_equal({ :host => "test.local" }, user_serializer.url_options)
|
||||
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
|
||||
user = User.new
|
||||
user.superuser = true
|
||||
@ -411,6 +422,40 @@ class SerializerTest < ActiveModel::TestCase
|
||||
assert_equal({ :items => [ hash.as_json ]}, serializer.as_json)
|
||||
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
|
||||
attr_accessor :public_posts, :public_user
|
||||
end
|
||||
@ -890,4 +935,358 @@ class SerializerTest < ActiveModel::TestCase
|
||||
end
|
||||
assert_equal ActiveModel::Serializer, loaded
|
||||
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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user