Feature #20609
openNested module namespace misses fallback to top level
Description
Currently, Ruby falls back to the top-level constants if it cannot find the Module in the local scope. This can cause it to behave unintuitively.
module A
module B
MY_CONST = 'defined in A::B'
end
end
module X
end
module X::Y
# Ruby treats A::B the same as ::A::B, because module X::Y::A is not defined. IMO this should raise a Name error or atleast a warning.
module A::B
# This was meant to be scoped to X::Y::A::B'
MY_CONST = 'defined in X::Y::A::B'
end
puts(::A::B::MY_CONST) # => defined in X::Y::A::B
puts(A::B::MY_CONST) # => defined in X::Y::A::B
end
puts(X::Y::A::B::MY_CONST) # uninitialized constant X::Y::A (NameError)
I think Ruby should raise an error or atleast a clear warning explaining the module it is using has different nesting than what the coder might expect.
Updated by jeremyevans0 (Jeremy Evans) 5 months ago
This is expected. When you do:
module A::B
end
Understand that in Ruby, this is a general form of:
module (expression)::B
end
Ruby resolves expression
(e.g. constant lookup for A
), then defines a constant B
under it. In your example:
module X::Y
module A::B # (`expression`::B) where expression is A
end
end
The reference to A
inside X::Y
resolves to ::A
because X::Y
does not define a constant named A
.
The idea that the above code should define X::Y::A::B
cannot really work, because Ruby would have no knolwedge of whether to define X::Y::A
as a module or as a class.