Reputation: 4681
I'm trying to do reverse delegation in Ruby (although fully aware that might not even be a thing.) To illustrate, say I have two classes:
class Foo
def initialize
@bar = Bar.new
end
def say_hello
@bar.say_hello
end
def greeting
"OHAI"
end
end
class Bar
def say_hello
puts greeting
end
def greeting
"Hello!"
end
end
How would I go about making a macro method on Foo
that tells Bar
to use Foo
's greeting
method?
Upvotes: 1
Views: 184
Reputation: 110685
You could change class Bar
as follows:
class Bar
def say_hello
puts greeting
end
def use_foos_greeting
self.class.class_eval do
def greeting
@f ||= Foo.new
@f.greeting
end
end
end
def use_bars_greeting
self.class.class_eval do
alias_method :greeting, :greeting_copy
end
end
def greeting
"Hello!"
end
alias_method :greeting_copy, :greeting
end
bar = Bar.new
bar.greeting #=> "Hello!"
bar.use_foos_greeting
bar.greeting #=> "OHAI"
bar.use_bars_greeting
bar.greeting #=> "Hello!"
Consider the method:
def use_foos_greeting
self.class.class_eval do
def greeting
@f ||= Foo.new
@f.greeting
end
end
end
Here
@f ||= Foo.new
is shorthand for
@f = @f || Foo.new
The first time use_foos_greeting
is called, @f => nil
, so @f
is set to Foo.new
. In following calls to use_foos_greeting
, @f
evaluate true
, so it will not be changed. The method would work if we had @f = Foo.new
instead, but a new instance of Foo
would be created each time the method were called (and the instance of Foo
it replaced would be garbage-collected).
Note you cannot add:
def initialize
@f = Foo.new
end
to class Bar
to save the Foo
instance, because class Foo
has
def initialize
@f = Bar.new
end
which would result in an endless loop.
Upvotes: 1