Logan Capaldo
9/8/2006 2:09:00 PM
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
>