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) 10 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.