Skip to content

Commit

Permalink
Split explicit namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
fxn committed Oct 12, 2024
1 parent b6fe78b commit b9261b9
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 20 deletions.
3 changes: 3 additions & 0 deletions lib/zeitwerk/cref.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
class Zeitwerk::Cref
include Zeitwerk::RealModName

# @sig Module
attr_reader :mod

# @sig Symbol
attr_reader :cname

Expand Down
36 changes: 26 additions & 10 deletions lib/zeitwerk/explicit_namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
Expand All @@ -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

Expand All @@ -69,7 +85,7 @@ module Synchronized
MUTEX.synchronize { super }
end

internal def registered?(...)
internal def registered_cname?(...)
MUTEX.synchronize { super }
end

Expand Down
2 changes: 1 addition & 1 deletion lib/zeitwerk/loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/lib/zeitwerk/test_autovivification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions test/lib/zeitwerk/test_unload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions test/lib/zeitwerk/test_unregister.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,29 @@ 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
registry.register_loader(loader2)
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

assert !registry.loaders.include?(loader1)
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
Expand Down

0 comments on commit b9261b9

Please sign in to comment.