require 'spec_helper' require 'support/models' describe "EmbeddedDocument" do before do @klass = Doc('Person') do key :name, String end @pet_klass = EDoc('Pet') do key :name, String key :flag, Boolean end @klass.many :pets, :class => @pet_klass @address_class = EDoc('Address') do key :city, String key :state, String end end context "Saving a document with a key that is an embedded document" do before do @klass.key :foo, @address_class end it "should embed embedded document" do address = @address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.create(:foo => address) doc.foo.city.should == 'South Bend' doc.foo.state.should == 'IN' doc = doc.reload doc.foo.city.should == 'South Bend' doc.foo.state.should == 'IN' end it "should assign _parent_document and _root_document" do address = @address_class.new(:city => 'South Bend', :state => 'IN') address._parent_document.should be_nil doc = @klass.create(:foo => address) address._parent_document.should be(doc) address._root_document.should be(doc) end it "should assign _parent_document and _root_document when loading" do address = @address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.create(:foo => address) doc.reload doc.foo._parent_document.should be(doc) doc.foo._root_document.should be(doc) end context "given subclass of embedded document" do before do @sub_address_class = Subclass(@address_class, 'SubAddress') end it "should embed embedded document" do address = @sub_address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.create(:foo => address) doc.foo.city.should == 'South Bend' doc.foo.state.should == 'IN' doc = doc.reload doc.foo.city.should == 'South Bend' doc.foo.state.should == 'IN' end it "should assign _parent_document and _root_document" do address = @sub_address_class.new(:city => 'South Bend', :state => 'IN') address._parent_document.should be_nil doc = @klass.create(:foo => address) address._parent_document.should be(doc) address._root_document.should be(doc) end it "should assign _parent_document and _root_document when loading" do address = @sub_address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.create(:foo => address) doc.reload doc.foo._parent_document.should be(doc) doc.foo._root_document.should be(doc) end end end it "should correctly instantiate single collection inherited embedded documents" do document = Doc('Foo') do key :message, Message end doc1 = document.create(:message => Enter.new) doc1.reload.message.class.should be(Enter) end context "new? (embedded key)" do before do @klass.key :foo, @address_class end it "should be true until document is created" do address = @address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.new(:foo => address) address.new?.should be_truthy end it "should be false after document is saved" do address = @address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.new(:foo => address) doc.save doc.foo.new?.should be_falsey end it "should be false when loaded from database" do address = @address_class.new(:city => 'South Bend', :state => 'IN') doc = @klass.new(:foo => address) doc.save doc.reload doc.foo.new?.should be_falsey end end context "new? (embedded many association)" do before do @doc = @klass.new(:pets => [{:name => 'poo bear'}]) end it "should be true until document is saved" do @doc.should be_new @doc.pets.first.should be_new end it "should be false after document is saved" do @doc.save @doc.pets.first.should_not be_new end it "should be false when loaded from database" do @doc.save @doc.pets.first.should_not be_new @doc.reload @doc.pets.first.should_not be_new end it "should be true until existing document is saved" do @doc.save pet = @doc.pets.build(:name => 'Rasmus') pet.new?.should be_truthy @doc.save pet.new?.should be_falsey end end context "new? (nested embedded many association)" do before do @pet_klass.many :addresses, :class=> @address_class @doc = @klass.new @doc.pets.build(:name => 'Rasmus') @doc.save end it "should be true until existing document is saved" do address = @doc.pets.first.addresses.build(:city => 'Holland', :state => 'MI') address.new?.should be_truthy @doc.save address.new?.should be_falsey end end context "new? (embedded one association)" do before do @klass.one :address, :class => @address_class @doc = @klass.new end it "should be true until existing document is saved" do @doc.save @doc.build_address(:city => 'Holland', :state => 'MI') @doc.address.new?.should be_truthy @doc.save @doc.address.new?.should be_falsey end end context "new? (nested embedded one association)" do before do @pet_klass.one :address, :class => @address_class @doc = @klass.new @doc.pets.build(:name => 'Rasmus') @doc.save end it "should be true until existing document is saved" do address = @doc.pets.first.build_address(:city => 'Holland', :stats => 'MI') address.new?.should be_truthy @doc.save address.new?.should be_falsey end end context "#destroyed?" do before do @doc = @klass.create(:pets => [@pet_klass.new(:name => 'sparky')]) end it "should be false if root document is not destroyed" do @doc.should_not be_destroyed @doc.pets.first.should_not be_destroyed end it "should be true if root document is destroyed" do @doc.destroy @doc.should be_destroyed @doc.pets.first.should be_destroyed end end context "#persisted?" do before do @doc = @klass.new(:name => 'persisted doc', :pets => [@pet_klass.new(:name => 'persisted pet')]) end it "should be false if new" do @doc.pets.first.should_not be_persisted end it "should be false if destroyed" do @doc.save @doc.destroy @doc.pets.first.should be_destroyed @doc.pets.first.should_not be_persisted end it "should be true if not new or destroyed" do @doc.save @doc.pets.first.should be_persisted end end it "should be able to save" do person = @klass.create pet = @pet_klass.new(:name => 'sparky') person.pets << pet pet.should be_new pet.save pet.should_not be_new person.reload person.pets.first.should == pet end it "should be able to save!" do person = @klass.create pet = @pet_klass.new(:name => 'sparky') person.pets << pet pet.should be_new person.should_receive(:save!) pet.save! end it "should be able to dynamically add new keys and save" do person = @klass.create pet = @pet_klass.new(:name => 'sparky', :crazy_key => 'crazy') person.pets << pet pet.save person.reload person.pets.first.crazy_key.should == 'crazy' end it "should be able to update_attribute" do pet = @pet_klass.new(:name => 'sparky') person = @klass.create(:pets => [pet]) person.reload pet = person.pets.first pet.update_attribute('name', 'koda').should be_truthy person.reload person.pets.first._id.should == pet._id person.pets.first.name.should == 'koda' end it "should be able to update_attributes" do pet = @pet_klass.new(:name => 'sparky') person = @klass.create(:pets => [pet]) person.reload pet = person.pets.first pet.update_attributes(:name => 'koda').should be_truthy person.reload person.pets.first._id.should == pet._id person.pets.first.name.should == 'koda' end it "should be able to update_attributes!" do person = @klass.create(:pets => [@pet_klass.new(:name => 'sparky')]) person.reload pet = person.pets.first attributes = {:name => 'koda'} pet.should_receive(:attributes=).with(attributes) pet.should_receive(:save!) pet.update_attributes!(attributes) end it "should have database instance method that is equal to root document" do person = @klass.create(:pets => [@pet_klass.new(:name => 'sparky')]) person.pets.first.database.should == person.database end it "should have collection instance method that is equal to root document" do person = @klass.create(:pets => [@pet_klass.new(:name => 'sparky')]) person.pets.first.collection.name.should == person.collection.name end it "should not fail if the source document contains nils in the embedded document list" do @klass.collection.insert_one(:pets => [nil, {:name => "Sasha"}]) lambda { @klass.all.first.pets }.should_not raise_error @klass.all.first.pets.tap do |pets| pets.length.should == 1 pets[0].name.should == "Sasha" end end context "Issue #536" do it "should update attributes with string keys" do person = @klass.create(:pets => [@pet_klass.new(:name => 'Rasmus', :flag => true)]) person.update_attributes!({"pets" => ["name" => "sparky", "flag" => "false"]}) person.reload person.pets.first.name.should == "sparky" person.pets.first.flag.should be_falsey end it "should update attributes with symbol keys" do person = @klass.create(:pets => [@pet_klass.new(:name => 'Rasmus', :flag => true)]) person.update_attributes!({:pets => [:name => "sparky", :flag => "false"]}) person.reload person.pets.first.name.should == "sparky" person.pets.first.flag.should be_falsey end end end