#!/usr/bin/env ruby #require 'facets/core/module/alias_method_chain' 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 # test 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