Issue #6810 has been updated by fxn (Xavier Noria).


Yes, this is expected.

The resolution algorithm first checks the modules in the nesting (in their very constants table, it ignores their ancestors), then the ancestors of the module the constants appears in (the first one in the nesting, or Object if the nesting is empty), and if the former is a module, then Object is checked by hand (and it still tries const_missing if all fails).

The key to understand those examples, in addition to the algorithm, is to be aware of the respective nestings:

    module A
      module B
        Module.nesting # => [A::B, A]
      end
    end

whereas

    module A::B
      Module.nesting # => [A::B]
    end

In the second case A is not checked, since it is not in the nesting, it is not an ancestor of B, and it is not Object.

The nesting is only modified by the class and module keywords, and by opening a singleton class with class << object (and some other obscure cases related to string eval'ing). In particular, the nesting inside and outside class methods defined with def self... is the same.
----------------------------------------
Bug #6810: `module A::B; end` is not equivalent to `module A; module B; end; end` with respect to constant lookup (scope)
https://bugs.ruby-lang.org/issues/6810#change-28512

Author: alexeymuranov (Alexey Muranov)
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: 
ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin10.8.0]


=begin
Is this the expected behavior?  To me it is rather surprising:

 N = 0

 module A
   module B
     def self.f; N; end
   end
   N = 1
 end

 A::B.f # => 1

but

 N = 0

 module A; end

 module A::B
   def self.f; N; end
 end

 module A
   N = 1
 end

 A::B.f # => 0

Even more striking:

 module A
   module B
     def self.f; N; end
   end
 end

 N = 0

 A::B.f # => 0

 A::N = 1

 puts A::B.f # => 1

 A::B::N = 2

 A::B.f # => 2

but

 module A; end

 module A::B
   def self.f; N; end
 end

 N = 0

 A::B.f # => 0

 A::N = 1

 A::B.f # => 0

 A::B::N = 2

 A::B.f # => 2


=end


-- 
http://bugs.ruby-lang.org/