'include <module>' and infinite recursion

B

Brian Candler

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 <<MyApp::O; self; end
p klass.ancestors
#==> [OpenStruct, Object, Kernel]

include MyApp

klass = class <<MyApp::O; self; end
p klass.ancestors
#==> [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.
 
B

Brian Schröder

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.

[snip]
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 ?

When you are doing a top level include, you include into Object.
Everything inherits from object, so every object has your function
now. Thats the reason why methods like puts work, they are private
methods of Object.
(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.

There is a chapter in pickaxe2 about classes and modules, maybe this
helps? I don't have time to look into it right now.
[snip]
(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)

The first creates two methods, while the second one creates only one message:
See:

------------------------------------------------- Module#module_function
module_function(symbol, ...) => self
------------------------------------------------------------------------
Creates module functions for the named methods. These functions may
be called with the module as a receiver, and also become availableindependently. The instance-method versions are made private. If
used with no arguments, subsequently defined methods become module
functions.

Many thanks...

I'm no guru, but I hope that I could help a bit.

another Brian
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top