From: transfire@... Date: 2006-07-14T13:40:16+09:00 Subject: Re: Preferred monkeypatching technique Daniel DeLorme wrote: > Tom Werner wrote: > > Allow me to present a scenario: > > > > class Firetruck > > def put_out_fire(options = {}) > > # code > > end > > end > > Here's what I'd do to keep it clean: > > module FastFiretruck > def initialize(*args) > super > self.extend FastFiretruck::Obj > end > module Obj > def put_out_fire(options = {}) > super({:fast => "please!"}.merge(options)) > end > end > end > Firetruck.class_eval{ include FastFiretruck } That only works because there is no initialize method defined in Firetruck. You might as well have done: class Firetruck def initialize(*args) super self.extend FastFiretruck end end module FastFiretruck def put_out_fire(options = {}) super({:fast => "please!"}.merge(options)) end end If you're just after one method you can alway use Facets' module#wrap_method. If you want to do aspecting (is that what this "monkeypatching" is all about?), well I would love to see a Cut implementation, but short of that, I've been working on #instance_interception. It still has some kinks, but it essentially works. class Module def instance_interception(&block) @instance_interception ||= Module.new do def self.append_features(mod) append_features_without_instance_interception( mod ) end end @instance_interception.module_eval(&block) if block_given? @instance_interception end private :instance_interception alias_method :append_features_without_instance_interception, :append_features # Append features def append_features( mod ) aspect = instance_interception aspect.__send__( :append_features_without_instance_interception, mod ) aspect.instance_methods.each do |meth| if mod.method_defined?( meth ) aspect.advise( mod, meth ) end end append_features_without_instance_interception( mod ) #if mod.instance_of? Module aspect.__send__( :append_features_without_instance_interception, mod.__send__(:instance_interception) ) #end end # Apply the around advice. def advise( mod, meth ) advice = instance_method( meth ) instance_target = mod.instance_method(meth) mod.__send__( :define_method, meth ) { |*args| #, &blk| target = instance_target.bind( self ) (class << target; self; end).class_eval { define_method( :super ){ call( *args ) } } advice.bind( self ).call( target, *args ) #, &blk ) } end # If a method is added to the module/class that is advised. def method_added( meth ) return if @method_added_short if instance_interception.method_defined?( meth ) include instance_interception @method_added_short = true instance_interception.advise( self, meth ) @method_added_short = false end end end # EXAMPLE module A def f ; "F" ; end def g ; "G" ; end instance_interception do def f( target, *args, &blk ) '{' + target.super + '}' end def g( target, *args, &blk ) '{' + target.super + '}' end end end class X def f ; super ; end include A def g ; super ; end end x = X.new p x.f p x.g T.