Bugfix: include nested has_many association

Currently, doing `include: author.bio` would work correctly, but not for
has_many associations such as `include: author.roles`. This fixes it.

The problem was basically that we were not handling arrays for has_many linked,
as happens for ArraySerializers.
This commit is contained in:
Alexandre de Oliveira 2014-11-11 14:35:00 -02:00
parent 33e8d09ad0
commit 971f501e55
6 changed files with 59 additions and 5 deletions

View File

@ -77,10 +77,13 @@ module ActiveModel
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)
attrs = [attributes_for_serializer(serializer, @options)].flatten
@top[:linked] ||= {}
@top[:linked][plural_name] ||= []
@top[:linked][plural_name].push attrs unless @top[:linked][plural_name].include? attrs
attrs.each do |attrs|
@top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs)
end
end
serializer.each_association do |name, association, opts|
@ -91,9 +94,19 @@ module ActiveModel
private
def attributes_for_serializer(serializer, options)
attributes = serializer.attributes(options)
attributes[:id] = attributes[:id].to_s if attributes[:id]
attributes
if serializer.respond_to?(:each)
result = []
serializer.each do |object|
attributes = object.attributes(options)
attributes[:id] = attributes[:id].to_s if attributes[:id]
result << attributes
end
else
result = serializer.attributes(options)
result[:id] = result[:id].to_s if result[:id]
end
result
end
def include_assoc?(assoc)

View File

@ -5,12 +5,15 @@ module ActionController
class JsonApiLinkedTest < ActionController::TestCase
class MyController < ActionController::Base
def setup_post
@role1 = Role.new(id: 1, name: 'admin')
@author = Author.new(id: 1, name: 'Steve K.')
@author.posts = []
@author.bio = nil
@author.roles = [@role1]
@author2 = Author.new(id: 2, name: 'Anonymous')
@author2.posts = []
@author2.bio = nil
@author2.roles = []
@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')
@ -51,6 +54,13 @@ module ActionController
end
end
def render_resource_with_nested_has_many_include
with_json_api_adapter do
setup_post
render json: @post, include: 'author,author.roles'
end
end
def render_collection_without_include
with_json_api_adapter do
setup_post
@ -82,6 +92,22 @@ module ActionController
assert_equal 'Steve K.', response['linked']['authors'].first['name']
end
def test_render_resource_with_nested_has_many_include
get :render_resource_with_nested_has_many_include
response = JSON.parse(@response.body)
expected_linked = {
"authors" => [{
"id" => "1",
"name" => "Steve K."
}],
"roles"=>[{
"id" => "1",
"name" => "admin"
}]
}
assert_equal expected_linked, response['linked']
end
def test_render_resource_with_nested_include
get :render_resource_with_nested_include
response = JSON.parse(@response.body)

View File

@ -8,6 +8,7 @@ module ActiveModel
def setup
@author = Author.new(name: 'Steve K.')
@author.bio = nil
@author.roles = nil
@first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!')
@second_post = Post.new(id: 2, title: 'New Post', body: 'Body')
@author.posts = [@first_post, @second_post]

View File

@ -16,6 +16,7 @@ module ActiveModel
@second_post.author = @author
@author.posts = [@first_post, @second_post]
@author.bio = @bio
@author.roles = []
@bio.author = @author
@serializer = ArraySerializer.new([@first_post, @second_post])

View File

@ -40,6 +40,7 @@ Comment = Class.new(Model)
Author = Class.new(Model)
Bio = Class.new(Model)
Blog = Class.new(Model)
Role = Class.new(Model)
PostSerializer = Class.new(ActiveModel::Serializer) do
attributes :title, :body, :id
@ -60,9 +61,16 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
has_many :posts, embed: :ids
has_many :roles, embed: :ids
belongs_to :bio
end
RoleSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
belongs_to :author
end
BioSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :content

View File

@ -27,6 +27,7 @@ module ActiveModel
def setup
@author = Author.new(name: 'Steve K.')
@author.bio = nil
@author.roles = []
@post = Post.new({ title: 'New Post', body: 'Body' })
@comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
@post.comments = [@comment]
@ -42,6 +43,7 @@ module ActiveModel
def test_has_many
assert_equal(
{ posts: { type: :has_many, options: { embed: :ids } },
roles: { type: :has_many, options: { embed: :ids } },
bio: { type: :belongs_to, options: {} } },
@author_serializer.class._associations
)
@ -52,6 +54,9 @@ module ActiveModel
elsif name == :bio
assert_equal({}, options)
assert_nil serializer
elsif name == :roles
assert_equal({embed: :ids}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer)
else
flunk "Unknown association: #{name}"
end