[ruby-core:121950] [Ruby Bug#21316] Namespaces leak with permanent names

Issue #21316 has been reported by fxn (Xavier Noria). ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by mame (Yusuke Endoh). This is definitely not ideal. `Class#name` could end up referring to a different constant. ```ruby # 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 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"` ```ruby # main.rb NS = Namespace.new NS.require "./sub" p NS::C #=> #<Namespace: ...>::C # sub.rb class C; end p C #=> C ``` ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113078 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by fxn (Xavier Noria). 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), passed to a callback (for example `on_load` callbacks in Zeitwerk), etc. That the name matches the constant path of the class or module being defined is a strong property of the language people rely on. ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113080 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by Eregon (Benoit Daloze). 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>' ``` ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113099 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by make_now_just (Hiroya Fujinami). I found an issue on `Marshal` and `Namespace` maybe related to this ticket. When dumping an object defined in a namespace using Marshal, the result will vary depending on whether the namespace is held as a variable or constant, and whether `Marshal.dump` is performed inside or outside the namespace. In other words, we have the following files: `ns.rb`: ```ruby class Foo def dump_in_ns = Marshal.dump(self) end ``` `main.rb`: ```ruby ns = Namespace.new ns.require("./ns.rb") NS = Namespace.new NS.require("./ns.rb") puts "var / in ns" begin ns::Foo.new.dump p :ok rescue => ex p :error p ex end puts puts "const / in ns" begin NS::Foo.new.dump p :ok rescue => ex p :error p ex end puts puts "var / out ns" begin Marshal.dump(ns::Foo.new) p :ok rescue => ex p :error p ex end puts puts "const / out ns" begin Marshal.dump(NS::Foo.new) p :ok rescue => ex p :error p ex end ``` Then, the result is as follows: ```console $ RUBY_NAMESPACE=1 ruby main.rb var / in ns :error #<NoMethodError: undefined method 'dump' for an instance of #<Namespace:0x000000012018ed88>::Foo> const / in ns :error #<NoMethodError: undefined method 'dump' for an instance of NS::Foo> var / out ns :error #<TypeError: can't dump anonymous class #<Namespace:0x000000012018ed88>::Foo> const / out ns :ok ``` I'm not sure this issue is completely related to this ticket, but `Marshal.dump` should work in any case. ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113195 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by Eregon (Benoit Daloze). @make_now_just In the first two cases, it should be `.dump_in_ns` not `.dump` ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113199 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by make_now_just (Hiroya Fujinami). @Eregon It has already been fixed. Thank you. ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113200 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by ko1 (Koichi Sasada). FYI: Java's case "OBJ09-J. Compare classes and not class names" https://wiki.sei.cmu.edu/confluence/display/java/OBJ09-J.+Compare+classes+an... ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113392 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by fxn (Xavier Noria). @ko1 yeah, in Ruby you can have two classes with the same permanent name today. You know that, but let me show an example for the archives: ```ruby c = Class.new C = c Object.send(:remove_const, :C) d = Class.new C = d p c.name == d.name # true ``` So, where possible, in arbitrary settings, you better work with class objects. But there are common scenarios (configuration, etc., I described a few above) in which you do not have the object and the natural way to refer to the class is by its name. Today, that is a common practice, and if you want to be able to load arbitrary code within a namespace, I think this has to be preserved. Otherwise, such code won't be loadable under a namespace. ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113393 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/

Issue #21316 has been updated by matz (Yukihiro Matsumoto). As #21335, we should not provide information that depends on namespace, until we fix high level API. Matz. ---------------------------------------- Bug #21316: Namespaces leak with permanent names https://bugs.ruby-lang.org/issues/21316#change-113600 * Author: fxn (Xavier Noria) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Namespaces are not transparent for this program ```ruby C = Class.new C.name == 'C' ``` because under a non-main user namespace, the name of `C` has the namespace as a prefix. -- https://bugs.ruby-lang.org/
participants (6)
-
Eregon (Benoit Daloze)
-
fxn (Xavier Noria)
-
ko1 (Koichi Sasada)
-
make_now_just (Hiroya Fujinami)
-
mame (Yusuke Endoh)
-
matz (Yukihiro Matsumoto)