Hi all!
This is my first post here, so please don't yell at me too harshly
I have a little problem with 'super' keyword when it is used in
define_method/alias_method. First, a little example:
class C
聽聽聽def greetings
聽聽聽聽聽p "Hello, #{caller.first}"
聽聽聽end
聽聽聽def curses
聽聽聽聽聽p "Damn you, #{caller.first}"
聽聽聽end
end
class D < C
聽聽聽def greetings
聽聽聽聽聽super
聽聽聽end
聽聽聽define_method :curses, instance_method(:greetings)
end
[3] pry(main)> D.new.greetings
"Hello, (pry):11:in `greetings'"
=> "Hello, (pry):11:in `greetings'"
[4] pry(main)> D.new.curses
"Hello, (pry):11:in `greetings'"
=> "Hello, (pry):11:in `greetings'"
So, as you can see 'super' is bound, perhaps a little unexpectedly, to
a method instance rather than to its call site. You can see it clearly
when inspecting the caller - in both cases it is 'greetings'. While
this may seem logical and sound, it is quite troubling when you try to
make a wrapper method around user-defined method doing metaprogramming
tricks. For example, I am trying to implement the chain pattern like
this:
def chain
聽聽聽聽聽聽聽聽new_method_name = "__#{@__chain_method}"
聽聽聽聽聽聽聽聽chained_method = @__chain_method
聽聽聽聽聽聽聽聽#....
聽聽聽聽聽聽聽聽alias_method new_method_name, chained_method
聽聽聽聽聽聽聽聽private new_method_name
聽聽聽聽聽聽聽聽remove_method chained_method
聽聽聽聽聽聽聽聽define_method(chained_method) do |*args|
聽聽聽聽聽聽聽聽聽聽send(new_method_name, *args) do |*yielded_args|
聽聽聽聽聽聽聽聽聽聽聽聽#...
聽聽聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽聽聽end unless method_defined? chained_method
聽聽聽聽聽聽聽聽#...
end
In short, when a new method of given name is defined I alias it to
some other name (__old_name) and then I define another method
replacing user-defined method. In pseudo-code
def execute
#...
end
ends up as
def __execute; end;
def execute; ... send(__execute) ...; end;
Works like a charm, except when original method contains super - it is
supposed to call superclass' __execute, but it calls superclass
execute instead and all this ends up with stack overflow (intended
callchain should be: execute -> __execute(child) -->
__execute(parent), but is: execute -> __execute(child) -> execute ->
__execute(child)).
Do you know any workaround? How can I overcome this problem and let
super be bound correctly if used from aliased method? Thanks in
advance for your opinions.
路路路
--
Greetings
Marcin Rze藕nicki