diff --git a/lib/zeitwerk/kernel.rb b/lib/zeitwerk/kernel.rb index 1fed5a1..875381e 100644 --- a/lib/zeitwerk/kernel.rb +++ b/lib/zeitwerk/kernel.rb @@ -29,7 +29,6 @@ def require(path) required else loader.on_dir_autoloaded(path) - $LOADED_FEATURES << path true end else diff --git a/lib/zeitwerk/loader.rb b/lib/zeitwerk/loader.rb index a0b9621..c99ad17 100644 --- a/lib/zeitwerk/loader.rb +++ b/lib/zeitwerk/loader.rb @@ -123,7 +123,13 @@ def setup # @sig () -> void def unload mutex.synchronize do - abspaths_of_unloaded_crefs = Set.new + # We are going to keep track of the files that were required by our + # autoloads to later remove them from $LOADED_FEATURES, thus making them + # loadable by Kernel#require again. + # + # Directories are not stored in $LOADED_FEATURES, keeping track of files + # is enough. + unloaded_files = Set.new autoloads.each do |abspath, (parent, cname)| if parent.autoload?(cname) @@ -133,7 +139,7 @@ def unload # and the constant path would escape unloadable_cpath? This is just # defensive code to clean things up as much as we are able to. unload_cref(parent, cname) - abspaths_of_unloaded_crefs.add(abspath) + unloaded_files.add(abspath) if ruby?(abspath) end end @@ -144,14 +150,10 @@ def unload end unload_cref(parent, cname) - abspaths_of_unloaded_crefs.add(abspath) + unloaded_files.add(abspath) if ruby?(abspath) end - unless abspaths_of_unloaded_crefs.empty? - # We remove these abspaths from $LOADED_FEATURES because, otherwise, - # `require`'s idempotence would prevent newly defined autoloads from - # loading them again. - # + unless unloaded_files.empty? # Bootsnap decorates Kernel#require to speed it up using a cache and # this optimization does not check if $LOADED_FEATURES has the file. # @@ -163,7 +165,7 @@ def unload # Rails applications may depend on bootsnap, so for unloading to work # in that setting it is preferable that we restrict our API choice to # one of those methods. - $LOADED_FEATURES.reject! { |file| abspaths_of_unloaded_crefs.member?(file) } + $LOADED_FEATURES.reject! { |file| unloaded_files.member?(file) } end autoloads.clear diff --git a/test/lib/zeitwerk/test_eager_load.rb b/test/lib/zeitwerk/test_eager_load.rb index d9c3e12..1a90c2f 100644 --- a/test/lib/zeitwerk/test_eager_load.rb +++ b/test/lib/zeitwerk/test_eager_load.rb @@ -159,7 +159,6 @@ class MyGem remove_const :DbAdapters delete_loaded_feature "db_adapters/mysql_adapter.rb" - delete_loaded_feature "db_adapters" end $test_eager_load_eager_loaded_p = false @@ -213,7 +212,6 @@ module DbAdapters::MysqlAdapter delete_loaded_feature "ns/foo.rb" delete_loaded_feature "ns/bar.rb" - delete_loaded_feature "ns" end $test_eager_load_eager_loaded_p = false @@ -236,7 +234,6 @@ module DbAdapters::MysqlAdapter delete_loaded_feature "ns/foo.rb" delete_loaded_feature "ns/bar.rb" - delete_loaded_feature "ns" end $test_eager_load_eager_loaded_p = false @@ -246,7 +243,7 @@ module DbAdapters::MysqlAdapter ] with_files(files) do loader = new_loader(dirs: %w(lazylib eagerlib), enable_reloading: enable_reloading) - loader.do_not_eager_load('lazylib/ns') + loader.do_not_eager_load('lazylib/namespace') loader.eager_load assert $test_eager_load_eager_loaded_p diff --git a/test/lib/zeitwerk/test_exceptions.rb b/test/lib/zeitwerk/test_exceptions.rb index 0b8b15a..f311ae8 100644 --- a/test/lib/zeitwerk/test_exceptions.rb +++ b/test/lib/zeitwerk/test_exceptions.rb @@ -41,7 +41,6 @@ def assert_error_message(message, error) on_teardown do remove_const :CLI delete_loaded_feature 'cli/x.rb' - delete_loaded_feature 'cli' end files = [["cli/x.rb", "module CLI; X = 1; end"]] diff --git a/test/lib/zeitwerk/test_reloading.rb b/test/lib/zeitwerk/test_reloading.rb index 0b03c2f..372f53a 100644 --- a/test/lib/zeitwerk/test_reloading.rb +++ b/test/lib/zeitwerk/test_reloading.rb @@ -88,18 +88,6 @@ module Namespace; end end end - test "reloading works even if directories for managed namespaces get somehow pushed ot $LOADED_FEATURES" do - files = [["x/y.rb", "X::Y = true"]] - with_setup(files) do - assert X::Y - $LOADED_FEATURES << File.expand_path("x") - - loader.reload - - assert X::Y - end - end - test "reloading raises if the flag is not set" do e = assert_raises(Zeitwerk::ReloadingDisabledError) do loader = Zeitwerk::Loader.new @@ -120,7 +108,6 @@ module Namespace; end remove_const :Z delete_loaded_feature "z/a.rb" - delete_loaded_feature "z" end files = [ @@ -154,7 +141,6 @@ module Namespace; end remove_const :Z delete_loaded_feature "z/a.rb" - delete_loaded_feature "z" end files = [ diff --git a/test/lib/zeitwerk/test_require_interaction.rb b/test/lib/zeitwerk/test_require_interaction.rb index 12a618c..b4d570e 100644 --- a/test/lib/zeitwerk/test_require_interaction.rb +++ b/test/lib/zeitwerk/test_require_interaction.rb @@ -93,14 +93,6 @@ def assert_not_required(str) end end - test "autovivification pushes the directory of the implicit namespace to $LOADED_FEATURES" do - files = [["x/y.rb", "X::Y = true"]] - with_setup(files) do - assert X - assert_equal File.expand_path("x"), $LOADED_FEATURES[-1] - end - end - test "files deep down the current visited level are recognized as managed (implicit)" do files = [["foo/bar/baz/zoo/woo.rb", "Foo::Bar::Baz::Zoo::Woo = 1"]] with_setup(files, load_path: ".") do diff --git a/test/lib/zeitwerk/test_ruby_compatibility.rb b/test/lib/zeitwerk/test_ruby_compatibility.rb index 20dfb39..9e5ccd2 100644 --- a/test/lib/zeitwerk/test_ruby_compatibility.rb +++ b/test/lib/zeitwerk/test_ruby_compatibility.rb @@ -85,6 +85,19 @@ class C; end end end + # While unloading constants we leverage this property to avoid lookups in + # $LOADED_FEATURES for strings that we know are not going to be there. + test "directories are not included in $LOADED_FEATURES" do + with_files([]) do + FileUtils.mkdir("admin") + loader.push_dir(".") + loader.setup + + assert Admin + assert !$LOADED_FEATURES.include?(File.expand_path("admin")) + end + end + # We exploit this one to simplify the detection of explicit namespaces. # # Let's suppose `Admin` is an explicit namespace and scanning finds first a diff --git a/test/lib/zeitwerk/test_unloadable_cpath.rb b/test/lib/zeitwerk/test_unloadable_cpath.rb index 1d3232a..d6ec2d9 100644 --- a/test/lib/zeitwerk/test_unloadable_cpath.rb +++ b/test/lib/zeitwerk/test_unloadable_cpath.rb @@ -50,7 +50,6 @@ def self.name remove_const :M delete_loaded_feature "m/x.rb" delete_loaded_feature "m/y.rb" - delete_loaded_feature "m" remove_const :Z delete_loaded_feature "z.rb"