From b97403b8ead703fc06c6c1e2ec20d72110ede0ae Mon Sep 17 00:00:00 2001 From: yann ARMAND Date: Wed, 11 Mar 2015 23:07:29 +0000 Subject: [PATCH] add option to support specifying reverse association field --- lib/couchrest/model/associations.rb | 33 +++++++++++++++++++---------- spec/fixtures/models/husband.rb | 7 ------ spec/fixtures/models/kid.rb | 4 ++-- spec/fixtures/models/parent.rb | 10 +++++++++ spec/fixtures/models/super_power.rb | 5 +++++ spec/fixtures/models/wife.rb | 7 ------ spec/unit/assocations_dual_spec.rb | 30 ++++++++++++++++++-------- 7 files changed, 60 insertions(+), 36 deletions(-) delete mode 100644 spec/fixtures/models/husband.rb create mode 100644 spec/fixtures/models/parent.rb create mode 100644 spec/fixtures/models/super_power.rb delete mode 100644 spec/fixtures/models/wife.rb diff --git a/lib/couchrest/model/associations.rb b/lib/couchrest/model/associations.rb index ef433513..88227810 100644 --- a/lib/couchrest/model/associations.rb +++ b/lib/couchrest/model/associations.rb @@ -111,9 +111,11 @@ def associations private def merge_belongs_to_association_options(attrib, options = nil) + class_name = options.delete(:class_name) if options.is_a?(Hash) + class_name ||= attrib opts = { :foreign_key => attrib.to_s.singularize + '_id', - :class_name => attrib.to_s.singularize.camelcase, + :class_name => class_name.to_s.singularize.camelcase, :proxy_name => attrib.to_s.pluralize, :allow_blank => false } @@ -161,9 +163,9 @@ def #{attrib}=(value) self.#{options[:foreign_key]} = value.nil? ? nil : value.id unless value.nil? binding = value - binding.set_back_association(self, self.class.name) + binding.set_back_association(self, self.class.name, '#{options[:reverse_association]}') else - binding.set_back_association(nil, self.class.name) + binding.set_back_association(nil, self.class.name, '#{options[:reverse_association]}') end register_dirty_association(binding) @#{attrib} = value @@ -193,14 +195,23 @@ def #{attrib}=(value) end - def set_back_association(value, class_name) - assoc = self.class.associations.detect { |ass| ass[:options][:class_name] == class_name } - return unless assoc - case assoc[:type] - when :belongs_to - send("#{assoc[:options][:foreign_key]}=", (value.nil? ? nil : value.id)) - when :collection_of - instance_eval("#{assoc[:options][:foreign_key]}.push('#{value.nil? ? nil : value.id}')") + def set_back_association(value, class_name, reverse_association = nil) + if reverse_association && !reverse_association.empty? + prop = self.class.properties.detect { |prop| prop.name =~ %r{#{reverse_association}_id} } + if prop.type.ancestors.include? Enumerable + instance_eval("#{prop.name}.push('#{value.nil? ? nil : value.id}')") + else + send("#{prop.name}=", (value.nil? ? nil : value.id)) + end + else + assoc = self.class.associations.detect { |ass| ass[:options][:class_name] == class_name } + return unless assoc + case assoc[:type] + when :belongs_to + send("#{assoc[:options][:foreign_key]}=", (value.nil? ? nil : value.id)) + when :collection_of + instance_eval("#{assoc[:options][:foreign_key]}.push('#{value.nil? ? nil : value.id}')") + end end end diff --git a/spec/fixtures/models/husband.rb b/spec/fixtures/models/husband.rb deleted file mode 100644 index 291e33bc..00000000 --- a/spec/fixtures/models/husband.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Husband < CouchRest::Model::Base - property :name, String - - belongs_to :wife - - collection_of :children, :class_name => 'Kid' -end diff --git a/spec/fixtures/models/kid.rb b/spec/fixtures/models/kid.rb index bb125870..518ef2d5 100644 --- a/spec/fixtures/models/kid.rb +++ b/spec/fixtures/models/kid.rb @@ -1,7 +1,7 @@ class Kid < CouchRest::Model::Base property :name, String - belongs_to :dad, :class_name => 'Husband' - belongs_to :mum, :class_name => 'Wife' + belongs_to :dad, :class_name => 'Parent' + belongs_to :mum, :class_name => 'Parent' end diff --git a/spec/fixtures/models/parent.rb b/spec/fixtures/models/parent.rb new file mode 100644 index 00000000..7820c117 --- /dev/null +++ b/spec/fixtures/models/parent.rb @@ -0,0 +1,10 @@ +class Parent < CouchRest::Model::Base + property :name, String + + belongs_to :super_power + belongs_to :husband, :class_name => :parent, :reverse_association => :wife + belongs_to :wife, :class_name => :parent, :reverse_association => :husband + belongs_to :lives_with, :class_name => :parent, :reverse_association => :lives_with + + collection_of :children, :class_name => 'Kid' +end diff --git a/spec/fixtures/models/super_power.rb b/spec/fixtures/models/super_power.rb new file mode 100644 index 00000000..3dc58449 --- /dev/null +++ b/spec/fixtures/models/super_power.rb @@ -0,0 +1,5 @@ +class SuperPower < CouchRest::Model::Base + property :description, String + + belongs_to :parent +end diff --git a/spec/fixtures/models/wife.rb b/spec/fixtures/models/wife.rb deleted file mode 100644 index 8aa06308..00000000 --- a/spec/fixtures/models/wife.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Wife < CouchRest::Model::Base - property :name, String - - belongs_to :husband - - collection_of :children, :class_name => 'Kid' -end diff --git a/spec/unit/assocations_dual_spec.rb b/spec/unit/assocations_dual_spec.rb index cae97339..88c12750 100644 --- a/spec/unit/assocations_dual_spec.rb +++ b/spec/unit/assocations_dual_spec.rb @@ -3,21 +3,32 @@ describe 'Associations' do - let(:father) { Husband.create(name: 'bob')} - let(:mummy) { Wife.create( name: 'claire')} + let(:father) { Parent.create(name: 'Bob')} + let(:can_fly){ SuperPower.create(description: 'Can fly when there is no cloud')} + let(:mummy) { Parent.create( name: 'Claire')} let(:kid) { Kid.create( name: 'Vladimir')} describe 'of type belongs_to' do context 'with the other side also belongs_to (1-1)' do - it 'should set the other side property too' do - father.wife = mummy - mummy.husband.should eql(father) + context '[non ambiguous association]' do + it 'should set the other side property too' do + father.super_power = can_fly + can_fly.parent.should eql father + end end - it 'should remove the other side if value is nil' do - father.wife = mummy - father.wife = nil - mummy.husband.should be_nil + context '[ambiguous association]' do + it 'should set the other side property too' do + father.wife = mummy + mummy.husband.should eql(father) + end + end + + context '[cyclic association]' do + it 'should set the other side property too' do + father.lives_with = mummy + mummy.lives_with.should eql(father) + end end end @@ -25,6 +36,7 @@ let(:invoice) { SaleInvoice.create(:price => 2000) } let(:client) { Client.create(:name => "Sam Lown") } it 'should set property without error' do + invoice.client = client lambda { invoice.client = client }.should_not raise_error end