From: Brian Candler Date: 2005-03-04T19:25:21+09:00 Subject: 'include ' and infinite recursion Hello, I am having trouble understanding what's going on here, and I wonder if any guru here could enlighten me. I am getting infinite recursion when I don't expect it, and it's something to do with 'including' a module at the top level. $ ruby --version ruby 1.8.2 (2004-07-29) [i386-freebsd5] Here's a small bit of code which demonstrates the issue: ---- 8< ------------------------------------------------------------------ require 'ostruct' module MyApp O = OpenStruct.new O.testitem = "hello" def test "test ok" end def wibble return O.wibble # should return nil end end klass = class < [OpenStruct, Object, Kernel] include MyApp klass = class < [OpenStruct, Object, MyApp, Kernel] p O.testitem #==> "hello" p test #==> "test ok" p wibble #==> stack level too deep (SystemStackError) !!! ---- 8< ------------------------------------------------------------------ The idea is that I create an object 'MyApp::O', which uses method_missing to capture calls to unknown methods (that's how OpenStruct works internally). Also in module MyApp is a method 'wibble' that I want to call. It uses the value of 'MyApp::O.wibble' in its processing. Finally, I do 'include MyApp' to make the method "wibble" available at the top level, and then call it. At that point, I get an infinite loop. As far as I can tell: - I call wibble (= MyApp#wibble) - it calls MyApp::O.wibble - while searching for the methods inside this object, it finds MyApp#wibble and thus recurses, instead of falling through to method_missing. So my questions are: (1) Why is this method found? In other words, why does including MyApp at the top level affect the methods which are visible within MyApp::O ? (2) (probably related). After doing 'include MyApp', I see that the singleton class of MyApp::O includes MyApp in its ancestors. Why does it appear there? I thought that the 'include' operation would only affect the 'main' object. NOW: I have discovered I can solve this problem simply by putting "module_function :wibble" after the definition of method wibble. So my next question is: (3) Why does this fix things? I see that MyApp still appears in the ancestors of the singleton class of MyApp::O, but clearly the calling sequence must have changed somehow. I've read what there is about module_function in PickAxe v2, but it doesn't have much to say (p558, p355). As far as I can gather, this should duplicate my existing methods, so that they can be called as MyApp.test MyApp.wibble but it doesn't say anything about otherwise changing the behaviour of the existing methods that I defined. I couldn't find anything on the RubyGarden wiki either. Incidentally, I can remove OpenStruct and method_missing from the problem set; the resulting code is less practically useful, but still demonstrates the issue. ---- 8< ------------------------------------------------------------------ module MyApp O = Hash.new O[:testitem] = "hello" def test "test ok" end def wibble return O.wibble # should give 'method not found' end #module_function :wibble end include MyApp p O[:testitem] #==> "hello" p test #==> "test ok" p wibble #==> stack level too deep (SystemStackError) !!! ---- 8< ------------------------------------------------------------------ With module_function :wibble commented out, I get 8:in `wibble': stack level too deep (SystemStackError) With module_function :wibble uncommented, I get 8:in `wibble':private method `wibble' called for {:testitem=>"hello"}:Hash (NoMethodError) and again, I don't really understand why I get either of those messages. I thought that 'wibble' would not be visible at all to MyApp::O, rather than visible as a private method? And final related question: (4) How/why are the following two definitions different? module MyApp def wibble; puts "ok"; end module_function :wibble end MyApp.wibble #==> "ok" include MyApp wibble #==> "ok" ----- module MyApp def MyApp.wibble; puts "ok"; end end MyApp.wibble #==> "ok" include MyApp wibble #==> undefined local variable or method `wibble' for main:Object (NameError) Anyway, I'd much appreciate it if someone could explain, or could point me at some documentation which has enough detail about the method search mechanism and the semantics of 'include' and 'module_function' that I can work it out for myself. Many thanks... Brian.