From: Logan Capaldo Date: 2006-09-08T23:09:07+09:00 Subject: Re: [CHALLENGE] better alias_method 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 >