From 938bf2cf2c9fc56e07e8805189c252583d646271 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Fri, 20 May 2011 02:15:18 +0200 Subject: [PATCH] Fixing dirty tracking on collection_of association type --- history.md | 1 + lib/couchrest/model/associations.rb | 104 +++++++++++++--------------- spec/couchrest/assocations_spec.rb | 29 +++++++- 3 files changed, 79 insertions(+), 55 deletions(-) diff --git a/history.md b/history.md index d780dd0..7e8d482 100644 --- a/history.md +++ b/history.md @@ -9,6 +9,7 @@ * Initialization blocks when creating new models (Peter Williams) * Removed railties dependency (DAddYE) * DesignDoc cache refreshed if a database is deleted. + * Fixing dirty tracking on collection_of association. ## 1.1.0.beta5 - 2011-04-30 diff --git a/lib/couchrest/model/associations.rb b/lib/couchrest/model/associations.rb index ed2c23c..f8e5a12 100644 --- a/lib/couchrest/model/associations.rb +++ b/lib/couchrest/model/associations.rb @@ -153,7 +153,7 @@ module CouchRest def #{attrib}(reload = false) return @#{attrib} unless @#{attrib}.nil? or reload ary = self.#{options[:foreign_key]}.collect{|i| #{options[:proxy]}.get(i)} - @#{attrib} = ::CouchRest::CollectionOfProxy.new(ary, self, '#{options[:foreign_key]}') + @#{attrib} = ::CouchRest::Model::CollectionOfProxy.new(ary, find_property('#{options[:foreign_key]}'), self) end EOS end @@ -161,7 +161,7 @@ module CouchRest def create_collection_of_setter(attrib, options) class_eval <<-EOS, __FILE__, __LINE__ + 1 def #{attrib}=(value) - @#{attrib} = ::CouchRest::CollectionOfProxy.new(value, self, '#{options[:foreign_key]}') + @#{attrib} = ::CouchRest::Model::CollectionOfProxy.new(value, find_property('#{options[:foreign_key]}'), self) end EOS end @@ -169,67 +169,63 @@ module CouchRest end end - end - # Special proxy for a collection of items so that adding and removing - # to the list automatically updates the associated property. - class CollectionOfProxy < Array - attr_accessor :property - attr_accessor :casted_by + # Special proxy for a collection of items so that adding and removing + # to the list automatically updates the associated property. + class CollectionOfProxy < CastedArray - def initialize(array, casted_by, property) - self.property = property - self.casted_by = casted_by - (array ||= []).compact! - casted_by[property.to_s] = [] # replace the original array! - array.compact.each do |obj| - check_obj(obj) - casted_by[property.to_s] << obj.id + def initialize(array, property, parent) + (array ||= []).compact! + super(array, property, parent) + casted_by[casted_by_property.to_s] = [] # replace the original array! + array.compact.each do |obj| + check_obj(obj) + casted_by[casted_by_property.to_s] << obj.id + end + end + + def << obj + check_obj(obj) + casted_by[casted_by_property.to_s] << obj.id + super(obj) + end + + def push(obj) + check_obj(obj) + casted_by[casted_by_property.to_s].push obj.id + super(obj) + end + + def unshift(obj) + check_obj(obj) + casted_by[casted_by_property.to_s].unshift obj.id + super(obj) end - super(array) - end - - def << obj - check_obj(obj) - casted_by[property.to_s] << obj.id - super(obj) - end - - def push(obj) - check_obj(obj) - casted_by[property.to_s].push obj.id - super(obj) - end - - def unshift(obj) - check_obj(obj) - casted_by[property.to_s].unshift obj.id - super(obj) - end - def []= index, obj - check_obj(obj) - casted_by[property.to_s][index] = obj.id - super(index, obj) - end + def []= index, obj + check_obj(obj) + casted_by[casted_by_property.to_s][index] = obj.id + super(index, obj) + end - def pop - casted_by[property.to_s].pop - super - end - - def shift - casted_by[property.to_s].shift - super - end + def pop + casted_by[casted_by_property.to_s].pop + super + end + + def shift + casted_by[casted_by_property.to_s].shift + super + end - protected + protected + + def check_obj(obj) + raise "Object cannot be added to #{casted_by.class.to_s}##{casted_by_property.to_s} collection unless saved" if obj.new? + end - def check_obj(obj) - raise "Object cannot be added to #{casted_by.class.to_s}##{property.to_s} collection unless saved" if obj.new? end end - end diff --git a/spec/couchrest/assocations_spec.rb b/spec/couchrest/assocations_spec.rb index cb3b0c4..0affb6e 100644 --- a/spec/couchrest/assocations_spec.rb +++ b/spec/couchrest/assocations_spec.rb @@ -101,7 +101,7 @@ describe "Assocations" do it "should create an associated property and collection proxy" do @invoice.respond_to?('entry_ids').should be_true @invoice.respond_to?('entry_ids=').should be_true - @invoice.entries.class.should eql(::CouchRest::CollectionOfProxy) + @invoice.entries.class.should eql(::CouchRest::Model::CollectionOfProxy) end it "should allow replacement of objects" do @@ -154,6 +154,33 @@ describe "Assocations" do @invoice.entries.should be_empty end + # Account for dirty tracking + describe "dirty tracking" do + it "should register changes on push" do + @invoice.changed?.should be_false + @invoice.entries << @entries[0] + @invoice.changed?.should be_true + end + it "should register changes on pop" do + @invoice.entries << @entries[0] + @invoice.save + @invoice.changed?.should be_false + @invoice.entries.pop + @invoice.changed?.should be_true + end + it "should register id changes on push" do + @invoice.entry_ids << @entries[0].id + @invoice.changed?.should be_true + end + it "should register id changes on pop" do + @invoice.entry_ids << @entries[0].id + @invoice.save + @invoice.changed?.should be_false + @invoice.entry_ids.pop + @invoice.changed?.should be_true + end + end + describe "proxy" do it "should ensure new entries to proxy are matched" do