Fix infinite recursion

The method for testing whether to include an association was causing
an infinite loop when two models referenced each other.
This commit is contained in:
Gary Gordon 2014-11-07 09:28:10 -05:00
parent 95d122046d
commit d97b2f5005
9 changed files with 54 additions and 13 deletions

View File

@ -83,11 +83,9 @@ module ActiveModel
@top[:linked][plural_name].push attrs unless @top[:linked][plural_name].include? attrs @top[:linked][plural_name].push attrs unless @top[:linked][plural_name].include? attrs
end end
unless serializer.respond_to?(:each) serializer.each_association do |name, association, opts|
serializer.each_association do |name, association, opts| add_linked(name, association, resource_path) if association
add_linked(name, association, resource) if association end if include_nested_assoc? resource_path
end
end
end end
private private
@ -98,8 +96,20 @@ module ActiveModel
attributes attributes
end end
def include_assoc? assoc def include_assoc?(assoc)
@options[:include] && @options[:include].split(',').include?(assoc.to_s) return false unless @options[:include]
check_assoc("#{assoc}$")
end
def include_nested_assoc?(assoc)
return false unless @options[:include]
check_assoc("#{assoc}.")
end
def check_assoc(assoc)
@options[:include].split(',').any? do |s|
s.match(/^#{assoc.gsub('.', '\.')}/)
end
end end
end end
end end

View File

@ -7,8 +7,10 @@ module ActionController
def setup_post def setup_post
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
@author.posts = [] @author.posts = []
@author.bio = nil
@author2 = Author.new(id: 2, name: 'Anonymous') @author2 = Author.new(id: 2, name: 'Anonymous')
@author2.posts = [] @author2.posts = []
@author2.bio = nil
@post = Post.new(id: 1, title: 'New Post', body: 'Body') @post = Post.new(id: 1, 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')

View File

@ -7,6 +7,7 @@ module ActiveModel
class BelongsToTest < Minitest::Test class BelongsToTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
@author.bio = nil
@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!!') @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')

View File

@ -7,6 +7,7 @@ module ActiveModel
class CollectionTest < Minitest::Test class CollectionTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
@author.bio = nil
@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 = []

View File

@ -7,6 +7,7 @@ module ActiveModel
class HasManyEmbedIdsTest < Minitest::Test class HasManyEmbedIdsTest < Minitest::Test
def setup def setup
@author = Author.new(name: 'Steve K.') @author = Author.new(name: 'Steve K.')
@author.bio = nil
@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')
@author.posts = [@first_post, @second_post] @author.posts = [@first_post, @second_post]

View File

@ -8,6 +8,7 @@ module ActiveModel
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
@author.posts = [] @author.posts = []
@author.bio = nil
@post = Post.new(id: 1, title: 'New Post', body: 'Body') @post = Post.new(id: 1, title: 'New Post', body: 'Body')
@post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') @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')

View File

@ -7,6 +7,7 @@ module ActiveModel
class LinkedTest < Minitest::Test class LinkedTest < Minitest::Test
def setup def setup
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
@bio = Bio.new(id: 1, content: 'AMS Contributor')
@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 = []
@ -14,9 +15,11 @@ module ActiveModel
@first_post.author = @author @first_post.author = @author
@second_post.author = @author @second_post.author = @author
@author.posts = [@first_post, @second_post] @author.posts = [@first_post, @second_post]
@author.bio = @bio
@bio.author = @author
@serializer = ArraySerializer.new([@first_post, @second_post]) @serializer = ArraySerializer.new([@first_post, @second_post])
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,comments') @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'author,author.bio,comments')
end end
def test_include_multiple_posts_and_linked def test_include_multiple_posts_and_linked
@ -31,7 +34,10 @@ module ActiveModel
{ title: "Hello!!", body: "Hello, world!!", id: "1", links: { comments: ['1', '2'], author: "1" } }, { 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 => "1" } }
], @adapter.serializable_hash[:posts]) ], @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]) assert_equal({ :comments => [{ :id => "1", :body => "ZOMG A COMMENT" },
{ :id => "2", :body => "ZOMG ANOTHER COMMENT" }],
:authors => [{ :id => "1", :name => "Steve K." }],
:bios=>[{:id=>"1", :content=>"AMS Contributor"}] }, @adapter.serializable_hash[:linked])
end end
end end
end end

View File

@ -38,6 +38,7 @@ 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)
Bio = Class.new(Model)
Blog = Class.new(Model) Blog = Class.new(Model)
PostSerializer = Class.new(ActiveModel::Serializer) do PostSerializer = Class.new(ActiveModel::Serializer) do
@ -59,6 +60,13 @@ AuthorSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name attributes :id, :name
has_many :posts, embed: :ids has_many :posts, embed: :ids
belongs_to :bio
end
BioSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :content
belongs_to :author
end end
BlogSerializer = Class.new(ActiveModel::Serializer) do BlogSerializer = Class.new(ActiveModel::Serializer) do

View File

@ -26,6 +26,7 @@ module ActiveModel
def setup def setup
@author = Author.new(name: 'Steve K.') @author = Author.new(name: 'Steve K.')
@author.bio = nil
@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]
@ -39,11 +40,21 @@ module ActiveModel
end end
def test_has_many def test_has_many
assert_equal({posts: {type: :has_many, options: {embed: :ids}}}, @author_serializer.class._associations) assert_equal(
{ posts: { type: :has_many, options: { embed: :ids } },
bio: { type: :belongs_to, options: {} } },
@author_serializer.class._associations
)
@author_serializer.each_association do |name, serializer, options| @author_serializer.each_association do |name, serializer, options|
assert_equal(:posts, name) if name == :posts
assert_equal({embed: :ids}, options) assert_equal({embed: :ids}, options)
assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer)
elsif name == :bio
assert_equal({}, options)
assert_nil serializer
else
flunk "Unknown association: #{name}"
end
end end
end end