From: "Marcin R." Date: 2013-03-21T19:31:58+09:00 Subject: Re: Finding one's way with 'super' in define_method/alias_emthod Robert Klemme wrote in post #1102288: >> >> I don't think so, I mean, obviously super already has to know the name >> of the method it's invoked from to do a lookup. > > Yes, but with the current solution the name can be baked into the > compiled version of the Ruby code. While with your suggestion the > name would have to be retrieved for every single call. That is the > difference. Is there anything like compiled Ruby code? I'm curious. Could you elaborate? > >> BTW, having given it a little thought I think that I can easily check >> for the condition of being called from super myself. What it takes is >> just a flag that signals that we're recursively called and a check what >> real 'self' is. I still need to flesh out the details but I guess this >> might work. > > Good. I don't understand why "self" might change when chaining method > calls on the same instance but I truest you to sort that out. :-) > Ok, I solved this. My description was rather clumsy, but code speaks more than thousand words. So, here it is. It is not tested thoroughly though. module Chain attr_accessor :next def self.init_chain(chain) chain.each_cons(2) { |first, last| first.next = last } chain.last.next = nil chain.first end def self.included(host) host.extend(ChainClassMethods) host.instance_variable_set(:@__chain_method, nil) end module ChainClassMethods def chain_method(method_name) raise "There is another chain method already defined in this module: #{@__chain_method}" if @__chain_method @__chain_method = method_name chain if method_defined? method_name end def method_added(method_name) chain if @__chain_method == method_name and not instance_variable_get(:@__in_chain) end def inherited(subclass) subclass.instance_variable_set(:@__chain_method, @__chain_method) end private def chain new_method_name = "__#{@__chain_method}" chain_method = @__chain_method @__in_chain = true alias_method new_method_name, chain_method private new_method_name chained_method = instance_method(new_method_name) define_method(chain_method) do |*args| if instance_variable_get(:@_in_chain) chained_method.bind(self).call(*args) do |*yielded_args| begin new_args = yielded_args.empty? ? args : yielded_args self.next.send chain_method, *new_args end if self.next end else @_in_chain = true send new_method_name, *args do |*yielded_args| begin new_args = yielded_args.empty? ? args : yielded_args self.next.send chain_method, *new_args end if self.next end remove_instance_variable :@_in_chain end end remove_instance_variable :@__in_chain end end end Comments are welcome. The trick here is to store method instance in a closure: chained_method = instance_method(new_method_name) and keep a flag signalling if the call is recursive (@_in_chain). If it is not then we simply pass the message to the original method adding a block that does the chaining (call it premature optimization if you will, but I have a feeling that #send is somewhat better to use in the base case because of the cost of method binding - at least one new object needs to be created every time we're binding). This block gives user the ability to write method like this: chain_method :hello def hello p "Hello" yield end If call is recursive than it is either 'super' call or plain recursion, we don't care which one it is. In this case we're binding method reference that is accessible from closure we're in to self and call it via #call, thus bypassing polymorphic call that caused troubles in the first place. Makes sense? I'll be happy to hear your take on this. > Kind regards > > robert -- Posted via http://www.ruby-forum.com/.