Nesting and constants

module TT_Test

module Foo
CHEESE = ‘I am Foo!’
module Bar
def self.prod
puts CHEESE
end
def self.q
p Module.nesting
end
end # Bar
def self.q
p Module.nesting
end
end # Foo

module Foo::Biz
def self.prod
puts CHEESE
end
def self.q
p Module.nesting
end
end # Foo

end # TT_Test

TT_Test::Foo.q

=> [TT_Test::Foo, TT_Test]

TT_Test::Foo::Bar.q

=> [TT_Test::Foo::Bar, TT_Test::Foo, TT_Test]

TT_Test::Foo::Bar.q

=> [TT_Test::Foo::Bar, TT_Test]

TT_Test::Foo.constants

=> [“Biz”, “Bar”, “CHEESE”]

My question is: Why is the constant scope different between Bar and Biz

  • despite that they both exist under TT_Test::Foo ? I mean, if TT_Test
    got a constant named “Foo::Biz” I would have understood the constant
    lookup.

On Tue, Aug 7, 2012 at 4:02 PM, Thomas T.
[email protected]wrote:

module TT_Test

end # Bar
def self.q

TT_Test::Foo::Bar.q
got a constant named “Foo::Biz” I would have understood the constant
lookup.

The module keyword pushes to the nesting the module it has opened
(either
just created or reopened).

Therefore, in

module M
  module N
    p Module.nesting # => [M::N, M]
  end
end

because first we open M, and then in a nested call we open M::N. But on
the
other hand

module M::N
  p Module.nesting # => [M::N]
end

opens M::N and that’s it. Nesting gets just that one module object
pushed.

Note that what we push are module objects, not constants:

module M
end

N = M

module N
  p Module.nesting # => [M]
end

In the example above, N evaluates to the module object stored in the
constant M, that’s the module object being reopened and therefore pushed
to
the nesting.

Hi,

Module.nesting lists all modules that lexically surround the method
call. So it only takes into account the modules that are actually
present in the form of

module …
module …

end
end

If a parent module is only mentioned in the name, it’s ignored.

This has just recently been reported to Ruby’s issue tracker:

– Matma R.

Bartosz Dziewoński wrote in post #1071556:

This has just recently been reported to Ruby’s issue tracker:
Feature #6810: `module A::B; end` is not equivalent to `module A; module B; end; end` with respect to constant lookup (scope) - Ruby master - Ruby Issue Tracking System

– Matma R.

The comments there reflects what Matz mentions here:

But I still don’t understand why this is considered expected behaviour.
All I can find is technical explanation to what happens - but not the
reason and logic for it. From my point of view it’s counter-intuitive
and I cannot see a use-case for it.

On Tue, Aug 7, 2012 at 6:06 PM, Thomas T.
[email protected]wrote:

The comments there reflects what Matz mentions here:

But I still don’t understand why this is considered expected behaviour.

Well, it is expected behavior because that’s the way Ruby works.

A given module can be stored in a hundred constants in dozens of
differently nested constant paths. Constants are largely irrelevant to
Ruby, Ruby semantics are defined around module and class objects.

So, the rule is that whatever module object is opened by the module
keyword, that’s what gets pushed to the nesting. It is simple, and it
works
in all cases. It does not matter the constant path used to reach the
module
object. Given (untested code):

module M
  N = Module.new
end

module A
  module B
    C = M::N
  end
end

module M::N
  # (1)
end

module A::B::C
  # (2)
end

X = M::N
module X
  # (3)
end

the nesting in (1), (2), and (3) is the same (M::N). The fact that we
got
the M::N module via X or A::b::C is irrelevant. And the fact that we
reach
it via M::N is equally irrelevant by the same principle. Constants do
not
matter
, Ruby only cares about the module objects they yield.

In my view all is round and follows those basic axioms, but of course
the
only one who can ensure which is the rationale is Matz himself.