On Sep 8, 2006, at 1:34 AM, ara.t.howard / noaa.gov wrote:

>
> i've been wanting a better alias_method for quite some time.   
> essentially i'd
> like a way out of the trap where executing
>
>   alias_method '__fubar__', 'fubar'
>
> goes haywire when __fubar__ already exists.  we've all seen it  
> happen before.
>
> anyhow.  the interface it'd like would be
>
>
>   class C
>     def m() 'a' end
>
>     push_method 'm'
>
>     def m() super + 'b' end
>   end
>
>   p C.new.m  #=> 'ab'
>
>
> what i've got is quite close, but no cigar.  it has a fundemental  
> problem with
> the way ruby scopes super which i'm too tired atttm to figure out.   
> i'm hoping
> i can go to bed and wake up to a nice patch ;-)  here's what i've got:
>
>
>     harp:~ > cat a.rb
>     class Module
>       def push_method m
>         this = self
>
>         include Module.new{
>           @m = this.instance_method m
>
>           this.module_eval{ remove_method m }
>
>           module_eval <<-code
>             def #{ m }(*a, &b)
>               um = ObjectSpace._id2ref #{ @m.object_id }
>               um.bind(self).call *a, &b
>             end
>           code
>         }
>       end
>     end
>
>     class C
>       def m
>         'a'
>       end
>       p new.m #=> 'a'
>
>
>       push_method 'm'
>
>
>       def m
>         super + 'b'
>       end
>       p new.m #=> 'ab'
>
>
>       push_method 'm'
>
>
>       def m
>         super + 'c'
>       end
>       p new.m #=> 'abc'
>     end
>
>
>
>     harp :~ > ruby a.rb
>     "a"
>     "ab"
>     a.rb:31:in `m': stack level too deep (SystemStackError)
>             from (eval):3:in `m'
>             from a.rb:31:in `m'
>             from (eval):3:in `m'
>             from a.rb:31:in `m'
>             from (eval):3:in `m'
>             from a.rb:31:in `m'
>             from (eval):3:in `m'
>             from a.rb:31:in `m'
>              ... 2343 levels...
>             from a.rb:31:in `m'
>             from (eval):3:in `m'
>             from a.rb:40:in `m'
>             from a.rb:42
>
>
> have at it - i'll be back in 8 hrs.  ;-)
>
>

I have this, it takes a different approach though:

module Patchable
   module ClassMethods
     def patch(method_name = nil, &new_body)
       if method_name
         method_name = method_name.to_sym
         pre_patched_versions[method_name] = instance_method 
(method_name)
         define_method(method_name, &new_body)
       else
         klass = Class.new
         imeths = klass.instance_methods
         klass.class_eval(&new_body)
         new_meths = klass.instance_methods - imeths
         new_meths.each do |m|
           pre_patched_versions[m.to_sym] = instance_method(m)
         end
         class_eval(&new_body)
       end
       self
     end

     def pre_patched_versions
       @pre_patched_versions ||= {}
     end
   end

   def hyper(*args, &block)
     meth_name = caller[0][/`([^']+)'/, 1].to_sym
     self.class.pre_patched_versions[meth_name].bind(self).call 
(*args, &block)
   end

   def self.included(other)
     other.extend(ClassMethods)
   end
end

class C
   include Patchable

   def m
     'a'
   end

   p new.m
   patch do
     def m
       hyper + 'b'
     end
   end

   p new.m

   patch do
     def m
       hyper + 'c'
     end
   end

   #p new.m doesn't work, infinite recursion
end

Darn. You seem to have made me discover a bug in my impl. It doesn't  
work for more than one level of patching per method. Well maybe  
someone will give me a patch too ;)




> -a
> -- 
> what science finds to be nonexistent, we must accept as  
> nonexistent; but what
> science merely does not find is a completely different matter... it  
> is quite
> clear that there are many, many mysterious things.
> - h.h. the 14th dalai lama
>