From cf77786da2415d0982ce5f7e9a22d48007cea355 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 21 Jun 2015 02:55:07 -0500 Subject: [PATCH] Fix #955 --- lib/active_model/serializer.rb | 20 ++++++++++++++++---- test/adapter/json/has_many_test.rb | 16 ++++++++++------ test/adapter/json_api/has_many_test.rb | 13 +++++++++++-- test/serializers/associations_test.rb | 4 +++- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 1e59cc23..5a75af42 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -206,10 +206,22 @@ module ActiveModel serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options) if serializer_class - serializer = serializer_class.new( - association_value, - options.except(:serializer).merge(serializer_from_options(association_options)) - ) + begin + serializer = serializer_class.new( + association_value, + options.except(:serializer).merge(serializer_from_options(association_options)) + ) + rescue NoMethodError + # 1. Failure to serialize an element in a collection, e.g. [ {hi: "Steve" } ] will fail + # with NoMethodError when the ArraySerializer finds no serializer for the hash { hi: "Steve" }, + # and tries to call new on that nil. + # 2. Convert association_value to hash using implicit as_json + # 3. Set as virtual value (serializer is nil) + # 4. Consider warning when this happens + virtual_value = association_value + virtual_value = virtual_value.as_json if virtual_value.respond_to?(:as_json) + association_options[:association_options][:virtual_value] = virtual_value + end elsif !association_value.nil? && !association_value.instance_of?(Object) association_options[:association_options][:virtual_value] = association_value end diff --git a/test/adapter/json/has_many_test.rb b/test/adapter/json/has_many_test.rb index 5e10358a..eeff41df 100644 --- a/test/adapter/json/has_many_test.rb +++ b/test/adapter/json/has_many_test.rb @@ -8,7 +8,7 @@ module ActiveModel def setup ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(title: 'New Post', body: 'Body') + @post = Post.new(id: 42, 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] @@ -19,22 +19,26 @@ module ActiveModel @post.blog = @blog @tag = Tag.new(id: 1, name: "#hash_tag") @post.tags = [@tag] - - @serializer = PostSerializer.new(@post) - @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) end def test_has_many + serializer = PostSerializer.new(@post) + adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) assert_equal([ {id: 1, body: 'ZOMG A COMMENT'}, {id: 2, body: 'ZOMG ANOTHER COMMENT'} - ], @adapter.serializable_hash[:post][:comments]) + ], adapter.serializable_hash[:post][:comments]) end def test_has_many_with_no_serializer serializer = PostWithTagsSerializer.new(@post) adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) - assert_includes(adapter.as_json, :tags) + assert_equal({ + id: 42, + tags: [ + {"attributes"=>{"id"=>1, "name"=>"#hash_tag"}} + ] + }, adapter.serializable_hash[:post_with_tags]) end end end diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index 9104e839..5381a080 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -29,7 +29,6 @@ module ActiveModel @post_without_comments.blog = nil @tag = Tag.new(id: 1, name: "#hash_tag") @post.tags = [@tag] - @serializer = PostSerializer.new(@post) @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) end @@ -97,6 +96,7 @@ module ActiveModel serializer = BlogSerializer.new(@blog) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) actual = adapter.serializable_hash[:data][:relationships][:articles] + expected = { data: [{ type: "posts", @@ -109,7 +109,16 @@ module ActiveModel def test_has_many_with_no_serializer serializer = PostWithTagsSerializer.new(@post) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) - assert_includes(adapter.serializable_hash, :tags) + + assert_equal({ + data: { + id: "1", + type: "posts", + relationships: { + tags: {:data=>nil} + } + } + }, adapter.serializable_hash) end end end diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index d87ebae7..bf468931 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -69,7 +69,9 @@ module ActiveModel def test_has_many_with_no_serializer PostWithTagsSerializer.new(@post).each_association do |name, serializer, options| - puts "The line above will crash this test" + assert_equal name, :tags + assert_equal serializer, nil + assert_equal options, {:virtual_value=>[{"attributes"=>{"name"=>"#hashtagged"}}]} end end