I want to know which is the best way to create a two-ways father-children relationship in ruby. For example, the following code:
class Bar
def initialize(name)
@name = name
end
end
class Foo
attr_accessor :children
def initialize(names = %w(john ben maudy))
@children = names.map{|n| Bar.new(n)}
end
end
With this, it is fairy easy to get from a Foo instance to its Bar children. But it doesn't have the other way around. For for example:
# Instanciates a Foo and get his first son
bar = Foo.new.children.first
# bar can't get to his parent. Essentially, I want
bar.foo
The only idea I had so far is to pass self in the Bar#new method, to keep the reference on the bar objects, but I would like to avoid this. Can you explain me a better way?
EDIT
From google I could find the caller method, which gives me the line number. However I was looking for an object reference, a little more abstract than the line number.
EDIT 2
You can do this in python
EDIT 3
This spinoff of pry seems to be best fit. If anyone can find a way to do this without the gem, I might answer my own question with it.
This is a strong composition, where the component couldn't be built without a reference to the composite.
That means you need to initialized them with that composite (i.e. pass self to the Bar instance). This is the way to do it, as you need a reference to the owner. You can possibly set it after, for instance bar.foo = foo, but it will implement a looser link, an aggregation.
If we don't receive a reference, we need to find it somewhere. We can make Foo a singleton, but it's an anti-pattern as it's like a global variable. Bar instances could request to another object what's the reference of their Foo. If there is multiple instances of Foo, you need to identify them, with an id for instance, but that means Bar instances need that id. Then, why not working directly with the reference of the instance of Foo?
As conclusion, IMHO the only good solution consists in passing a reference of Foo when building a new Bar instance. If the intention is to respect OO paradigm, IMHO it's bad idea to hide that important relation implementation.
Code for the initialization of Bar instance with Foo instance
class Bar # Class of the component
attr_reader :foo
# the component must be initialized/built
# with a ref to the composite foo
def initialize(name, foo)
@name = name
@foo = foo
end
end
class Foo # Class of the composite
attr_reader :children
def initialize(names = %w(john ben maudy))
@children = names.map do |n|
Bar.new(n, self) # pass self to build the component
end
end
end
bar = Foo.new.children.first
bar.foo
Update about binding_of_caller
It's an interesting project making accessible the callstack. In the current case, it would be used in Bar::initialized method to find the composite, who is the caller, and ask it to return self.
However, the project needs to extend the Ruby implementation. It's then not pure ruby.
I think you should just pass foos into Bar's constructor like:
class Bar
def initialize(name, parent)
@name = name
@parent = parent
end
end
class Foo
attr_accessor :children
def initialize(names = %w(john ben maudy))
@children = names.map{|n| Bar.new(n, self)}
end
end
This way each bar will have a reference to the foo that created it.
If you really don't want to pass foo into bar's constructor, you could also do something like the following, but I don't think it gains you much.
class Bar
attr_accessor :foo
def initialize(name)
@name = name
end
end
class Foo
attr_accessor :children
def initialize(names = %w(john ben maudy))
@children = names.map{|n| Bar.new(n)}
end
def get_nth_child(n)
bar = @children[n]
bar.foo = self
bar
end
end
and then you access your original foo like:
Foo.new.get_nth_child(1).foo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With