diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 21e73358..af2ec277 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -108,6 +108,18 @@ module ActiveModel # end # class Serializer + class IncludeError < StandardError + attr_reader :source, :association + + def initialize(source, association) + @source, @association = source, association + end + + def to_s + "Cannot serialize #{association} when #{source} does not have a root!" + end + end + module Associations #:nodoc: class Config #:nodoc: class_attribute :options @@ -502,7 +514,9 @@ module ActiveModel if association.embed_ids? node[association.key] = association.serialize_ids - if association.embed_in_root? + if association.embed_in_root? && hash.nil? + raise IncludeError.new(self.class, association.name) + elsif association.embed_in_root? merge_association hash, association.root, association.serialize_many, unique_values end elsif association.embed_objects? diff --git a/test/serializer_test.rb b/test/serializer_test.rb index 60bb123e..3e269be8 100644 --- a/test/serializer_test.rb +++ b/test/serializer_test.rb @@ -1169,4 +1169,79 @@ class SerializerTest < ActiveModel::TestCase } }, actual) end + + def test_raises_an_error_when_a_child_serializer_includes_associations_when_the_source_doesnt + attachment_serializer = Class.new(ActiveModel::Serializer) do + attributes :name + end + + fruit_serializer = Class.new(ActiveModel::Serializer) do + embed :ids, :include => true + has_one :attachment, :serializer => attachment_serializer + attribute :color + end + + banana_class = Class.new Model do + def self.to_s + 'banana' + end + + def attachment + @attributes[:attachment] + end + + define_method :active_model_serializer do + fruit_serializer + end + end + + strawberry_class = Class.new Model do + def self.to_s + 'strawberry' + end + + def attachment + @attributes[:attachment] + end + + define_method :active_model_serializer do + fruit_serializer + end + end + + smoothie = Class.new do + attr_reader :base, :flavor + + def initialize(base, flavor) + @base, @flavor = base, flavor + end + end + + smoothie_serializer = Class.new(ActiveModel::Serializer) do + root false + embed :ids, :include => true + + has_one :base, :polymorphic => true + has_one :flavor, :polymorphic => true + end + + banana_attachment = Attachment.new({ + :name => 'banana_blending.md', + :id => 3, + }) + + strawberry_attachment = Attachment.new({ + :name => 'strawberry_cleaning.doc', + :id => 4 + }) + + banana = banana_class.new :color => "yellow", :id => 1, :attachment => banana_attachment + strawberry = strawberry_class.new :color => "red", :id => 2, :attachment => strawberry_attachment + + smoothie = smoothie_serializer.new(smoothie.new(banana, strawberry)) + + assert_raise ActiveModel::Serializer::IncludeError do + smoothie.as_json + end + end end