Created
March 13, 2012 18:03
-
-
Save stevecj/2030318 to your computer and use it in GitHub Desktop.
Ruby module nesting and scope of constant names
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# == Module name assignment == | |
# A Ruby module's name is determined when it first becomes | |
# assigned as the value of a top-level constant or of a | |
# constant in a module that has received a name. | |
# The module's name represents the hierarchy of containership, | |
# and is not dependent upon the execution path of the code | |
# that defined the module (as opposed to the module nesting | |
# for defined methods -- see below). | |
p [self, self.class, Module.nesting] # => [main, Object, []] | |
a = Module.new | |
p [a, a.name] # => [#<Module:0x100182918>, ""] | |
a::AB = Module.new | |
p [a::AB, a::AB.name] # => [#<Module:0x100182800>, ""] | |
a::AB::ABC = Module.new | |
p [a::AB::ABC, a::AB::ABC.name] # => [#<Module:0x1001826e8>, ""] | |
A = a | |
p [a, a.name] # => [A, "A"] | |
p [a::AB, a::AB.name] # => [A::AB, "A::AB"] | |
p [a::AB::ABC, a::AB::ABC.name] # => [A::AB::ABC, "A::AB::ABC"] | |
# == Module nesting and scope of constants == | |
# Ruby looks first at for constants defined directly on a | |
# method's class, inherited class, and included modules (see | |
# http://coderrr.wordpress.com/2008/03/11/constant-name-resolution-in-ruby/ | |
# for a good demonstration of that), and then starts walking | |
# through the module nesting. | |
# The module nesting is NOT the same as the heirarchy of | |
# containership -- it is the sequence of module contexts the | |
# code execution path passed through before defining the | |
# method, which can jump across structural hierarchies, | |
# traverse them inside out, or whatever. | |
# Within the invocation of a method (except for special | |
# methods like eval, module_eval, etc.), the module nesting | |
# starts out the same as was in effect when the method was | |
# originally defined, and does not include anything from | |
# the nesting at the invocation point. | |
X = 'x' | |
module A | |
AX = 'ax' | |
module AB | |
ABX = 'abx' | |
module ABC | |
ABCX = 'abcx' | |
class << self | |
ABCSX = 'abcsx' | |
def m_a_ab_abc_s ; Module.nesting ; end | |
def m_a_ab_abc_s_eval(expr) ; eval(expr) ; end | |
end | |
def m_a_ab_abc ; Module.nesting ; end | |
def m_a_ab_abc_eval(expr) ; eval(expr) ; end | |
end | |
def ABC.m_a_ab ; Module.nesting ; end | |
def ABC.m_a_ab_eval(expr) ; eval(expr) ; end | |
end | |
abc_mod = AB::ABC | |
def abc_mod.m_a ; Module.nesting ; end | |
def abc_mod.m_a_eval(expr) ; eval(expr) ; end | |
AB::ABC.module_eval "def self.m_a_abc ; Module.nesting ; end" | |
AB::ABC.module_eval "def self.m_a_abc_eval(expr) ; eval(expr) ; end" | |
end | |
abc_mod = A::AB::ABC | |
def abc_mod.m ; Module.nesting ; end | |
def abc_mod.m_eval(expr) ; eval(expr) ; end | |
A::AB::ABC.module_eval "def A.m_abc_a ; Module.nesting ; end" | |
A::AB::ABC.module_eval "def A.m_abc_a_eval(expr) ; eval(expr) ; end" | |
module M | |
extend A::AB::ABC | |
end | |
def result_of | |
yield ; rescue Exception => e ; e | |
end | |
p result_of{ A::AB::ABC.m_a_ab_abc_s } # => [#<Class:A::AB::ABC>, A::AB::ABC, A::AB, A] | |
p result_of{ A::AB::ABC.m_a_ab_abc_s_eval("ABCSX") } # => "abcsx" | |
p result_of{ A::AB::ABC.m_a_ab_abc_s_eval("ABCX" ) } # => "abcx" | |
p result_of{ A::AB::ABC.m_a_ab_abc_s_eval("ABX" ) } # => "abx" | |
p result_of{ A::AB::ABC.m_a_ab_abc_s_eval("AX" ) } # => "ax" | |
p result_of{ A::AB::ABC.m_a_ab_abc_s_eval("X" ) } # => "x" | |
p result_of{ M.m_a_ab_abc } # => [A::AB::ABC, A::AB, A] | |
p result_of{ M.m_a_ab_abc_eval("ABCSX") } # => #<NameError: (eval):1:in `m_a_ab_abc_eval': uninitialized constant A::AB::ABC::ABCSX> | |
p result_of{ M.m_a_ab_abc_eval("ABCX" ) } # => "abcx" | |
p result_of{ M.m_a_ab_abc_eval("ABX" ) } # => "abx" | |
p result_of{ M.m_a_ab_abc_eval("AX" ) } # => "ax" | |
p result_of{ M.m_a_ab_abc_eval("X" ) } # => "x" | |
p result_of{ A::AB::ABC.m_a_ab } # => [A::AB, A] | |
p result_of{ A::AB::ABC.m_a_ab_eval("ABCSX") } # => #<NameError: (eval):1:in `m_a_ab_eval': uninitialized constant A::AB::ABCSX> | |
p result_of{ A::AB::ABC.m_a_ab_eval("ABCX" ) } # => #<NameError: (eval):1:in `m_a_ab_eval': uninitialized constant A::AB::ABCX> | |
p result_of{ A::AB::ABC.m_a_ab_eval("ABX" ) } # => "abx" | |
p result_of{ A::AB::ABC.m_a_ab_eval("AX" ) } # => "ax" | |
p result_of{ A::AB::ABC.m_a_ab_eval("X" ) } # => "x" | |
p result_of{ A::AB::ABC.m_a } # => [A] | |
p result_of{ A::AB::ABC.m_a_eval("ABCSX") } # => #<NameError: (eval):1:in `m_a_eval': uninitialized constant A::ABCSX> | |
p result_of{ A::AB::ABC.m_a_eval("ABCX" ) } # => #<NameError: (eval):1:in `m_a_eval': uninitialized constant A::ABCX> | |
p result_of{ A::AB::ABC.m_a_eval("ABX" ) } # => #<NameError: (eval):1:in `m_a_eval': uninitialized constant A::ABX> | |
p result_of{ A::AB::ABC.m_a_eval("AX" ) } # => "ax" | |
p result_of{ A::AB::ABC.m_a_eval("X" ) } # => "x" | |
p result_of{ A::AB::ABC.m } # => [] | |
p result_of{ A::AB::ABC.m_eval("ABCSX") } # => #<NameError: (eval):1:in `m_eval': uninitialized constant ABCSX> | |
p result_of{ A::AB::ABC.m_eval("ABCX" ) } # => #<NameError: (eval):1:in `m_eval': uninitialized constant ABCX> | |
p result_of{ A::AB::ABC.m_eval("ABX" ) } # => #<NameError: (eval):1:in `m_eval': uninitialized constant ABX> | |
p result_of{ A::AB::ABC.m_eval("AX" ) } # => #<NameError: (eval):1:in `m_eval': uninitialized constant AX> | |
p result_of{ A::AB::ABC.m_eval("X" ) } # => "x" | |
p result_of{ A::AB::ABC.m_a_abc } # => [A::AB::ABC, A] | |
p result_of{ A::AB::ABC.m_a_abc_eval("ABCSX") } # => #<NameError: (eval):1:in `m_a_abc_eval': uninitialized constant A::AB::ABC::ABCSX> | |
p result_of{ A::AB::ABC.m_a_abc_eval("ABCX" ) } # => "abcx" | |
p result_of{ A::AB::ABC.m_a_abc_eval("ABX" ) } # => #<NameError: (eval):1:in `m_a_abc_eval': uninitialized constant A::AB::ABC::ABX> | |
p result_of{ A::AB::ABC.m_a_abc_eval("AX" ) } # => "ax" | |
p result_of{ A::AB::ABC.m_a_abc_eval("X" ) } # => "x" | |
p result_of{ A.m_abc_a } # => [A::AB::ABC] | |
p result_of{ A.m_abc_a_eval("ABCSX") } # => #<NameError: (eval):1:in `m_abc_a_eval': uninitialized constant A::AB::ABC::ABCSX> | |
p result_of{ A.m_abc_a_eval("ABCX" ) } # => "abcx" | |
p result_of{ A.m_abc_a_eval("ABX" ) } # => #<NameError: (eval):1:in `m_abc_a_eval': uninitialized constant A::AB::ABC::ABX> | |
p result_of{ A.m_abc_a_eval("AX" ) } # => #<NameError: (eval):1:in `m_abc_a_eval': uninitialized constant A::AB::ABC::AX> | |
p result_of{ A.m_abc_a_eval("X" ) } # => "x" | |
p A::AB::ABC.m_a_abc_eval("A.m_abc_a") # => [A::AB::ABC] |
Perhaps, what this means is that for each self that is encountered in a code execution path, the binding holds adds an entry to Module.nesting for self if it is a module/class or self.class otherwise. Apparently, consecutive duplicate entries can be added since Object.module_eval "Object.module_eval 'Module.nesting'"
gives [Object, Object].
A also see now that Object.module_eval "self"
and Object.instance_eval "self"
yield Object, but Object.module_eval "Module.nesting"
gives [Object] and Object.instance_eval "Module.nesting"
gives [#Class:Object], so I guess there's still more I don't grok yet.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Actually, I now see that, per the bottom example, even before we start traversing the Module.nesting list, the context in which the method was defined is what matters, not what module the method was defined on. If it were the latter, then in the execution context of A.m_abc_a_eval, the A::AX constant should be accessible as AX, but it isn't.