From: Csaba Henk Date: 2005-01-27T04:50:47+09:00 Subject: Re: Injecting methods from one class into another. On 2005-01-25, Robert Klemme wrote: > class Object > def cast(target_class) > copy = case target_class > when Class; target_class.allocate > when Module; dup.extend(target_class) > else target_class.class.allocate > end > > instance_variables.each do |var| > copy.instance_variable_set(var, instance_variable_get(var)) > end > > copy > end > end > >> Then you can create a tmp copy of an instance with all instance > variables >> copied but a different class. So you can do >> foo.cast(Bar).bar_method_to_be_called(). Here's a bit more thorough implementation which lets you do: o = Object.new o.cuckoo_bind Array, :size o.size => 0 It also assumes you know what you are doing, ie. it relies on the methods of the object which adopts the method. I had to use metaclasses much more intensively than I wanted to (which was "not at all"), for reasons not totally understood by me. (Just try to strip them off, you'll see.) It can be modified to bind several methods of the same class, but I won't do that now. Tested w/ 1.8.2. #################### class Object def metaclass class << self self end end end class Object def all_methods a=[] %w(public protected private).each {|x| a += send(x+"_methods") } a end def cuckoo_bind klass, methname methname = methname.to_s # We could ask for the (Unbound)Method directly as an argument # But then I can't see a better way to extract klass, methname than # meth.inspect[/: ([^#]*)#([^>]*)/] # klass = eval $1; methname = $2 # and that's too hacky. It's a flaw in the API, imho. klass.instance_methods.include? methname or raise TypeError, "victim is unuseable" cuckoo = case klass when Class; cuckoo = klass.allocate when Module; cuckoo = Object.new; cuckoo.extend klass end # first we teach cuckoo what is easy to teach: our instance # variables instance_variables.each{|v| cuckoo.instance_variable_set v, instance_variable_get(v) } class << cuckoo def self.fetch data @fetched_data = data end end # cuckoo, we own you! warning_triggerers = %w(__id__ initialize __send__) bare_minimum = %w(fetch undef_method metaclass instance_variable_get) + [methname] + warning_triggerers (all_methods - bare_minimum).each{ |m| cuckoo.metaclass.fetch m class << cuckoo begin undef_method @fetched_data rescue NameError end end } cuckoo.metaclass.fetch self # fly the cuckoo def cuckoo.method_missing *args, &bl metaclass.instance_variable_get(:@fetched_data).send *args, &bl end # we also have to learn making good use of cuckoo # it's hard to import local names to the metaclass' namespace class << self def self.fetch data @fetched_data = data end end @zoo ||= {} @zoo[methname] = cuckoo metaclass.fetch :methname => methname, :cuckoo => cuckoo class << self # define_method's block can't have block argument, hence the eval hack eval "def #{@fetched_data[:methname]}(*args, &bl) @zoo['#{@fetched_data[:methname]}'].#{@fetched_data[:methname]}(*args,&bl) end" end method methname end end ####################