From 2f5626d616c3a4a59eaacdf41390fe31e368d9b7 Mon Sep 17 00:00:00 2001 From: Alexandre de Oliveira Date: Thu, 13 Nov 2014 16:38:39 -0200 Subject: [PATCH] Fixes nested has_many links in JSONAPI When linked resource had has_many links, all of them would use the association from the first resource, causing all of the items to build `links` with the same associations. This fixes it by iterating over the serializers, not just the attributes array. --- .../serializer/adapter/json_api.rb | 21 +++-- test/adapter/json_api/linked_test.rb | 92 ++++++++++++++++--- 2 files changed, 92 insertions(+), 21 deletions(-) diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 587485e5..f02152c7 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -58,25 +58,30 @@ module ActiveModel end end - def add_linked(resource_name, serializer, parent = nil) + def add_linked(resource_name, serializers, parent = nil) + serializers = Array(serializers) unless serializers.respond_to?(:each) + resource_path = [parent, resource_name].compact.join('.') if include_assoc?(resource_path) - plural_name = serialized_object_type(serializer).pluralize.to_sym - attrs = [attributes_for_serializer(serializer, @options)].flatten + plural_name = serialized_object_type(serializers).pluralize.to_sym @top[:linked] ||= {} @top[:linked][plural_name] ||= [] - attrs.each do |attrs| + serializers.each do |serializer| + attrs = attributes_for_serializer(serializer, @options) + add_resource_links(attrs, serializer, add_linked: false) @top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs) end end - serializer.each_association do |name, association, opts| - add_linked(name, association, resource_path) if association - end if include_nested_assoc? resource_path + serializers.each do |serializer| + serializer.each_association do |name, association, opts| + add_linked(name, association, resource_path) if association + end if include_nested_assoc? resource_path + end end def attributes_for_serializer(serializer, options) @@ -124,7 +129,7 @@ module ActiveModel def add_resource_links(attrs, serializer, options = {}) options[:add_linked] = options.fetch(:add_linked, true) - Array(serializer).first.each_association do |name, association, opts| + serializer.each_association do |name, association, opts| attrs[:links] ||= {} if association.respond_to?(:each) diff --git a/test/adapter/json_api/linked_test.rb b/test/adapter/json_api/linked_test.rb index d25cfa06..8e896460 100644 --- a/test/adapter/json_api/linked_test.rb +++ b/test/adapter/json_api/linked_test.rb @@ -6,24 +6,32 @@ module ActiveModel class JsonApi class LinkedTest < Minitest::Test def setup - @author = Author.new(id: 1, name: 'Steve K.') - @bio = Bio.new(id: 1, content: 'AMS Contributor') + @author1 = Author.new(id: 1, name: 'Steve K.') + @author2 = Author.new(id: 2, name: 'Tenderlove') + @bio1 = Bio.new(id: 1, content: 'AMS Contributor') + @bio2 = Bio.new(id: 2, content: 'Rails Contributor') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') + @third_post = Post.new(id: 3, title: 'Yet Another Post', body: 'Body') @first_post.comments = [] @second_post.comments = [] - @first_post.author = @author - @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]) - @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,author.bio,comments') + @first_post.author = @author1 + @second_post.author = @author2 + @third_post.author = @author1 + @author1.posts = [@first_post, @third_post] + @author1.bio = @bio1 + @author1.roles = [] + @author2.posts = [@second_post] + @author2.bio = @bio2 + @author2.roles = [] + @bio1.author = @author1 + @bio2.author = @author2 end def test_include_multiple_posts_and_linked + @serializer = ArraySerializer.new([@first_post, @second_post]) + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,author.bio,comments') + @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] @@ -33,7 +41,7 @@ module ActiveModel @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" } } + { title: "New Post", body: "Body", id: "2", links: { comments: [], :author => "2" } } ], @adapter.serializable_hash[:posts]) @@ -57,10 +65,18 @@ module ActiveModel id: "1", name: "Steve K.", links: { - posts: ["1", "2"], + posts: ["1"], roles: [], bio: "1" } + }, { + id: "2", + name: "Tenderlove", + links: { + posts: ["2"], + roles: [], + bio: "2" + } }], bios: [{ id: "1", @@ -68,6 +84,56 @@ module ActiveModel links: { author: "1" } + }, { + id: "2", + content: "Rails Contributor", + links: { + author: "2" + } + }] + } + assert_equal expected, @adapter.serializable_hash[:linked] + end + + def test_include_multiple_posts_and_linked + @serializer = BioSerializer.new(@bio1) + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,author.posts') + + @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] + @third_post.comments = [] + @first_comment.post = @first_post + @first_comment.author = nil + @second_comment.post = @first_post + @second_comment.author = nil + + expected = { + authors: [{ + id: "1", + name: "Steve K.", + links: { + posts: ["1", "3"], + roles: [], + bio: "1" + } + }], + posts: [{ + title: "Hello!!", + body: "Hello, world!!", + id: "1", + links: { + comments: ["1", "2"], + author: "1" + } + }, { + title: "Yet Another Post", + body: "Body", + id: "3", + links: { + comments: [], + author: "1" + } }] } assert_equal expected, @adapter.serializable_hash[:linked]