Reputation: 91
I am working on a project of context-oriented programming in ruby. And I come to this problem:
Suppose that I have a class Klass:
class Klass
def my_method
proceed
end
end
I also have a proc stored inside a variable impl
. And impl
contains { puts "it works!" }
.
From somewhere outside Klass, I would like to define a method called proceed
inside the method my_method
. So that if a call Klass.new.my_method
, I get the result "it works"
.
So the final result should be something like that:
class Klass
def my_method
def proceed
puts "it works!"
end
proceed
end
end
Or if you have any other idea to make the call of proceed
inside my_method
working, it's also good. But the proceed
of another method (let's say my_method_2
) isn't the same as my_method
.
In fact, the proceed
of my_method
represent an old version of my_method
. And the proceed
of my_method_2
represent an old version of my_method_2
.
Thanks for your help
Upvotes: 0
Views: 124
Reputation: 110675
One way of doing that is to construct a hash whose keys are the names of the methods calling proceed
and whose values are procs that represent the implementations of proceed
for each method calling it.
class Klass
singleton_class.send(:attr_reader, :proceeds)
@proceeds = {}
def my_method1(*args)
proceed(__method__,*args)
end
def my_method2(*args)
proceed(__method__,*args)
end
def proceed(m, *args)
self.class.proceeds[m].call(*args)
end
end
def define_proceed(m, &block)
Klass.proceeds[m] = Proc.new &block
end
define_proceed(:my_method1) { |*arr| arr.sum }
define_proceed(:my_method2) { |a,b| "%s-%s" % [a,b] }
k = Klass.new
k.my_method1(1,2,3) #=> 6
k.my_method2("cat", "dog") #=> "cat-dog"
Upvotes: 0
Reputation: 121000
Disclaimer: you are doing it wrong!
There must be more robust, elegant and rubyish way to achieve what you want. If you still want to abuse metaprogramming, here you go:
class Klass
def self.proceeds
@proceeds ||= {}
end
def def_proceed
self.class.proceeds[caller.first[/`.*?'/]] = Proc.new
end
def proceed *args
self.class.proceeds[caller.first[/`.*?'/]].(*args)
end
def m_1
def_proceed { puts 1 }
proceed
end
def m_2
def_proceed { puts 2 }
proceed
end
end
inst = Klass.new
inst.m_1
#⇒ 1
inst.m_2
#⇒ 2
What you in fact need, is Module#prepend
and call super
from there.
Upvotes: 1