Skip to content

Commit

Permalink
add option to support specifying reverse association field
Browse files Browse the repository at this point in the history
  • Loading branch information
yann ARMAND committed Mar 11, 2015
1 parent 330c1b8 commit b97403b
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 36 deletions.
33 changes: 22 additions & 11 deletions lib/couchrest/model/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
7 changes: 0 additions & 7 deletions spec/fixtures/models/husband.rb

This file was deleted.

4 changes: 2 additions & 2 deletions spec/fixtures/models/kid.rb
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions spec/fixtures/models/parent.rb
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions spec/fixtures/models/super_power.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class SuperPower < CouchRest::Model::Base
property :description, String

belongs_to :parent
end
7 changes: 0 additions & 7 deletions spec/fixtures/models/wife.rb

This file was deleted.

30 changes: 21 additions & 9 deletions spec/unit/assocations_dual_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,40 @@

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

context 'with the other side do not back associate (1-0)' do
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

Expand Down

0 comments on commit b97403b

Please sign in to comment.