Merge pull request #703 from ggordon/specify_serializer_from_controller

Support serializer and each_serializer options in renderer
This commit is contained in:
Alexandre de Oliveira 2014-11-12 20:09:51 -02:00
commit 4af02021ac
5 changed files with 132 additions and 7 deletions

View File

@ -94,6 +94,27 @@ member when the resource names are included in the `include` option.
render @posts, include: 'authors,comments'
```
### Specify a serializer
If you wish to use a serializer other than the default, you can explicitly pass it to the renderer.
#### 1. For a resource:
```ruby
render json: @post, serializer: PostPreviewSerializer
```
#### 2. For an array resource:
```ruby
# Use the default `ArraySerializer`, which will use `each_serializer` to
# serialize each element
render json: @posts, each_serializer: PostPreviewSerializer
# Or, you can explicitly provide the collection serializer as well
render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer
```
## Installation
Add this line to your application's Gemfile:

View File

@ -8,16 +8,27 @@ module ActionController
ADAPTER_OPTION_KEYS = [:include, :root]
def get_serializer(resource, options)
@_serializer ||= options.delete(:serializer)
@_serializer ||= ActiveModel::Serializer.serializer_for(resource)
if options.key?(:each_serializer)
options[:serializer] = options.delete(:each_serializer)
end
@_serializer
end
[:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
define_method renderer_method do |resource, options|
serializer = ActiveModel::Serializer.serializer_for(resource)
if serializer
adapter_opts, serializer_opts =
options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }
options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] }
if (serializer = get_serializer(resource, serializer_opts))
# omg hax
object = serializer.new(resource, Hash[serializer_opts])
adapter = ActiveModel::Serializer.adapter.new(object, Hash[adapter_opts])
object = serializer.new(resource, serializer_opts)
adapter = ActiveModel::Serializer.adapter.new(object, adapter_opts)
super(adapter, options)
else
super(resource, options)

View File

@ -6,7 +6,10 @@ module ActiveModel
def initialize(objects, options = {})
@objects = objects.map do |object|
serializer_class = ActiveModel::Serializer.serializer_for(object)
serializer_class = options.fetch(
:serializer,
ActiveModel::Serializer.serializer_for(object)
)
serializer_class.new(object)
end
end

View File

@ -0,0 +1,78 @@
require 'test_helper'
module ActionController
module Serialization
class ExplicitSerializerTest < ActionController::TestCase
class MyController < ActionController::Base
def render_using_explicit_serializer
@profile = Profile.new(name: 'Name 1',
description: 'Description 1',
comments: 'Comments 1')
render json: @profile, serializer: ProfilePreviewSerializer
end
def render_array_using_explicit_serializer
array = [
Profile.new(name: 'Name 1',
description: 'Description 1',
comments: 'Comments 1'),
Profile.new(name: 'Name 2',
description: 'Description 2',
comments: 'Comments 2')
]
render json: array,
serializer: PaginatedSerializer,
each_serializer: ProfilePreviewSerializer
end
def render_array_using_implicit_serializer
array = [
Profile.new(name: 'Name 1',
description: 'Description 1',
comments: 'Comments 1'),
Profile.new(name: 'Name 2',
description: 'Description 2',
comments: 'Comments 2')
]
render json: array,
each_serializer: ProfilePreviewSerializer
end
end
tests MyController
def test_render_using_explicit_serializer
get :render_using_explicit_serializer
assert_equal 'application/json', @response.content_type
assert_equal '{"name":"Name 1"}', @response.body
end
def test_render_array_using_explicit_serializer
get :render_array_using_explicit_serializer
assert_equal 'application/json', @response.content_type
expected = {
'paginated' => [
{ 'name' => 'Name 1' },
{ 'name' => 'Name 2' }
]
}
assert_equal expected.to_json, @response.body
end
def test_render_array_using_explicit_serializer
get :render_array_using_implicit_serializer
assert_equal 'application/json', @response.content_type
expected = [
{ 'name' => 'Name 1' },
{ 'name' => 'Name 2' }
]
assert_equal expected.to_json, @response.body
end
end
end
end

12
test/fixtures/poro.rb vendored
View File

@ -35,6 +35,12 @@ class ProfileSerializer < ActiveModel::Serializer
urls :posts, :comments
end
class ProfilePreviewSerializer < ActiveModel::Serializer
attributes :name
urls :posts, :comments
end
Post = Class.new(Model)
Comment = Class.new(Model)
Author = Class.new(Model)
@ -83,3 +89,9 @@ BlogSerializer = Class.new(ActiveModel::Serializer) do
belongs_to :writer
has_many :articles
end
PaginatedSerializer = Class.new(ActiveModel::Serializer::ArraySerializer) do
def json_key
'paginated'
end
end