Forum: Ruby Bidirectionnal relation

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E257c17034ca33ea3f75574f6b22e402?d=identicon&s=25 dohzya (Guest)
on 2007-07-24 20:39
(Received via mailing list)
Hi everyone,
I have a problem with bidirectional relations : how to implements it ?

I can't implements it with 2 relations without synchronize them :
---
# no synchronization
class Entity
  attr_accessor :mother
  attr_reader :daughters
  def initialize
    @daughters = []
  end
end

a = Entity.new
b = Entity.new
a.mother = b
puts (b.daughters.include? a) # false
a.daughters << b
puts (b.mother == a) # false
---
too bad...

Try with a simple synchronization :
---
# simple synchronization
class Entity
  attr_reader :mother, :daughters
  def initialize
    @daughters = []
  end
  def mother= m
    @mother.daughters.delete self unless @mother.nil?
    @mother = m
    @mother.daughters << self unless @mother.nil?
  end
  def add_daughter d
    unless d.nil?
      @daughters << d
      d.mother = self
    end
  end
end

a = Entity.new
b = Entity.new
a.mother = b
puts (b.daughters.include? a) # true
a.daughters << b # it's possible so why not use it ?
puts (b.mother == a) # false
---
ok for some operations, but not exhaustive.

The problem seems to come from @daughters which is a simple Array.
Let's fix it :
---
# better synchronization
class Entity
  attr_reader :mother, :daughters
  def initialize
    @daughters = []
    class << @daughters
      attr_accessor :mother
      alias __old_add__ <<
      def << d, recc=false
        __old_add__ d
        d.mother = @mother unless recc
      end
      alias __old_delete__ delete
      def delete d
        if include? d
          d.mother = nil
          __old_delete__ d
        end
      end
    end
    @daughters.mother = self
  end
  def mother= m
    @mother.daughters.delete self unless @mother.nil?
    @mother = m
    @mother.daughters.<<(self, true) unless @mother.nil?
  end
  def add_daughter d
    unless d.nil?
      @daughters << d
      d.mother = self
    end
  end
end

a = Entity.new
b = Entity.new
a.mother = b
puts (b.daughters.include? a) # true
a.daughters << b
puts (b.mother == a) # true
---
ok it works (I think), but I need to redefine all methods from
@daughters...

26 lines more than the first (and natural) solution, for a really simple
problem... I don't think I'll often use it.

I tried to implement a solution with a real relation, but I had too many
problems.

If anyone has a suggestion (solution or link or anything) to implement
transparent bidirectional relation... :)
Thanks !
852a62a28f1de229dc861ce903b07a60?d=identicon&s=25 Gavin Kistner (phrogz)
on 2007-07-24 21:29
dohzya wrote:
> Hi everyone,
> I have a problem with bidirectional relations : how to implements it ?

How about:
class Female
  attr_accessor :mother
  def initialize( name )
    @name = name
    @daughters = []
  end
  def daughters
    @daughters.dup.freeze
  end
  def mother=( mom )
    @mother = mom
    unless mom.daughters.include?( self )
      mom.add_daughter( self )
    end
  end
  def add_daughter( girl )
    @daughters << girl unless @daughters.include?( girl )
    girl.mother = self
  end
  def inspect
    "<Female '#{@name}'>"
  end
end

a = Female.new( 'Strami' )
b = Female.new( 'Lisa' )
c = Female.new( 'Imogen' )

b.mother = a
p a.daughters.include?( b )
#=> true

b.daughters << c
#=> Error: can't modify frozen array (TypeError)

b.add_daughter c

p a.daughters
#=> [<Female 'Lisa'>]

p b.daughters
#=> [<Female 'Imogen'>]

p c.mother
#=> <Female 'Lisa'>
50b2daf0e7666574579b9edaf8f2b69a?d=identicon&s=25 Pit Capitain (Guest)
on 2007-07-25 18:26
(Received via mailing list)
2007/7/24, dohzya <dohzya@gmail.com>:
> I have a problem with bidirectional relations : how to implements it ?
> (...)
> If anyone has a suggestion (solution or link or anything) to implement
> transparent bidirectional relation... :)

Hi Etienne,

in ruby-talk:121602 I posted a little library to implement this.
Here's your example:

  require "relation"

  class Entity
  end

  Relation.new Entity, :one, :mother, Entity, :many, :daughters

  a = Entity.new
  b = Entity.new

  a.mother = b
  puts(b.daughters.include?(a))  # => true

  a.daughters << b
  puts(b.mother == a)            # => true


If you find nothing else maybe I should create my first gem...

Regards,
Pit
E257c17034ca33ea3f75574f6b22e402?d=identicon&s=25 dohzya (Guest)
on 2007-07-25 20:05
(Received via mailing list)
Le jeudi 26 juillet 2007 à 01:25 +0900, Pit Capitain a écrit :
>
>   a.mother = b
>
Why find again ? it's exactly what I want !

I wait for your gem, thanks :)
E257c17034ca33ea3f75574f6b22e402?d=identicon&s=25 dohzya (Guest)
on 2007-07-25 20:56
(Received via mailing list)
Le jeudi 26 juillet 2007 à 01:25 +0900, Pit Capitain a écrit :
> 2007/7/24, dohzya <dohzya@gmail.com>:
> > I have a problem with bidirectional relations : how to implements
it ?
> > (...)
> > If anyone has a suggestion (solution or link or anything) to
implement
>   end
>   puts(b.mother == a)            # => true
>
>
> If you find nothing else maybe I should create my first gem...
>
> Regards,
> Pit
>

Why find again ? it's exactly what I want !

I wait for your gem, thanks :)
E257c17034ca33ea3f75574f6b22e402?d=identicon&s=25 dohzya (Guest)
on 2007-07-26 10:08
(Received via mailing list)
Le jeudi 26 juillet 2007 à 01:25 +0900, Pit Capitain a écrit :
> 2007/7/24, dohzya <dohzya@gmail.com>:
> > I have a problem with bidirectional relations : how to implements
it ?
> > (...)
> > If anyone has a suggestion (solution or link or anything) to
implement
>   end
>   puts(b.mother == a)            # => true
>
>
> If you find nothing else maybe I should create my first gem...
>
> Regards,
> Pit
>

Why find again ? it's exactly what I want !

I wait for your gem, thanks :)

(and I hate my mail client...)
This topic is locked and can not be replied to.