diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index 415167ef..acafc0fd 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -39,17 +39,18 @@ module ActionController options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] } if use_adapter? && (serializer = get_serializer(resource)) - @_serializer_opts[:scope] ||= serialization_scope @_serializer_opts[:scope_name] = _serialization_scope - # omg hax - object = serializer.new(resource, @_serializer_opts) - adapter = ActiveModel::Serializer::Adapter.create(object, @_adapter_opts) - super(adapter, options) - else - super(resource, options) + begin + serialized = serializer.new(resource, @_serializer_opts) + rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError + else + resource = ActiveModel::Serializer::Adapter.create(serialized, @_adapter_opts) + end end + + super(resource, options) end end diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 1e59cc23..7e4eb0d6 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -206,10 +206,16 @@ 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 ActiveModel::Serializer::ArraySerializer::NoSerializerError + 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/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb index 174e16fc..fa66e290 100644 --- a/lib/active_model/serializer/array_serializer.rb +++ b/lib/active_model/serializer/array_serializer.rb @@ -1,6 +1,7 @@ module ActiveModel class Serializer class ArraySerializer + NoSerializerError = Class.new(StandardError) include Enumerable delegate :each, to: :@objects @@ -13,7 +14,12 @@ module ActiveModel :serializer, ActiveModel::Serializer.serializer_for(object) ) - serializer_class.new(object, options.except(:serializer)) + + if serializer_class.nil? + fail NoSerializerError, "No serializer found for object: #{object.inspect}" + else + serializer_class.new(object, options.except(:serializer)) + end end @meta = options[:meta] @meta_key = options[:meta_key] diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index 08b32035..a8c8f827 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -47,6 +47,14 @@ module ActionController render json: @post end + def render_json_object_without_serializer + render json: {error: 'Result is Invalid'} + end + + def render_json_array_object_without_serializer + render json: [{error: 'Result is Invalid'}] + end + def update_and_render_object_with_cache_enabled @post.updated_at = DateTime.now @@ -160,6 +168,20 @@ module ActionController assert_equal expected.to_json, @response.body end + def test_render_json_object_without_serializer + get :render_json_object_without_serializer + + assert_equal 'application/json', @response.content_type + assert_equal ({error: 'Result is Invalid'}).to_json, @response.body + end + + def test_render_json_array_object_without_serializer + get :render_json_array_object_without_serializer + + assert_equal 'application/json', @response.content_type + assert_equal ([{error: 'Result is Invalid'}]).to_json, @response.body + end + def test_render_array_using_implicit_serializer get :render_array_using_implicit_serializer assert_equal 'application/json', @response.content_type diff --git a/test/adapter/json/has_many_test.rb b/test/adapter/json/has_many_test.rb index 19fe16cd..a88b2f54 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] @@ -17,20 +17,31 @@ module ActiveModel @second_comment.post = @post @blog = Blog.new(id: 1, name: "My Blog!!") @post.blog = @blog - - @serializer = PostSerializer.new(@post) - @adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer) + @tag = Tag.new(id: 1, name: "#hash_tag") + @post.tags = [@tag] 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_equal({ + id: 42, + tags: [ + {"attributes"=>{"id"=>1, "name"=>"#hash_tag"}} + ] + }.to_json, adapter.serializable_hash[:post_with_tags].to_json) end end end end end end - diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index a544fc80..3cef3ef8 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -27,7 +27,8 @@ module ActiveModel @blog.articles = [@post] @post.blog = @blog @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 @@ -95,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", @@ -103,6 +105,21 @@ module ActiveModel } assert_equal expected, actual end + + def test_has_many_with_no_serializer + serializer = PostWithTagsSerializer.new(@post) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + + assert_equal({ + data: { + id: "1", + type: "posts", + relationships: { + tags: { data: nil } + } + } + }, adapter.serializable_hash) + end end end end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index ee1913ec..4f0b513a 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -76,6 +76,7 @@ Role = Class.new(Model) User = Class.new(Model) Location = Class.new(Model) Place = Class.new(Model) +Tag = Class.new(Model) Comment = Class.new(Model) do # Uses a custom non-time-based cache key def cache_key @@ -224,6 +225,12 @@ PostPreviewSerializer = Class.new(ActiveModel::Serializer) do belongs_to :author, serializer: AuthorPreviewSerializer end +PostWithTagsSerializer = Class.new(ActiveModel::Serializer) do + attributes :id + + has_many :tags +end + Spam::UnrelatedLinkSerializer = Class.new(ActiveModel::Serializer) do attributes :id end diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index ab481de7..04681d2c 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -29,8 +29,10 @@ module ActiveModel @author.roles = [] @blog = Blog.new({ name: 'AMS Blog' }) @post = Post.new({ title: 'New Post', body: 'Body' }) + @tag = Tag.new({name: '#hashtagged'}) @comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' }) @post.comments = [@comment] + @post.tags = [@tag] @post.blog = @blog @comment.post = @post @comment.author = nil @@ -65,6 +67,14 @@ module ActiveModel end end + def test_has_many_with_no_serializer + PostWithTagsSerializer.new(@post).each_association do |name, serializer, options| + assert_equal name, :tags + assert_equal serializer, nil + assert_equal [{ attributes: { name: "#hashtagged" }}].to_json, options[:virtual_value].to_json + end + end + def test_serializer_options_are_passed_into_associations_serializers @post_serializer.each_association do |name, association| if name == :comments