| |   |
| 364 | 364 | # If it's belongs_to relationships haven't been saved |
| 365 | 365 | # then it has be done first. |
| 366 | 366 | # Saving the master will also automatically save the child |
| 367 | | |
| 368 | 367 | performed_save = false |
| 368 | |
| 369 | # If the belongs_to relationships haven't already been loaded, |
| 370 | # there is reason to believe that: |
| 371 | # * The object is new and doesn't have any relation set |
| 372 | # * The object already knows about it's belongs_to relations |
| 373 | # and doesn't need aditional information about them for saving |
| 374 | # We therefore deactivate the loading of belongs_to relations |
| 375 | original_state_of_load_belongs_to_relations = \ |
| 376 | @do_not_load_belongs_to_relations |
| 377 | @do_not_load_belongs_to_relations = true |
| 378 | |
| 369 | 379 | belongs_to.each do |what_it_belongs_to| |
| 370 | 380 | master_class = self.send(what_it_belongs_to) |
| 371 | 381 | unless master_class == nil || !master_class.new? |
| … | … | |
| 384 | 384 | end |
| 385 | 385 | end |
| 386 | 386 | |
| 387 | # Reset the do_not_load_belongs_to_relations variable to its |
| 388 | # original state |
| 389 | @do_not_load_belongs_to_relations = \ |
| 390 | original_state_of_load_belongs_to_relations |
| 391 | |
| 387 | 392 | # If none of the master classes were saved, meaning they weren't new |
| 388 | 393 | # or didn't exist, then we have to manually save this object. |
| 389 | 394 | couch_perform_save unless performed_save |
| … | … | |
| 680 | 680 | "@id", |
| 681 | 681 | "@revision", |
| 682 | 682 | "@do_not_load_has_many_relations", |
| 683 | "@do_not_load_belongs_to_relations", |
| 683 | 684 | "@couch_object_original_state", |
| 684 | 685 | "@couch_initial_load", |
| 685 | 686 | "@belongs_to"] |
| … | … | |
| 729 | 729 | # NOTE: this is only for |
| 730 | 730 | # cases where smart_save |
| 731 | 731 | # has been activated. |
| 732 | |
| 733 | # No reason to load the belongs_to relations if they haven't |
| 734 | # already been loaded from the database! |
| 735 | original_state_of_load_belongs_to_relations = \ |
| 736 | @do_not_load_belongs_to_relations |
| 737 | @do_not_load_belongs_to_relations = true |
| 738 | |
| 732 | 739 | what_it_belongs_to = {} |
| 733 | 740 | self.send(:belongs_to).each do |relation| |
| 734 | 741 | as_what = self.send("belongs_to_#{relation}_as") |
| … | … | |
| 743 | 743 | |
| 744 | 744 | # Unless the relation is unset, in which case it will be of |
| 745 | 745 | # type NilClass, set it. |
| 746 | | what_it_belongs_to[as_what] = object_it_belongs_to.id \ |
| 746 | what_it_belongs_to[as_what.to_s] = object_it_belongs_to.id || "new" \ |
| 747 | 747 | unless object_it_belongs_to.class == NilClass |
| 748 | 748 | end |
| 749 | | parameters["belongs_to"] = what_it_belongs_to |
| 749 | |
| 750 | # Reset the value so they are loaded the next time when needed |
| 751 | # if that is what the user wants. |
| 752 | @do_not_load_belongs_to_relations = \ |
| 753 | original_state_of_load_belongs_to_relations |
| 754 | |
| 755 | |
| 756 | # We have to make sure the @belongs_to variable contains all changes |
| 757 | # and all the original values for the keys that haven't changed/ |
| 758 | # relations that haven't been loaded |
| 759 | original_belongs_to = @belongs_to || {} |
| 760 | @belongs_to = what_it_belongs_to |
| 761 | times_through = 1 |
| 762 | |
| 763 | # LOOP 1... see below for problem description |
| 764 | original_belongs_to.each_pair do |key, value| |
| 765 | times_through += 1 |
| 766 | @belongs_to[key.to_s] = value unless @belongs_to[key.to_s] |
| 767 | end |
| 768 | |
| 769 | # FIXME: |
| 770 | # Now... this is a really hacky way to solve this problem |
| 771 | # and should be improved... Feel free to come up with sollutions |
| 772 | # Case: |
| 773 | # If it has a belongs_to relationship that is new and therefore |
| 774 | # doesn't have an ID it would normally be written in the @belongs_to |
| 775 | # variable as nil. The problem is that if self has previously |
| 776 | # been saved with another parent object this ID would still come |
| 777 | # through in the @belongs_to variable updater (see "LOOP 1" above). |
| 778 | # We therefore assign the ID "new" to all unsaved relations, which we |
| 779 | # now have to nilify. If we don't the smart save wont work for this |
| 780 | # type of cases. |
| 781 | |
| 750 | 782 | end |
| 751 | 783 | |
| 752 | | parameters["belongs_to"] = @belongs_to if @couch_initial_load && @belongs_to |
| 784 | parameters["belongs_to"] = @belongs_to |
| 785 | parameters.delete("belongs_to") if @belongs_to == {} or @belongs_to == nil |
| 786 | |
| 787 | # if @couch_initial_load && @belongs_to |
| 753 | 788 | |
| 754 | 789 | begin |
| 755 | 790 | parameters.to_json |
| … | … | |
| 814 | 814 | def do_load_has_many_relations |
| 815 | 815 | @do_not_load_has_many_relations = false |
| 816 | 816 | end |
| 817 | alias do_not_load_has_one_relations do_not_load_has_many_relations |
| 818 | alias do_load_has_one_relations do_load_has_many_relations |
| 819 | alias do_not_load_has_one_relation do_not_load_has_many_relations |
| 820 | alias do_load_has_one_relation do_load_has_many_relations |
| 821 | |
| 822 | # |
| 823 | # If you need to access the belongs_to variable without loading the |
| 824 | # relation if it hasn't already been loaded, you can call the instance |
| 825 | # method +do_not_load_belongs_to_relations+. To reactivate loading so |
| 826 | # the relation is loaded the next time it is needed, call the instance |
| 827 | # method +do_load_belongs_to_relations+. |
| 828 | # |
| 829 | def do_not_load_belongs_to_relations |
| 830 | @do_not_load_belongs_to_relations = true |
| 831 | end |
| 832 | def do_load_belongs_to_relations |
| 833 | @do_not_load_belongs_to_relations = false |
| 834 | end |
| 835 | alias do_not_load_has_many_relation do_not_load_has_many_relations |
| 836 | alias do_load_belongs_to_relation do_load_belongs_to_relations |
| 817 | 837 | |
| 818 | 838 | |
| 819 | 839 | protected |
| … | … | |
| 910 | 910 | # |
| 911 | 911 | def couch_load_belongs_to_relation(that_is_called) |
| 912 | 912 | |
| 913 | return nil if new? || @do_not_load_belongs_to_relations |
| 914 | |
| 913 | 915 | # If it doesn't have a belongs to ID then there is no |
| 914 | 916 | # related object in the database |
| 915 | 917 | if @belongs_to && @belongs_to[that_is_called] |
| toggle raw diff |
--- a/lib/couch_object/persistable.rb
+++ b/lib/couch_object/persistable.rb
@@ -364,8 +364,18 @@ module CouchObject
# If it's belongs_to relationships haven't been saved
# then it has be done first.
# Saving the master will also automatically save the child
-
performed_save = false
+
+ # If the belongs_to relationships haven't already been loaded,
+ # there is reason to believe that:
+ # * The object is new and doesn't have any relation set
+ # * The object already knows about it's belongs_to relations
+ # and doesn't need aditional information about them for saving
+ # We therefore deactivate the loading of belongs_to relations
+ original_state_of_load_belongs_to_relations = \
+ @do_not_load_belongs_to_relations
+ @do_not_load_belongs_to_relations = true
+
belongs_to.each do |what_it_belongs_to|
master_class = self.send(what_it_belongs_to)
unless master_class == nil || !master_class.new?
@@ -374,6 +384,11 @@ module CouchObject
end
end
+ # Reset the do_not_load_belongs_to_relations variable to its
+ # original state
+ @do_not_load_belongs_to_relations = \
+ original_state_of_load_belongs_to_relations
+
# If none of the master classes were saved, meaning they weren't new
# or didn't exist, then we have to manually save this object.
couch_perform_save unless performed_save
@@ -665,6 +680,7 @@ module CouchObject
"@id",
"@revision",
"@do_not_load_has_many_relations",
+ "@do_not_load_belongs_to_relations",
"@couch_object_original_state",
"@couch_initial_load",
"@belongs_to"]
@@ -713,6 +729,13 @@ module CouchObject
# NOTE: this is only for
# cases where smart_save
# has been activated.
+
+ # No reason to load the belongs_to relations if they haven't
+ # already been loaded from the database!
+ original_state_of_load_belongs_to_relations = \
+ @do_not_load_belongs_to_relations
+ @do_not_load_belongs_to_relations = true
+
what_it_belongs_to = {}
self.send(:belongs_to).each do |relation|
as_what = self.send("belongs_to_#{relation}_as")
@@ -720,13 +743,48 @@ module CouchObject
# Unless the relation is unset, in which case it will be of
# type NilClass, set it.
- what_it_belongs_to[as_what] = object_it_belongs_to.id \
+ what_it_belongs_to[as_what.to_s] = object_it_belongs_to.id || "new" \
unless object_it_belongs_to.class == NilClass
end
- parameters["belongs_to"] = what_it_belongs_to
+
+ # Reset the value so they are loaded the next time when needed
+ # if that is what the user wants.
+ @do_not_load_belongs_to_relations = \
+ original_state_of_load_belongs_to_relations
+
+
+ # We have to make sure the @belongs_to variable contains all changes
+ # and all the original values for the keys that haven't changed/
+ # relations that haven't been loaded
+ original_belongs_to = @belongs_to || {}
+ @belongs_to = what_it_belongs_to
+ times_through = 1
+
+ # LOOP 1... see below for problem description
+ original_belongs_to.each_pair do |key, value|
+ times_through += 1
+ @belongs_to[key.to_s] = value unless @belongs_to[key.to_s]
+ end
+
+ # FIXME:
+ # Now... this is a really hacky way to solve this problem
+ # and should be improved... Feel free to come up with sollutions
+ # Case:
+ # If it has a belongs_to relationship that is new and therefore
+ # doesn't have an ID it would normally be written in the @belongs_to
+ # variable as nil. The problem is that if self has previously
+ # been saved with another parent object this ID would still come
+ # through in the @belongs_to variable updater (see "LOOP 1" above).
+ # We therefore assign the ID "new" to all unsaved relations, which we
+ # now have to nilify. If we don't the smart save wont work for this
+ # type of cases.
+
end
- parameters["belongs_to"] = @belongs_to if @couch_initial_load && @belongs_to
+ parameters["belongs_to"] = @belongs_to
+ parameters.delete("belongs_to") if @belongs_to == {} or @belongs_to == nil
+
+ # if @couch_initial_load && @belongs_to
begin
parameters.to_json
@@ -756,6 +814,26 @@ module CouchObject
def do_load_has_many_relations
@do_not_load_has_many_relations = false
end
+ alias do_not_load_has_one_relations do_not_load_has_many_relations
+ alias do_load_has_one_relations do_load_has_many_relations
+ alias do_not_load_has_one_relation do_not_load_has_many_relations
+ alias do_load_has_one_relation do_load_has_many_relations
+
+ #
+ # If you need to access the belongs_to variable without loading the
+ # relation if it hasn't already been loaded, you can call the instance
+ # method +do_not_load_belongs_to_relations+. To reactivate loading so
+ # the relation is loaded the next time it is needed, call the instance
+ # method +do_load_belongs_to_relations+.
+ #
+ def do_not_load_belongs_to_relations
+ @do_not_load_belongs_to_relations = true
+ end
+ def do_load_belongs_to_relations
+ @do_not_load_belongs_to_relations = false
+ end
+ alias do_not_load_has_many_relation do_not_load_has_many_relations
+ alias do_load_belongs_to_relation do_load_belongs_to_relations
protected
@@ -832,6 +910,8 @@ module CouchObject
#
def couch_load_belongs_to_relation(that_is_called)
+ return nil if new? || @do_not_load_belongs_to_relations
+
# If it doesn't have a belongs to ID then there is no
# related object in the database
if @belongs_to && @belongs_to[that_is_called] |
| |   |
| 268 | 268 | @apartment.save("foo") |
| 269 | 269 | @apartment.new?.should == false |
| 270 | 270 | end |
| 271 | |
| 272 | it "should not load the belongs_to relations if they are not already loaded before saving" do |
| 273 | @inhabitant_content = %{ {"_id":"50F1A07A283C1879057AFE4C4B727D35","_rev":"1957693345","belongs_to":{"inhabitants":"12A64B5156002BFD66F23F559C5AD5FA"},"class":"Inhabitant","attributes":{}} |
| 274 | } |
| 275 | |
| 276 | response = HTTPResponse.new(@inhabitant_content) |
| 277 | |
| 278 | CouchObject::Database.should_receive(:open).exactly(1).and_return(@db) |
| 279 | |
| 280 | @db.should_receive(:get).once.with("foo").and_return(response) |
| 281 | |
| 282 | inhabitant = Inhabitant.get_with_smart_save("foo") |
| 283 | puts "\boriginal: #{inhabitant.instance_variable_get("@couch_object_original_state")}" |
| 284 | puts " now: #{inhabitant.to_json}" |
| 285 | inhabitant.location.should_not == nil |
| 286 | |
| 287 | # Telling the object not to load its relatives |
| 288 | inhabitant.save |
| 289 | |
| 290 | end |
| 271 | 291 | end |
| 272 | 292 | |
| 273 | 293 | describe CouchObject::Persistable, "loading object relations: " do |
| … | … | |
| 369 | 369 | |
| 370 | 370 | end |
| 371 | 371 | |
| 372 | | it "should be possible to tell the object not to load its relatives" do |
| 372 | it "should be possible to tell the object not to load its has many relations" do |
| 373 | 373 | response = HTTPResponse.new(@apartment_building_content) |
| 374 | 374 | |
| 375 | 375 | CouchObject::Database.should_receive(:open).exactly(1).and_return(@db) |
| … | … | |
| 386 | 386 | apartment_building.inhabitants.size.should == 0 |
| 387 | 387 | |
| 388 | 388 | end |
| 389 | |
| 390 | it "should be possible to tell the object not to load its has many relations" do |
| 391 | response = HTTPResponse.new(@inhabitant_content) |
| 392 | |
| 393 | CouchObject::Database.should_receive(:open).exactly(1).and_return(@db) |
| 394 | |
| 395 | @db.should_receive(:get).once.with("foo").and_return(response) |
| 396 | |
| 397 | inhabitant = Inhabitant.get("foo") |
| 398 | inhabitant.location.should_not == nil |
| 399 | |
| 400 | # Telling the object not to load its relatives |
| 401 | inhabitant.do_not_load_belongs_to_relations |
| 402 | |
| 403 | # The content should be loaded on first request! |
| 404 | inhabitant.apartment_building.should == nil |
| 405 | |
| 406 | end |
| 407 | |
| 389 | 408 | end |
| 390 | 409 | |
| 391 | 410 | describe CouchObject::Persistable, "has_one relations" do |
| toggle raw diff |
--- a/spec/persistable/relations.rb
+++ b/spec/persistable/relations.rb
@@ -268,6 +268,26 @@ describe CouchObject::Persistable, "saving related objects: " do
@apartment.save("foo")
@apartment.new?.should == false
end
+
+ it "should not load the belongs_to relations if they are not already loaded before saving" do
+ @inhabitant_content = %{ {"_id":"50F1A07A283C1879057AFE4C4B727D35","_rev":"1957693345","belongs_to":{"inhabitants":"12A64B5156002BFD66F23F559C5AD5FA"},"class":"Inhabitant","attributes":{}}
+ }
+
+ response = HTTPResponse.new(@inhabitant_content)
+
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
+
+ @db.should_receive(:get).once.with("foo").and_return(response)
+
+ inhabitant = Inhabitant.get_with_smart_save("foo")
+ puts "\boriginal: #{inhabitant.instance_variable_get("@couch_object_original_state")}"
+ puts " now: #{inhabitant.to_json}"
+ inhabitant.location.should_not == nil
+
+ # Telling the object not to load its relatives
+ inhabitant.save
+
+ end
end
describe CouchObject::Persistable, "loading object relations: " do
@@ -349,7 +369,7 @@ describe CouchObject::Persistable, "loading object relations: " do
end
- it "should be possible to tell the object not to load its relatives" do
+ it "should be possible to tell the object not to load its has many relations" do
response = HTTPResponse.new(@apartment_building_content)
CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
@@ -366,6 +386,25 @@ describe CouchObject::Persistable, "loading object relations: " do
apartment_building.inhabitants.size.should == 0
end
+
+ it "should be possible to tell the object not to load its has many relations" do
+ response = HTTPResponse.new(@inhabitant_content)
+
+ CouchObject::Database.should_receive(:open).exactly(1).and_return(@db)
+
+ @db.should_receive(:get).once.with("foo").and_return(response)
+
+ inhabitant = Inhabitant.get("foo")
+ inhabitant.location.should_not == nil
+
+ # Telling the object not to load its relatives
+ inhabitant.do_not_load_belongs_to_relations
+
+ # The content should be loaded on first request!
+ inhabitant.apartment_building.should == nil
+
+ end
+
end
describe CouchObject::Persistable, "has_one relations" do |
| |   |
| 88 | 88 | |
| 89 | 89 | end |
| 90 | 90 | |
| 91 | | |
| 92 | 91 | it "should save an object that does not unsaved changes if it hasn't activated smart_save" do |
| 93 | 92 | response = HTTPResponse.new(@normal_content_motor_bike) |
| 94 | 93 | |
| … | … | |
| 133 | 133 | |
| 134 | 134 | end |
| 135 | 135 | |
| 136 | | |
| 137 | 136 | it "should only save relations that need to be saved" do |
| 138 | 137 | |
| 139 | 138 | response = HTTPResponse.new(@apartment_building_content) |
| … | … | |
| 164 | 164 | |
| 165 | 165 | end |
| 166 | 166 | |
| 167 | | it "a class in a belongs_to relationship should notice if it parent changes" do |
| 167 | it "a class in a belongs_to relationship should notice if its parent changes" do |
| 168 | 168 | |
| 169 | 169 | response = HTTPResponse.new(@apartment_building_content) |
| 170 | 170 | response_relation = HTTPResponse.new(@apartment_building_inhabitants) |
| … | … | |
| 198 | 198 | second_apartment_building.inhabitants.size.should == 1 |
| 199 | 199 | apartment_building.inhabitants.size.should == 1 |
| 200 | 200 | apartment_building.inhabitants.first.unsaved_changes?.should == false |
| 201 | puts "Original: #{second_apartment_building.inhabitants.first.instance_variable_get("@couch_object_original_state")}" |
| 202 | puts " Now: #{second_apartment_building.inhabitants.first.to_json}" |
| 203 | |
| 204 | puts "second_apartment_building.id '#{second_apartment_building.id == nil}'" |
| 201 | 205 | second_apartment_building.inhabitants.first.unsaved_changes?.should == true |
| 206 | |
| 202 | 207 | |
| 203 | 208 | # Should not save anything at all... |
| 204 | 209 | apartment_building.save |
| toggle raw diff |
--- a/spec/persistable/unsaved_changes.rb
+++ b/spec/persistable/unsaved_changes.rb
@@ -88,7 +88,6 @@ describe CouchObject::Persistable, "for loading objects:" do
end
-
it "should save an object that does not unsaved changes if it hasn't activated smart_save" do
response = HTTPResponse.new(@normal_content_motor_bike)
@@ -134,7 +133,6 @@ describe CouchObject::Persistable, "for loading objects:" do
end
-
it "should only save relations that need to be saved" do
response = HTTPResponse.new(@apartment_building_content)
@@ -166,7 +164,7 @@ describe CouchObject::Persistable, "for loading objects:" do
end
- it "a class in a belongs_to relationship should notice if it parent changes" do
+ it "a class in a belongs_to relationship should notice if its parent changes" do
response = HTTPResponse.new(@apartment_building_content)
response_relation = HTTPResponse.new(@apartment_building_inhabitants)
@@ -200,7 +198,12 @@ describe CouchObject::Persistable, "for loading objects:" do
second_apartment_building.inhabitants.size.should == 1
apartment_building.inhabitants.size.should == 1
apartment_building.inhabitants.first.unsaved_changes?.should == false
+ puts "Original: #{second_apartment_building.inhabitants.first.instance_variable_get("@couch_object_original_state")}"
+ puts " Now: #{second_apartment_building.inhabitants.first.to_json}"
+
+ puts "second_apartment_building.id '#{second_apartment_building.id == nil}'"
second_apartment_building.inhabitants.first.unsaved_changes?.should == true
+
# Should not save anything at all...
apartment_building.save |