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.
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.
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.
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.
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::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.
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.