Permalink
Browse files

:dependent specifies behavior for parent destruction _and_ associatio…

…n replacement for both one and many associations (default is nullify in both cases)
  • Loading branch information...
1 parent 2e8b4c7 commit 6caa8a6fcd6a713b36771e1e683f7a6f69774dab @brianhempel brianhempel committed Jun 1, 2011
View
@@ -1,6 +1,8 @@
0.9 => 1.0
* Using String IDs are no longer supported. If you are declaring your own ID, ensure it is an ObjectId, and set the default
key :_id, ObjectId, :default => lambda { BSON::ObjectId.new }
+ * The :dependent association option now applies to both when the parent is destroyed and when the association is reassigned (one and many associations)
+ * The default :dependent option is now :nullify for both when the parent is destroyed and when the association is reassigned
0.8.6 => 0.9
* [attribute]_before_typecast should become [attribute]_before_type_cast (note the extra _)
@@ -41,11 +41,11 @@ def #{name}=(value)
end
end_eval
- if options[:dependent] && !embeddable?
- association = self
- options = self.options
+ association = self
+ options = self.options
- model.after_destroy do
+ model.after_destroy do
+ if !association.embeddable?
case options[:dependent]
when :destroy
self.get_proxy(association).destroy_all
@@ -8,7 +8,15 @@ class ManyDocumentsProxy < Collection
def replace(docs)
load_target
- target.each { |t| t.destroy }
+
+ (target - docs).each do |t|
+ case options[:dependent]
+ when :destroy then t.destroy
+ when :delete_all then t.delete
+ else t.update_attributes(self.foreign_key => nil)
+ end
+ end
+
docs.each { |doc| prepare(doc).save }
reset
end
@@ -10,6 +10,27 @@ def embeddable?
def proxy_class
@proxy_class ||= klass.embeddable? ? OneEmbeddedProxy : OneProxy
end
+
+ def setup(model)
+ super
+
+ association = self
+ options = self.options
+
+ model.after_destroy do
+ if !association.embeddable?
+ proxy = self.get_proxy(association)
+
+ unless proxy.nil?
+ case options[:dependent]
+ when :destroy then proxy.destroy
+ when :delete then proxy.delete
+ else proxy.nullify
+ end
+ end
+ end
+ end
+ end
def autosave?
options.fetch(:autosave, embeddable?)
@@ -19,29 +19,41 @@ def replace(doc)
load_target
if !target.nil? && target != doc
- if options[:dependent] && target.persisted?
+ if target.persisted?
case options[:dependent]
- when :delete
- target.delete
- when :destroy
- target.destroy
- when :nullify
+ when :delete then target.delete
+ when :destroy then target.destroy
+ else
target[foreign_key] = nil
target.save
end
end
end
-
- if doc.nil?
- target.update_attributes(foreign_key => nil) unless target.nil?
- else
+
+ unless doc.nil?
proxy_owner.save unless proxy_owner.persisted?
doc = klass.new(doc) unless doc.is_a?(klass)
doc[foreign_key] = proxy_owner.id
doc.save unless doc.persisted?
- loaded
- @target = doc
end
+
+ loaded
+ @target = doc
+ end
+
+ def destroy
+ target.destroy
+ reset
+ end
+
+ def delete
+ target.delete
+ reset
+ end
+
+ def nullify
+ target.update_attributes(foreign_key => nil)
+ reset
end
protected
@@ -93,6 +93,144 @@ def pets
project.statuses[0].name.should == "ready"
end
end
+
+ context "with :dependent" do
+ setup do
+ @broker_class = Doc('Broker')
+ @property_class = Doc('Property') do
+ key :broker_id, ObjectId
+ belongs_to :broker
+ end
+ end
+
+ context "=> destroy" do
+ setup do
+ @broker_class.many :properties, :class => @property_class, :dependent => :destroy
+
+ @broker = @broker_class.create(:name => "Bob")
+ @property1 = @property_class.create
+ @property2 = @property_class.create
+ @property3 = @property_class.create
+ @broker.properties << @property1
+ @broker.properties << @property2
+ @broker.properties << @property3
+ end
+
+ should "call destroy the existing documents" do
+ @broker.properties[0].expects(:destroy).once
+ @broker.properties[1].expects(:destroy).once
+ @broker.properties[2].expects(:destroy).once
+ @broker.properties = [@property_class.new]
+ end
+
+ should "remove the existing document from the database" do
+ @property_class.count.should == 3
+ @broker.properties = []
+ @property_class.count.should == 0
+ end
+
+ should "skip over documents that are the same" do
+ @broker.properties[0].expects(:destroy).never
+ @broker.properties[1].expects(:destroy).once
+ @broker.properties[2].expects(:destroy).never
+ @broker.properties = [@property3, @property1]
+ end
+ end
+
+ context "=> delete_all" do
+ setup do
+ @broker_class.many :properties, :class => @property_class, :dependent => :delete_all
+
+ @broker = @broker_class.create(:name => "Bob")
+ @property1 = @property_class.create
+ @property2 = @property_class.create
+ @property3 = @property_class.create
+ @broker.properties << @property1
+ @broker.properties << @property2
+ @broker.properties << @property3
+ end
+
+ should "call delete the existing documents" do
+ @broker.properties[0].expects(:delete).once
+ @broker.properties[1].expects(:delete).once
+ @broker.properties[2].expects(:delete).once
+ @broker.properties = [@property_class.new]
+ end
+
+ should "remove the existing document from the database" do
+ @property_class.count.should == 3
+ @broker.properties = []
+ @property_class.count.should == 0
+ end
+
+ should "skip over documents that are the same" do
+ @broker.properties[0].expects(:delete).never
+ @broker.properties[1].expects(:delete).once
+ @broker.properties[2].expects(:delete).never
+ @broker.properties = [@property3, @property1]
+ end
+ end
+
+ context "=> nullify" do
+ setup do
+ @broker_class.many :properties, :class => @property_class, :dependent => :nullify
+
+ @broker = @broker_class.create(:name => "Bob")
+ @property1 = @property_class.create
+ @property2 = @property_class.create
+ @property3 = @property_class.create
+ @broker.properties << @property1
+ @broker.properties << @property2
+ @broker.properties << @property3
+ end
+
+ should "nullify the existing documents" do
+ @property1.reload.broker_id.should == @broker.id
+ @property2.reload.broker_id.should == @broker.id
+ @property3.reload.broker_id.should == @broker.id
+
+ @broker.properties = [@property_class.new]
+
+ @property1.reload.broker_id.should be_nil
+ @property2.reload.broker_id.should be_nil
+ @property3.reload.broker_id.should be_nil
+ end
+
+ should "skip over documents that are the same" do
+ @broker.properties = [@property3, @property1]
+
+ @property1.reload.broker_id.should == @broker.id
+ @property2.reload.broker_id.should be_nil
+ @property3.reload.broker_id.should == @broker.id
+ end
+
+ should "work" do
+ old_properties = @broker.properties
+ @broker.properties = [@property1, @property2, @property3]
+ old_properties.should == @broker.properties
+ end
+ end
+
+ context "unspecified" do
+ should "nullify the existing documents" do
+ @broker_class.many :properties, :class => @property_class
+
+ @broker = @broker_class.create(:name => "Bob")
+ @property1 = @property_class.create
+ @property2 = @property_class.create
+ @property3 = @property_class.create
+ @broker.properties << @property1
+ @broker.properties << @property2
+ @broker.properties << @property3
+
+ @broker.properties = [@property_class.new]
+
+ @property1.reload.broker_id.should be_nil
+ @property2.reload.broker_id.should be_nil
+ @property3.reload.broker_id.should be_nil
+ end
+ end
+ end
end
context "using <<, push and concat" do
@@ -624,6 +762,29 @@ class ::Thing
Property.count.should == 3
end
end
+
+ context "unspecified" do
+ setup do
+ Property.key :thing_id, ObjectId
+ Property.belongs_to :thing
+ Thing.has_many :properties, :dependent => :nullify
+
+ @thing = Thing.create(:name => "Tree")
+ @property1 = Property.create
+ @property2 = Property.create
+ @property3 = Property.create
+ @thing.properties << @property1
+ @thing.properties << @property2
+ @thing.properties << @property3
+ end
+
+ should "should nullify relationship but not destroy associated documents" do
+ @thing.properties.count.should == 3
+ @thing.destroy
+ @thing.properties.count.should == 0
+ Property.count.should == 3
+ end
+ end
end
context "namespaced foreign keys" do
Oops, something went wrong.

0 comments on commit 6caa8a6

Please sign in to comment.