From: "Martin S. Weber" Date: 2006-08-14T23:27:17+09:00 Subject: Calling non-existing methods works! (little magic??) Hoi Rubyists. I am kind of ... confused. Up till now I had the illusion you could only call methods on an object which you could also find via methods or via the included modules. Fine... Why does this output what it does then? Where is this method defined? Some kind of method lookup cache? How to force flushing it (besides redefining the methods of the receiver?) Thanks in advance for the help, -Martin --- snippiesnap: fun_delegate.rb --- class A; def a; "a"; end; end class B; def b; "b"; end; end # obvious: return difference of instance methods, ignoring "method_missing" def inst_meth_delta( klass1, klass2 ) klass1.instance_methods - klass2.instance_methods - [ "method_missing" ] end # obvious:P # if delegation is to be established, create an anonymous module, define # methods on it to delegate to the receiver, extend the sender with this # module. # if delegation is to be deleted, remove methods from the anonymous module. def fun_delegate(snd, rcv) return unless (snd && rcv) deinstalled=false; # maybe remove the delegation again? (class << snd ; included_modules ; end).each do |mod| # get the source object_id (obj which is delegated to) dd = mod.instance_variables.include?('@dd') && mod.instance_variable_get(:@dd) # deinstall methods if rcv is delegated to a second # time - or when rcv is :off. if dd && (dd == rcv.object_id || rcv == :off) then puts "Removing delegation from #{snd} to #{rcv} (mod: #{mod})" mod.instance_methods.each do |meth| mod.send(:remove_method, meth.intern) end deinstalled=true; mod.instance_variable_set(:@dd, nil) end end # ... or set it up? unless deinstalled (mod = Module.new).instance_variable_set(:@dd, rcv.object_id) puts "delegating from #{snd} to #{rcv} via #{mod}..." inst_meth_delta(rcv.class, snd.class).each {|meth| mod.send(:define_method, meth.intern) {|*args| rcv.send(meth.intern, *args) } } snd.extend(mod) end end a=A.new; b=B.new # obvious so far # nometherr begin puts a.b rescue puts $! end fun_delegate a,b # "b" begin puts a.b rescue puts $! end fun_delegate a,b # MAGIC!!! # "b" begin puts a.b rescue puts $! end # Fine, where is this "b" coming from? puts a.methods.find{|m| m=='b'} ;# -> nil puts a.methods - Kernel.methods ;# -> "a" puts a.singleton_methods ;# -> [] puts "#{(class << a ; included_modules ; end).collect {|mod| mod.instance_methods unless mod.name == "Kernel" }}" ;# -> [[], nil] --- snappiesnip: fun_delegate.rb --- --- output --- $ ruby -vw fun_delegate.rb ruby 1.8.4 (2005-12-24) [i386-netbsdelf] undefined method `b' for # delegating from # to # via #... b Removing delegation from # to # (mod: #) b nil a --- end ---