From b9261b9268d1688f9d19aa6a29cb8725b76a86ea Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 12 Oct 2024 09:52:42 +0200 Subject: [PATCH] Split explicit namespaces --- lib/zeitwerk/cref.rb | 3 ++ lib/zeitwerk/explicit_namespace.rb | 36 ++++++++++++++++------ lib/zeitwerk/loader.rb | 2 +- test/lib/zeitwerk/test_autovivification.rb | 2 +- test/lib/zeitwerk/test_unload.rb | 8 ++--- test/lib/zeitwerk/test_unregister.rb | 8 ++--- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/lib/zeitwerk/cref.rb b/lib/zeitwerk/cref.rb index 53050880..21398d1f 100644 --- a/lib/zeitwerk/cref.rb +++ b/lib/zeitwerk/cref.rb @@ -13,6 +13,9 @@ class Zeitwerk::Cref include Zeitwerk::RealModName + # @sig Module + attr_reader :mod + # @sig Symbol attr_reader :cname diff --git a/lib/zeitwerk/explicit_namespace.rb b/lib/zeitwerk/explicit_namespace.rb index c7e96dc9..8ec24ee2 100644 --- a/lib/zeitwerk/explicit_namespace.rb +++ b/lib/zeitwerk/explicit_namespace.rb @@ -8,9 +8,16 @@ module Zeitwerk # Loaders that reopen namespaces owned by other projects are responsible for # loading their constant before setup. This is documented. module ExplicitNamespace # :nodoc: all - # Maps cpaths of explicit namespaces with their corresponding loader. - # Entries are added as the namespaces are found, and removed as they are - # autoloaded. + # Maps cnames of explicit namespaces in Object with their corresponding + # loader. Entries are added as the namespaces are found, and removed as they + # are autoloaded. + # + # @sig Hash[Symbol => Zeitwerk::Loader] + @cnames = {}.compare_by_identity + + # Maps cpaths of explicit (nested) namespaces with their corresponding + # loader. Entries are added as the namespaces are found, and removed as they + # are autoloaded. # # @sig Hash[String => Zeitwerk::Loader] @cpaths = {} @@ -23,32 +30,41 @@ class << self # managed by `loader`. # # @sig (String, Zeitwerk::Loader) -> void - internal def register(cpath, loader) - @cpaths[cpath] = loader + internal def register(cref, loader) + if Object.equal?(cref.mod) + @cnames[cref.cname] = loader + else + @cpaths[cref.path] = loader + end end # @sig (String) -> Zeitwerk::Loader? internal def loader_for(mod, cname) - cpath = Object.equal?(mod) ? cname.name : "#{real_mod_name(mod)}::#{cname}" - @cpaths.delete(cpath) + if Object.equal?(mod) + @cnames.delete(cname) + else + @cpaths.delete("#{real_mod_name(mod)}::#{cname}") + end end # @sig (Zeitwerk::Loader) -> void internal def unregister_loader(loader) + @cnames.delete_if { _2.equal?(loader) } @cpaths.delete_if { _2.equal?(loader) } end # This is an internal method only used by the test suite. # # @sig (String) -> Zeitwerk::Loader? - internal def registered?(cpath) - @cpaths[cpath] + internal def registered_cname?(cname) + @cnames[cname] end # This is an internal method only used by the test suite. # # @sig () -> void internal def clear + @cnames.clear @cpaths.clear end @@ -69,7 +85,7 @@ module Synchronized MUTEX.synchronize { super } end - internal def registered?(...) + internal def registered_cname?(...) MUTEX.synchronize { super } end diff --git a/lib/zeitwerk/loader.rb b/lib/zeitwerk/loader.rb index cee1007f..9d1df3ed 100644 --- a/lib/zeitwerk/loader.rb +++ b/lib/zeitwerk/loader.rb @@ -553,7 +553,7 @@ def all_dirs # @sig (Zeitwerk::Cref) -> void private def register_explicit_namespace(cref) - ExplicitNamespace.__register(cref.path, self) + ExplicitNamespace.__register(cref, self) end # @sig (String) -> void diff --git a/test/lib/zeitwerk/test_autovivification.rb b/test/lib/zeitwerk/test_autovivification.rb index 5255e118..98163f24 100644 --- a/test/lib/zeitwerk/test_autovivification.rb +++ b/test/lib/zeitwerk/test_autovivification.rb @@ -63,7 +63,7 @@ module Namespace; end ["rd2/admin/y.rb", "Admin::Y = true"] ] with_setup(files) do - assert !Zeitwerk::ExplicitNamespace.__registered?("Admin") + assert !Zeitwerk::ExplicitNamespace.__registered_cname?(:Admin) end end diff --git a/test/lib/zeitwerk/test_unload.rb b/test/lib/zeitwerk/test_unload.rb index 5550faf1..281f6822 100644 --- a/test/lib/zeitwerk/test_unload.rb +++ b/test/lib/zeitwerk/test_unload.rb @@ -131,14 +131,14 @@ def self.name ] with_files(files) do la = new_loader(dirs: "a") - assert Zeitwerk::ExplicitNamespace.__registered?("M") == la + assert Zeitwerk::ExplicitNamespace.__registered_cname?(:M) == la lb = new_loader(dirs: "b") - assert Zeitwerk::ExplicitNamespace.__registered?("X") == lb + assert Zeitwerk::ExplicitNamespace.__registered_cname?(:X) == lb la.unload - assert_nil Zeitwerk::ExplicitNamespace.__registered?("M") - assert Zeitwerk::ExplicitNamespace.__registered?("X") == lb + assert_nil Zeitwerk::ExplicitNamespace.__registered_cname?(:M) + assert Zeitwerk::ExplicitNamespace.__registered_cname?(:X) == lb end end diff --git a/test/lib/zeitwerk/test_unregister.rb b/test/lib/zeitwerk/test_unregister.rb index 738142e7..b18e5603 100644 --- a/test/lib/zeitwerk/test_unregister.rb +++ b/test/lib/zeitwerk/test_unregister.rb @@ -10,7 +10,7 @@ class TestUnregister < LoaderTest registry.gem_loaders_by_root_file["dummy1"] = loader1 registry.register_autoload(loader1, "dummy1") registry.register_inception("dummy1", "dummy1", loader1) - Zeitwerk::ExplicitNamespace.__register("dummy1", loader1) + Zeitwerk::ExplicitNamespace.__register(Zeitwerk::Cref.new(Object, :"dummy"), loader1) loader2 = Zeitwerk::Loader.new registry = Zeitwerk::Registry @@ -18,7 +18,7 @@ class TestUnregister < LoaderTest registry.gem_loaders_by_root_file["dummy2"] = loader2 registry.register_autoload(loader2, "dummy2") registry.register_inception("dummy2", "dummy2", loader2) - Zeitwerk::ExplicitNamespace.__register("dummy2", loader2) + Zeitwerk::ExplicitNamespace.__register(Zeitwerk::Cref.new(Object, :"dummy2"), loader2) loader1.unregister @@ -26,13 +26,13 @@ class TestUnregister < LoaderTest assert !registry.gem_loaders_by_root_file.values.include?(loader1) assert !registry.autoloads.values.include?(loader1) assert !registry.inceptions.values.any? {|_, l| l == loader1} - assert !Zeitwerk::ExplicitNamespace.__registered?("dummy1") + assert !Zeitwerk::ExplicitNamespace.__registered_cname?(:"dummy1") assert registry.loaders.include?(loader2) assert registry.gem_loaders_by_root_file.values.include?(loader2) assert registry.autoloads.values.include?(loader2) assert registry.inceptions.values.any? {|_, l| l == loader2} - assert Zeitwerk::ExplicitNamespace.__registered?("dummy2") == loader2 + assert Zeitwerk::ExplicitNamespace.__registered_cname?(:"dummy2") == loader2 end test 'with_loader yields and unregisters' do