From 076e0628a19ee88bba34149e677dedd30e89cc28 Mon Sep 17 00:00:00 2001 From: Chris Salzberg Date: Mon, 15 May 2023 22:35:13 +0900 Subject: [PATCH 1/2] Allow setting different namespace root Currently the root for any loaded namespace is the loader instance. But someone may want to set autoloads on a nother (named or anonymous) module, and there is no reason Im should not support this. With this change, it is now possible to pass a "root" when initializing a loader. This root (a module) is then used in place of the loader itself as the root of the (auto)loaded namespace. --- lib/im/error.rb | 3 +++ lib/im/kernel.rb | 2 +- lib/im/loader.rb | 18 ++++++++++++++---- lib/im/loader/config.rb | 2 +- test/lib/im/test_different_root.rb | 14 ++++++++++++++ test/support/loader_test.rb | 3 ++- 6 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 test/lib/im/test_different_root.rb diff --git a/lib/im/error.rb b/lib/im/error.rb index 532a0e0..5370167 100644 --- a/lib/im/error.rb +++ b/lib/im/error.rb @@ -19,6 +19,9 @@ def initialize end end + class SetupFinished < Error + end + class InvalidModuleName < Error end end diff --git a/lib/im/kernel.rb b/lib/im/kernel.rb index 13afb24..711ea15 100644 --- a/lib/im/kernel.rb +++ b/lib/im/kernel.rb @@ -18,7 +18,7 @@ def require(path) if loaded = !$LOADED_FEATURES.include?(feature_path) $LOADED_FEATURES << feature_path begin - load path, loader + load path, loader.root rescue => e $LOADED_FEATURES.delete(feature_path) raise e diff --git a/lib/im/loader.rb b/lib/im/loader.rb index 9f34869..d825aec 100644 --- a/lib/im/loader.rb +++ b/lib/im/loader.rb @@ -113,7 +113,11 @@ def pretty_print(q) # @sig Mutex attr_reader :mutex2 - def initialize + # @private + # @sig Module + attr_reader :root + + def initialize(root: self) super @module_prefix = "#{Im.cpath(self)}::" @@ -125,6 +129,7 @@ def initialize @module_cpaths = {} @mutex = Mutex.new @mutex2 = Mutex.new + @root = root @setup = false @eager_loaded = false @@ -132,6 +137,11 @@ def initialize Registry.register_autoloaded_module(get_object_hash(self), nil, self) end + def root=(root) + raise SetupFinished, "cannot set root after setup" if @setup + @root = root + end + # Sets autoloads in the root namespaces. # # @sig () -> void @@ -351,7 +361,7 @@ def all_dirs private # ------------------------------------------------------------------------------------- # @sig (String, Module?) -> void - def set_autoloads_in_dir(dir, parent = self) + def set_autoloads_in_dir(dir, parent = root) ls(dir) do |basename, abspath| begin if ruby?(basename) @@ -517,7 +527,7 @@ def raise_if_conflicting_directory(dir) # @sig (Module, Symbol) -> String def relative_cpath(parent, cname) - if self == parent + if root == parent cname.to_s elsif module_cpaths.key?(get_object_hash(parent)) "#{module_cpaths[get_object_hash(parent)]}::#{cname}" @@ -525,7 +535,7 @@ def relative_cpath(parent, cname) # If an autoloaded file loads an autoloaded constant from another file, we need to deduce the module name # before we can add the parent to module_cpaths. In this case, we have no choice but to work from to_s. mod_name = Im.cpath(parent) - current_module_prefix = "#{Im.cpath(self)}::" + current_module_prefix = "#{Im.cpath(root)}::" raise InvalidModuleName, "invalid module name for #{parent}" unless mod_name.start_with?(current_module_prefix) "#{mod_name}::#{cname}".delete_prefix!(current_module_prefix) end diff --git a/lib/im/loader/config.rb b/lib/im/loader/config.rb index 152deea..7b71e9d 100644 --- a/lib/im/loader/config.rb +++ b/lib/im/loader/config.rb @@ -81,7 +81,7 @@ module Im::Loader::Config attr_reader :on_unload_callbacks private :on_unload_callbacks - def initialize + def initialize(root: self) @inflector = Im::Inflector.new @logger = self.class.default_logger @tag = SecureRandom.hex(3) diff --git a/test/lib/im/test_different_root.rb b/test/lib/im/test_different_root.rb new file mode 100644 index 0000000..0d1a823 --- /dev/null +++ b/test/lib/im/test_different_root.rb @@ -0,0 +1,14 @@ +require "test_helper" + +class TestDifferentRoot < LoaderTest + test "setting a different root for loader" do + files = [["foo.rb", "module Foo; end"]] + + mod = Module.new + @loader = new_loader(root: mod, setup: false) + + with_setup(files) do + assert mod::Foo + end + end +end diff --git a/test/support/loader_test.rb b/test/support/loader_test.rb index 36ffdfd..bafe3df 100644 --- a/test/support/loader_test.rb +++ b/test/support/loader_test.rb @@ -9,10 +9,11 @@ def setup @loader = new_loader(setup: false) end - def new_loader(dirs: [], enable_reloading: true, setup: true) + def new_loader(dirs: [], enable_reloading: true, setup: true, root: nil) Im::Loader.new.tap do |loader| Array(dirs).each { |dir| loader.push_dir(dir) } loader.enable_reloading if enable_reloading + loader.root = root if root loader.setup if setup end end From 66a53f4d1c1d6bc9d218b9d450e67fd9acbfa36c Mon Sep 17 00:00:00 2001 From: Chris Salzberg Date: Mon, 15 May 2023 23:33:15 +0900 Subject: [PATCH 2/2] Allow setting root to Object --- lib/im/kernel.rb | 6 +++++- test/lib/im/test_different_root.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/im/kernel.rb b/lib/im/kernel.rb index 711ea15..3591a7b 100644 --- a/lib/im/kernel.rb +++ b/lib/im/kernel.rb @@ -18,7 +18,11 @@ def require(path) if loaded = !$LOADED_FEATURES.include?(feature_path) $LOADED_FEATURES << feature_path begin - load path, loader.root + if loader.root == Object + load path + else + load path, loader.root + end rescue => e $LOADED_FEATURES.delete(feature_path) raise e diff --git a/test/lib/im/test_different_root.rb b/test/lib/im/test_different_root.rb index 0d1a823..53d21b6 100644 --- a/test/lib/im/test_different_root.rb +++ b/test/lib/im/test_different_root.rb @@ -11,4 +11,16 @@ class TestDifferentRoot < LoaderTest assert mod::Foo end end + + test "setting Object as root" do + on_teardown { remove_const :Foo, from: Object } + + files = [["foo.rb", "module Foo; end"]] + + @loader = new_loader(root: Object, setup: false) + + with_setup(files) do + assert ::Foo + end + end end