Bug #21316
openNamespaces leak with permanent names
Description
Namespaces are not transparent for this program
C = Class.new
C.name == 'C'
because under a non-main user namespace, the name of C
has the namespace as a prefix.
Updated by mame (Yusuke Endoh) about 17 hours ago
This is definitely not ideal. Class#name
could end up referring to a different constant.
# main.rb
NS = Namespace.new
NS.require "./sub"
# sub.rb
class C; end
p C #=> expected: C
#=> actual: NS::C ← This could refer to a different constant, which is problematic
@tagomoris (Satoshi Tagomori) suggested that Class#name
should behave as follows:
- If the current namespace is at the front, omit it and just return
"C"
- Otherwise, return something like
"#<Namespace: ...>::C"
# main.rb
NS = Namespace.new
NS.require "./sub"
p NS::C #=> #<Namespace: ...>::C
# sub.rb
class C; end
p C #=> C
Updated by fxn (Xavier Noria) about 15 hours ago
· Edited
Main problem here is that there are many programs that depend on that name.
They may store it somewhere for later const_get
. For example, polymorphic Active Record associations store names in the database. Users may also set class and module names in configuration, to be later lazily loaded. Associations do this to specify the name of the target model. Active Job queue adapters are configured this way too, etc.
Also, the other way around. You may pass the name to a callback, and when the program detects a class or module object with that name is created (by looking at Module#name
), the callback is invoked. This is the case in on_load
callbacks in Zeitwerk, for instance.
That the name matches the constant path of the class or module being defined is a strong property of the language people rely on.
Updated by Eregon (Benoit Daloze) about 11 hours ago
Right I think in the namespace defining the class/module the Module#name
needs to not have a prefix, or it will break many gems.
OTOH when used outside, it could be very confusing without a prefix.
suggested that Class#name should behave as follows:
Yeah, that's probably the best trade-off, although of course it means the Module#name for a given Module changes based on which Namespace is the current one.
BTW, I think the main namespace constants should also be prefixed when seen in another namespace, currently it's not the case:
$ RUBY_NAMESPACE=1 ruby -ve 'main = Namespace.current; ns = Namespace.new; class C; end; ns::MAIN_C = C; File.write "ns.rb", "p MAIN_C; p eval(MAIN_C.name)"; ns.require "./ns"'
ruby 3.5.0dev (2025-05-10T07:50:29Z namespace-on-read-.. bd4f57f96b) +PRISM [x86_64-linux]
ruby: warning: Namespace is experimental, and the behavior may change in the future!
See doc/namespace.md for know issues, etc.
C
(eval at /home/eregon/ns.rb:1):1:in '<top (required)>': uninitialized constant #<Namespace:24,user,optional>::C (NameError)
from /home/eregon/ns.rb:1:in 'Kernel#eval'
from /home/eregon/ns.rb:1:in '<top (required)>'
from -e:1:in 'Namespace#require'
from -e:1:in '<main>'