Skip to content

Instantly share code, notes, and snippets.

@stevecj
Created March 13, 2012 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevecj/2030318 to your computer and use it in GitHub Desktop.
Save stevecj/2030318 to your computer and use it in GitHub Desktop.
Ruby module nesting and scope of constant names
# == 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]
@stevecj
Copy link
Author

stevecj commented Mar 13, 2012

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.

@stevecj
Copy link
Author

stevecj commented Mar 13, 2012

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