merge upstream/master

This commit is contained in:
Aaron Renoir
2014-11-05 16:47:15 -08:00
21 changed files with 382 additions and 73 deletions

View File

@@ -6,6 +6,8 @@ rvm:
- jruby-19mode - jruby-19mode
- rbx-2 - rbx-2
- ruby-head - ruby-head
install:
- bundle install --retry=3
env: env:
- "RAILS_VERSION=3.2" - "RAILS_VERSION=3.2"
- "RAILS_VERSION=4.0" - "RAILS_VERSION=4.0"

View File

@@ -85,6 +85,15 @@ 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`.
### Built in Adapters
The `:json_api` adapter will include the associated resources in the `"linked"`
member when the resource names are included in the `include` option.
```ruby
render @posts, include: 'authors,comments'
```
## Installation ## Installation
Add this line to your application's Gemfile: Add this line to your application's Gemfile:
@@ -144,19 +153,17 @@ end
The attribute names are a **whitelist** of attributes to be serialized. The attribute names are a **whitelist** of attributes to be serialized.
The `has_many` and `belongs_to` declarations describe relationships between The `has_many` and `belongs_to` declarations describe relationships between
resources. By default, when you serialize a `Post`, you will resources. By default, when you serialize a `Post`, you will get its `Comment`s
get its `Comment`s as well. as well.
The `url` declaration describes which named routes to use while generating URLs The `url` declaration describes which named routes to use while generating URLs
for your JSON. Not every adapter will require URLs. for your JSON. Not every adapter will require URLs.
## Getting Help ## Getting Help
If you find a bug, please report an If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new).
[Issue](https://github.com/rails-api/active_model_serializers/issues/new).
If you have a question, please [post to Stack If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers).
Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers).
Thanks! Thanks!

View File

@@ -6,15 +6,18 @@ module ActionController
include ActionController::Renderers include ActionController::Renderers
ADAPTER_OPTION_KEYS = [:include, :root]
[:_render_option_json, :_render_with_renderer_json].each do |renderer_method| [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
define_method renderer_method do |resource, options| define_method renderer_method do |resource, options|
serializer = ActiveModel::Serializer.serializer_for(resource) serializer = ActiveModel::Serializer.serializer_for(resource)
if serializer if serializer
adapter_opts, serializer_opts =
options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }
# omg hax # omg hax
object = serializer.new(resource, options) object = serializer.new(resource, Hash[serializer_opts])
adapter = ActiveModel::Serializer.adapter.new(object) adapter = ActiveModel::Serializer.adapter.new(object, Hash[adapter_opts])
super(adapter, options) super(adapter, options)
else else
super(resource, options) super(resource, options)
@@ -23,4 +26,3 @@ module ActionController
end end
end end
end end

View File

@@ -140,7 +140,7 @@ module ActiveModel
self.class._associations.dup.each do |name, options| self.class._associations.dup.each do |name, options|
association = object.send(name) association = object.send(name)
serializer_class = ActiveModel::Serializer.serializer_for(association) serializer_class = ActiveModel::Serializer.serializer_for(association)
serializer = serializer_class.new(association) serializer = serializer_class.new(association) if serializer_class
if block_given? if block_given?
block.call(name, serializer, options[:options]) block.call(name, serializer, options[:options])

View File

@@ -10,13 +10,14 @@ module ActiveModel
def initialize(serializer, options = {}) def initialize(serializer, options = {})
@serializer = serializer @serializer = serializer
@options = options
end end
def serializable_hash(options = {}) def serializable_hash(options = {})
raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.' raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
end end
def to_json(options = {}) def as_json(options = {})
if fields = options.delete(:fields) if fields = options.delete(:fields)
options[:fieldset] = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key) options[:fieldset] = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
end end

View File

@@ -13,7 +13,11 @@ module ActiveModel
array_serializer = association array_serializer = association
@result[name] = array_serializer.map { |item| item.attributes(opts) } @result[name] = array_serializer.map { |item| item.attributes(opts) }
else else
if association
@result[name] = association.attributes(options) @result[name] = association.attributes(options)
else
@result[name] = nil
end
end end
end end
end end

View File

@@ -5,25 +5,23 @@ module ActiveModel
def initialize(serializer, options = {}) def initialize(serializer, options = {})
super super
serializer.root = true serializer.root = true
@hash = {}
@top = @options.fetch(:top) { @hash }
end end
def serializable_hash(options = {}) def serializable_hash(options = {})
@root = (options[:root] || serializer.json_key).to_s.pluralize.to_sym @root = (@options[:root] || serializer.json_key.to_s.pluralize).to_sym
@hash = {}
@fieldset = options[:fieldset] @fieldset = options[:fieldset]
if serializer.respond_to?(:each) if serializer.respond_to?(:each)
opt = @fieldset ? {fieldset: @fieldset} : {} @hash[@root] = serializer.map do |s|
self.class.new(s, @options.merge(top: @top)).serializable_hash[@root]
@hash[@root] = serializer.map{|s| self.class.new(s).serializable_hash(opt)[@root] } end
else else
@hash[@root] = attributes_for_serializer(serializer, {}) @hash[@root] = attributes_for_serializer(serializer, @options)
serializer.each_association do |name, association, opts| serializer.each_association do |name, association, opts|
@hash[@root][:links] ||= {} @hash[@root][:links] ||= {}
unless options[:embed] == :ids
@hash[:linked] ||= {}
end
if association.respond_to?(:each) if association.respond_to?(:each)
add_links(name, association, opts) add_links(name, association, opts)
@@ -37,23 +35,59 @@ module ActiveModel
end end
def add_links(name, serializers, options) def add_links(name, serializers, options)
if serializers.first
type = serializers.first.object.class.to_s.underscore.pluralize
end
if name.to_s == type || !type
@hash[@root][:links][name] ||= [] @hash[@root][:links][name] ||= []
@hash[@root][:links][name] += serializers.map{|serializer| serializer.id.to_s } @hash[@root][:links][name] += serializers.map{|serializer| serializer.id.to_s }
else
@hash[@root][:links][name] ||= {}
@hash[@root][:links][name][:type] = type
@hash[@root][:links][name][:ids] ||= []
@hash[@root][:links][name][:ids] += serializers.map{|serializer| serializer.id.to_s }
end
unless options[:embed] == :ids unless serializers.none? || @options[:embed] == :ids
@hash[:linked][name] ||= [] serializers.each do |serializer|
@hash[:linked][name] += serializers.map { |item| attributes_for_serializer(item, options) } add_linked(name, serializer)
end
end end
end end
def add_link(name, serializer, options) def add_link(name, serializer, options)
if serializer
type = serializer.object.class.to_s.underscore
if name.to_s == type || !type
@hash[@root][:links][name] = serializer.id.to_s @hash[@root][:links][name] = serializer.id.to_s
else
@hash[@root][:links][name] ||= {}
@hash[@root][:links][name][:type] = type
@hash[@root][:links][name][:id] = serializer.id.to_s
end
unless options[:embed] == :ids unless @options[:embed] == :ids
plural_name = name.to_s.pluralize.to_sym add_linked(name, serializer)
end
else
@hash[@root][:links][name] = nil
end
end
@hash[:linked][plural_name] ||= [] def add_linked(resource, serializer, parent = nil)
@hash[:linked][plural_name].push attributes_for_serializer(serializer, options) resource_path = [parent, resource].compact.join('.')
if include_assoc? resource_path
plural_name = resource.to_s.pluralize.to_sym
attrs = attributes_for_serializer(serializer, @options)
@top[:linked] ||= {}
@top[:linked][plural_name] ||= []
@top[:linked][plural_name].push attrs unless @top[:linked][plural_name].include? attrs
end
unless serializer.respond_to?(:each)
serializer.each_association do |name, association, opts|
add_linked(name, association, resource) if association
end
end end
end end
@@ -69,6 +103,10 @@ module ActiveModel
attributes[:id] = attributes[:id].to_s if attributes[:id] attributes[:id] = attributes[:id].to_s if attributes[:id]
attributes attributes
end end
def include_assoc? assoc
@options[:include] && @options[:include].split(',').include?(assoc.to_s)
end
end end
end end
end end

View File

@@ -0,0 +1,104 @@
require 'test_helper'
module ActionController
module Serialization
class JsonApiLinkedTest < ActionController::TestCase
class MyController < ActionController::Base
def setup_post
@author = Author.new(id: 1, name: 'Steve K.')
@author.posts = []
@author2 = Author.new(id: 2, name: 'Anonymous')
@author2.posts = []
@post = Post.new(id: 1, title: 'New Post', body: 'Body')
@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]
@post.author = @author
@first_comment.post = @post
@first_comment.author = @author2
@second_comment.post = @post
@second_comment.author = nil
end
def with_json_api_adapter
old_adapter = ActiveModel::Serializer.config.adapter
ActiveModel::Serializer.config.adapter = :json_api
yield
ensure
ActiveModel::Serializer.config.adapter = old_adapter
end
def render_resource_without_include
with_json_api_adapter do
setup_post
render json: @post
end
end
def render_resource_with_include
with_json_api_adapter do
setup_post
render json: @post, include: 'author'
end
end
def render_resource_with_nested_include
with_json_api_adapter do
setup_post
render json: @post, include: 'comments.author'
end
end
def render_collection_without_include
with_json_api_adapter do
setup_post
render json: [@post]
end
end
def render_collection_with_include
with_json_api_adapter do
setup_post
render json: [@post], include: 'author,comments'
end
end
end
tests MyController
def test_render_resource_without_include
get :render_resource_without_include
response = JSON.parse(@response.body)
refute response.key? 'linked'
end
def test_render_resource_with_include
get :render_resource_with_include
response = JSON.parse(@response.body)
assert response.key? 'linked'
assert_equal 1, response['linked']['authors'].size
assert_equal 'Steve K.', response['linked']['authors'].first['name']
end
def test_render_resource_with_nested_include
get :render_resource_with_nested_include
response = JSON.parse(@response.body)
assert response.key? 'linked'
assert_equal 1, response['linked']['authors'].size
assert_equal 'Anonymous', response['linked']['authors'].first['name']
end
def test_render_collection_without_include
get :render_collection_without_include
response = JSON.parse(@response.body)
refute response.key? 'linked'
end
def test_render_collection_with_include
get :render_collection_with_include
response = JSON.parse(@response.body)
assert response.key? 'linked'
end
end
end
end

View File

@@ -24,6 +24,16 @@ module ActionController
ActiveModel::Serializer.config.adapter = old_adapter ActiveModel::Serializer.config.adapter = old_adapter
end end
def render_using_custom_root_in_adapter_with_a_default
old_adapter = ActiveModel::Serializer.config.adapter
# JSON-API adapter sets root by default
ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi
@profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
render json: @profile, root: "profile"
ensure
ActiveModel::Serializer.config.adapter = old_adapter
end
def render_array_using_implicit_serializer def render_array_using_implicit_serializer
array = [ array = [
Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }), Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }),
@@ -57,6 +67,13 @@ module ActionController
assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', @response.body assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', @response.body
end end
def test_render_using_custom_root_in_adapter_with_a_default
get :render_using_custom_root_in_adapter_with_a_default
assert_equal 'application/json', @response.content_type
assert_equal '{"profile":{"name":"Name 1","description":"Description 1"}}', @response.body
end
def test_render_array_using_implicit_serializer def test_render_array_using_implicit_serializer
get :render_array_using_implicit_serializer get :render_array_using_implicit_serializer
assert_equal 'application/json', @response.content_type assert_equal 'application/json', @response.content_type

View File

@@ -7,9 +7,14 @@ module ActiveModel
class BelongsToTest < Minitest::Test class BelongsToTest < Minitest::Test
def setup def setup
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@post.comments = [@comment] @post.comments = [@comment]
@anonymous_post.comments = []
@post.author = @author
@comment.post = @post @comment.post = @post
@comment.author = nil
@anonymous_post.author = nil
@serializer = CommentSerializer.new(@comment) @serializer = CommentSerializer.new(@comment)
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer)
@@ -18,6 +23,13 @@ module ActiveModel
def test_includes_post def test_includes_post
assert_equal({id: 42, title: 'New Post', body: 'Body'}, @adapter.serializable_hash[:post]) assert_equal({id: 42, title: 'New Post', body: 'Body'}, @adapter.serializable_hash[:post])
end end
def test_include_nil_author
serializer = PostSerializer.new(@anonymous_post)
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
assert_equal({title: "Hello!!", body: "Hello, world!!", id: 43, comments: [], author: nil}, adapter.serializable_hash)
end
end end
end end
end end

View File

@@ -6,10 +6,13 @@ module ActiveModel
class Json class Json
class Collection < Minitest::Test class Collection < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.')
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@first_post.comments = [] @first_post.comments = []
@second_post.comments = [] @second_post.comments = []
@first_post.author = @author
@second_post.author = @author
@serializer = ArraySerializer.new([@first_post, @second_post]) @serializer = ArraySerializer.new([@first_post, @second_post])
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer)
@@ -17,8 +20,8 @@ module ActiveModel
def test_include_multiple_posts def test_include_multiple_posts
assert_equal([ assert_equal([
{title: "Hello!!", body: "Hello, world!!", id: 1, comments: []}, {title: "Hello!!", body: "Hello, world!!", id: 1, comments: [], author: {id: 1, name: "Steve K."}},
{title: "New Post", body: "Body", id: 2, comments: []} {title: "New Post", body: "Body", id: 2, comments: [], author: {id: 1, name: "Steve K."}}
], @adapter.serializable_hash) ], @adapter.serializable_hash)
end end
end end

View File

@@ -6,10 +6,12 @@ module ActiveModel
class Json class Json
class HasManyTestTest < Minitest::Test class HasManyTestTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.')
@post = Post.new(title: 'New Post', body: 'Body') @post = Post.new(title: 'New Post', body: 'Body')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@post.comments = [@first_comment, @second_comment] @post.comments = [@first_comment, @second_comment]
@post.author = @author
@first_comment.post = @post @first_comment.post = @post
@second_comment.post = @post @second_comment.post = @post

View File

@@ -6,10 +6,20 @@ module ActiveModel
class JsonApi class JsonApi
class BelongsToTest < Minitest::Test class BelongsToTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.')
@post = Post.new(id: 42, title: 'New Post', body: 'Body') @post = Post.new(id: 42, title: 'New Post', body: 'Body')
@anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@post.comments = [@comment] @post.comments = [@comment]
@anonymous_post.comments = []
@comment.post = @post @comment.post = @post
@comment.author = nil
@post.author = @author
@anonymous_post.author = nil
@blog = Blog.new(id: 1, name: "My Blog!!")
@blog.writer = @author
@blog.articles = [@post, @anonymous_post]
@author.posts = []
@serializer = CommentSerializer.new(@comment) @serializer = CommentSerializer.new(@comment)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
@@ -20,8 +30,22 @@ module ActiveModel
end end
def test_includes_linked_post def test_includes_linked_post
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post')
assert_equal([{id: "42", title: 'New Post', body: 'Body'}], @adapter.serializable_hash[:linked][:posts]) assert_equal([{id: "42", title: 'New Post', body: 'Body'}], @adapter.serializable_hash[:linked][:posts])
end end
def test_include_nil_author
serializer = PostSerializer.new(@anonymous_post)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
assert_equal({comments: [], author: nil}, adapter.serializable_hash[:posts][:links])
end
def test_include_type_for_association_when_is_different_than_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
assert_equal({type: "author", id: "1"}, adapter.serializable_hash[:blogs][:links][:writer])
end
end end
end end
end end

View File

@@ -4,12 +4,16 @@ module ActiveModel
class Serializer class Serializer
class Adapter class Adapter
class JsonApi class JsonApi
class Collection < Minitest::Test class CollectionTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.')
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@first_post.comments = [] @first_post.comments = []
@second_post.comments = [] @second_post.comments = []
@first_post.author = @author
@second_post.author = @author
@author.posts = [@first_post, @second_post]
@serializer = ArraySerializer.new([@first_post, @second_post]) @serializer = ArraySerializer.new([@first_post, @second_post])
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
@@ -17,8 +21,8 @@ module ActiveModel
def test_include_multiple_posts def test_include_multiple_posts
assert_equal([ assert_equal([
{title: "Hello!!", body: "Hello, world!!", id: "1", links: {comments: []}}, { title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: [], author: "1" } },
{title: "New Post", body: "Body", id: "2", links: {comments: []}} { title: "New Post", body: "Body", id: "2", links: { comments: [], author: "1" } }
], @adapter.serializable_hash[:posts]) ], @adapter.serializable_hash[:posts])
end end
end end

View File

@@ -12,6 +12,8 @@ module ActiveModel
@author.posts = [@first_post, @second_post] @author.posts = [@first_post, @second_post]
@first_post.author = @author @first_post.author = @author
@second_post.author = @author @second_post.author = @author
@first_post.comments = []
@second_post.comments = []
@serializer = AuthorSerializer.new(@author) @serializer = AuthorSerializer.new(@author)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)

View File

@@ -6,12 +6,23 @@ module ActiveModel
class JsonApi class JsonApi
class HasManyTest < Minitest::Test class HasManyTest < Minitest::Test
def setup def setup
@post = Post.new(title: 'New Post', body: 'Body') @author = Author.new(id: 1, name: 'Steve K.')
@author.posts = []
@post = Post.new(id: 1, title: 'New Post', body: 'Body')
@post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@first_comment.author = nil
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@second_comment.author = nil
@post.comments = [@first_comment, @second_comment] @post.comments = [@first_comment, @second_comment]
@post_without_comments.comments = []
@first_comment.post = @post @first_comment.post = @post
@second_comment.post = @post @second_comment.post = @post
@post.author = @author
@post_without_comments.author = nil
@blog = Blog.new(id: 1, name: "My Blog!!")
@blog.writer = @author
@blog.articles = [@post]
@serializer = PostSerializer.new(@post) @serializer = PostSerializer.new(@post)
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
@@ -22,11 +33,25 @@ module ActiveModel
end end
def test_includes_linked_comments def test_includes_linked_comments
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments')
assert_equal([ assert_equal([
{id: "1", body: 'ZOMG A COMMENT'}, {id: "1", body: 'ZOMG A COMMENT'},
{id: "2", body: 'ZOMG ANOTHER COMMENT'} {id: "2", body: 'ZOMG ANOTHER COMMENT'}
], @adapter.serializable_hash[:linked][:comments]) ], @adapter.serializable_hash[:linked][:comments])
end end
def test_no_include_linked_if_comments_is_empty
serializer = PostSerializer.new(@post_without_comments)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
assert_nil adapter.serializable_hash[:linked]
end
def test_include_type_for_association_when_is_different_than_name
serializer = BlogSerializer.new(@blog)
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
assert_equal({type: "posts", ids: ["1"]}, adapter.serializable_hash[:blogs][:links][:articles])
end
end end
end end
end end

View File

@@ -0,0 +1,40 @@
require 'test_helper'
module ActiveModel
class Serializer
class Adapter
class JsonApi
class LinkedTest < Minitest::Test
def setup
@author = Author.new(id: 1, name: 'Steve K.')
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@first_post.comments = []
@second_post.comments = []
@first_post.author = @author
@second_post.author = @author
@author.posts = [@first_post, @second_post]
@serializer = ArraySerializer.new([@first_post, @second_post])
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,comments')
end
def test_include_multiple_posts_and_linked
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@first_post.comments = [@first_comment, @second_comment]
@first_comment.post = @first_post
@first_comment.author = nil
@second_comment.post = @first_post
@second_comment.author = nil
assert_equal([
{ title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: ['1', '2'], author: "1" } },
{ title: "New Post", body: "Body", id: "2", links: { comments: [], :author => "1" } }
], @adapter.serializable_hash[:posts])
assert_equal({ :comments => [{ :id => "1", :body => "ZOMG A COMMENT" }, { :id => "2", :body => "ZOMG ANOTHER COMMENT" }], :authors => [{ :id => "1", :name => "Steve K." }] }, @adapter.serializable_hash[:linked])
end
end
end
end
end
end

View File

@@ -5,12 +5,14 @@ module ActiveModel
class Adapter class Adapter
class JsonTest < Minitest::Test class JsonTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.')
@post = Post.new(title: 'New Post', body: 'Body') @post = Post.new(title: 'New Post', body: 'Body')
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
@post.comments = [@first_comment, @second_comment] @post.comments = [@first_comment, @second_comment]
@first_comment.post = @post @first_comment.post = @post
@second_comment.post = @post @second_comment.post = @post
@post.author = @author
@serializer = PostSerializer.new(@post) @serializer = PostSerializer.new(@post)
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer)

10
test/fixtures/poro.rb vendored
View File

@@ -38,11 +38,13 @@ end
Post = Class.new(Model) Post = Class.new(Model)
Comment = Class.new(Model) Comment = Class.new(Model)
Author = Class.new(Model) Author = Class.new(Model)
Blog = Class.new(Model)
PostSerializer = Class.new(ActiveModel::Serializer) do PostSerializer = Class.new(ActiveModel::Serializer) do
attributes :title, :body, :id attributes :title, :body, :id
has_many :comments has_many :comments
belongs_to :author
url :comments url :comments
end end
@@ -50,6 +52,7 @@ CommentSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :body attributes :id, :body
belongs_to :post belongs_to :post
belongs_to :author
end end
AuthorSerializer = Class.new(ActiveModel::Serializer) do AuthorSerializer = Class.new(ActiveModel::Serializer) do
@@ -57,3 +60,10 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do
has_many :posts, embed: :ids has_many :posts, embed: :ids
end end
BlogSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
belongs_to :writer
has_many :articles
end

View File

@@ -2,7 +2,7 @@ require 'test_helper'
module ActiveModel module ActiveModel
class Serializer class Serializer
class AssocationsTest < Minitest::Test class AssociationsTest < Minitest::Test
class Model class Model
def initialize(hash={}) def initialize(hash={})
@attributes = hash @attributes = hash
@@ -25,30 +25,40 @@ module ActiveModel
def setup def setup
@author = Author.new(name: 'Steve K.')
@post = Post.new({ title: 'New Post', body: 'Body' }) @post = Post.new({ title: 'New Post', body: 'Body' })
@comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' }) @comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
@post.comments = [@comment] @post.comments = [@comment]
@comment.post = @post @comment.post = @post
@comment.author = nil
@post.author = @author
@author.posts = [@post]
@post_serializer = PostSerializer.new(@post) @author_serializer = AuthorSerializer.new(@author)
@comment_serializer = CommentSerializer.new(@comment) @comment_serializer = CommentSerializer.new(@comment)
end end
def test_has_many def test_has_many
assert_equal({comments: {type: :has_many, options: {}}}, @post_serializer.class._associations) assert_equal({posts: {type: :has_many, options: {embed: :ids}}}, @author_serializer.class._associations)
@post_serializer.each_association do |name, serializer, options| @author_serializer.each_association do |name, serializer, options|
assert_equal(:comments, name) assert_equal(:posts, name)
assert_equal({}, options) assert_equal({embed: :ids}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer)
end end
end end
def test_has_one def test_has_one
assert_equal({post: {type: :belongs_to, options: {}}}, @comment_serializer.class._associations) assert_equal({post: {type: :belongs_to, options: {}}, :author=>{:type=>:belongs_to, :options=>{}}}, @comment_serializer.class._associations)
@comment_serializer.each_association do |name, serializer, options| @comment_serializer.each_association do |name, serializer, options|
assert_equal(:post, name) if name == :post
assert_equal({}, options) assert_equal({}, options)
assert_kind_of(PostSerializer, serializer) assert_kind_of(PostSerializer, serializer)
elsif name == :author
assert_equal({}, options)
assert_nil serializer
else
flunk "Unknown association: #{name}"
end
end end
end end
end end