Project

General

Profile

Actions

Bug #21316

open

Namespaces leak with permanent names

Added by fxn (Xavier Noria) 1 day ago. Updated about 11 hours ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:121950]

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>'
Actions

Also available in: Atom PDF

Like1
Like1Like0Like0