Skip to content

Instantly share code, notes, and snippets.

@stevecj
Created March 13, 2012 18:03
Show Gist options
  • 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

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