Skip to content

Commit

Permalink
Explicitly set serializer for associations
Browse files Browse the repository at this point in the history
Document specifying serializer for assocaition
  • Loading branch information
ggordon committed Nov 7, 2014
1 parent 95d1220 commit 03829e1
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 7 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ The `has_many` and `belongs_to` declarations describe relationships between
resources. By default, when you serialize a `Post`, you will get its `Comment`s
as well.

You may also use the `:serializer` option to specify a custom serializer class, for example:

```ruby
has_many :comments, serializer: CommentPreviewSerializer
```

The `url` declaration describes which named routes to use while generating URLs
for your JSON. Not every adapter will require URLs.

Expand Down
20 changes: 15 additions & 5 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def self.inherited(base)
def self.attributes(*attrs)
@_attributes.concat attrs


attrs.each do |attr|
define_method attr do
object.read_attribute_for_serialization(attr)
Expand Down Expand Up @@ -72,11 +71,12 @@ def self.urls(*attrs)
@_urls.concat attrs
end

def self.serializer_for(resource)
def self.serializer_for(resource, options = {})
if resource.respond_to?(:to_ary)
config.array_serializer
else
get_serializer_for(resource.class)
options.fetch(:options, {})
.fetch(:serializer, get_serializer_for(resource.class))
end
end

Expand Down Expand Up @@ -132,15 +132,25 @@ def attributes(options = {})
def each_association(&block)
self.class._associations.dup.each do |name, options|
association = object.send(name)
serializer_class = ActiveModel::Serializer.serializer_for(association)
serializer = serializer_class.new(association) if serializer_class
serializer_class = ActiveModel::Serializer.serializer_for(association, options)
serializer = serializer_class.new(
association,
serializer_from_options(options)
) if serializer_class

if block_given?
block.call(name, serializer, options[:options])
end
end
end

def serializer_from_options(options)
opts = {}
serializer = options.fetch(:options, {}).fetch(:serializer, nil)
opts[:serializer] = serializer if serializer
opts
end

private

def self.get_serializer_for(klass)
Expand Down
2 changes: 1 addition & 1 deletion lib/active_model/serializer/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def add_links(name, serializers, options)
end

def add_link(name, serializer, options)
if serializer
if serializer && serializer.object
type = serializer.object.class.to_s.underscore
if name.to_s == type || !type
@hash[@root][:links][name] = serializer.id.to_s
Expand Down
6 changes: 5 additions & 1 deletion lib/active_model/serializer/array_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ class ArraySerializer

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
Expand Down
62 changes: 62 additions & 0 deletions test/adapter/json_api/has_many_explicit_serializer_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'test_helper'

module ActiveModel
class Serializer
class Adapter
class JsonApi
# Test 'has_many :assocs, serializer: AssocXSerializer'
class HasManyExplicitSerializerTest < Minitest::Test
def setup
@post = Post.new(title: 'New Post', body: 'Body')
@author = Author.new(name: 'Jane Blogger')
@author.posts = [@post]
@post.author = @author
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@post.comments = [@first_comment, @second_comment]
@first_comment.post = @post
@second_comment.post = @post

@serializer = PostPreviewSerializer.new(@post)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(
@serializer,
include: 'comments,author,author'
)
end

def test_includes_comment_ids
assert_equal(['1', '2'],
@adapter.serializable_hash[:posts][:links][:comments])
end

def test_includes_linked_comments
assert_equal([{ id: '1' }, { id: '2' }],
@adapter.serializable_hash[:linked][:comments])
end

def test_includes_author_id
assert_equal(@author.id.to_s,
@adapter.serializable_hash[:posts][:links][:author])
end

def test_includes_linked_authors
assert_equal([{ id: @author.id.to_s }],
@adapter.serializable_hash[:linked][:authors])
end

def test_explicit_serializer_with_null_resource
@post.author = nil
assert_equal(nil,
@adapter.serializable_hash[:posts][:links][:author])
end

def test_explicit_serializer_with_null_collection
@post.comments = []
assert_equal([],
@adapter.serializable_hash[:posts][:links][:comments])
end
end
end
end
end
end
23 changes: 23 additions & 0 deletions test/fixtures/poro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,26 @@ class ProfileSerializer < ActiveModel::Serializer
belongs_to :writer
has_many :articles
end

CommentPreviewSerializer = Class.new(ActiveModel::Serializer) do
attributes :id

belongs_to :post
end

AuthorPreviewSerializer = Class.new(ActiveModel::Serializer) do
attributes :id

has_many :posts
end

PostPreviewSerializer = Class.new(ActiveModel::Serializer) do
def self.root_name
'posts'
end

attributes :title, :body, :id

has_many :comments, serializer: CommentPreviewSerializer
belongs_to :author, serializer: AuthorPreviewSerializer
end

0 comments on commit 03829e1

Please sign in to comment.