From: "fxn (Xavier Noria) via ruby-core" Date: 2023-05-14T10:26:49+00:00 Subject: [ruby-core:113478] [Ruby master Feature#19633] Allow passing block to `Kernel#autoload` as alternative to second `filename` argument Issue #19633 has been updated by fxn (Xavier Noria). The question was: If `/foo` is a root directory, and you want to associate to it an anonymous module as custom namespace, how would `/foo/bar.rb` look like? So, `/foo/bar.rb` is not `Foo::Bar`. We can also assume, your project has more than one file. OK, the answer in Zeitwerk is that you need the namespace to be globally available: for example ```ruby $__MY_GEM = Module.new loader.push_dir('/foo', namespace: $__MY_GEM) ``` and then you cannot: ```ruby module $__MY_GEM class Bar end end ``` you necessarily need to use this style: ```ruby class $__MY_GEM::Bar end ``` * I have never seen this used * Inside that body, you cannot refer to other constants in the project using a constant reference as usual, because `$__MY_GEM` is not in the nesting. You have to use constant paths. * This does not solve isolation, because `$__MY_GEM` is as reachable as `MyGem` is. This is why Zeitwerk does not support anonymous namespaces today. It would involve work and more complixity in the code base, and probably for nothing. Wanted to close this circle about the reason anonymous namespaces are not supported today in Zeitwerk. I'll reflect about the rest of your comments! ---------------------------------------- Feature #19633: Allow passing block to `Kernel#autoload` as alternative to second `filename` argument https://bugs.ruby-lang.org/issues/19633#change-103061 * Author: shioyama (Chris Salzberg) * Status: Open * Priority: Normal ---------------------------------------- `Kernel#autoload` takes two arguments, a symbol `module` representing the constant to be autoloaded, and a `filepath` to load the constant from (with `require`). Currently, Zeitwerk has to [monkeypatch `Kernel#require`](https://github.com/fxn/zeitwerk/blob/a7c4a983df0f4e4058f32c610dac1e8b99f687da/lib/zeitwerk/kernel.rb) to fetch the loader for the file being loaded, then run the original (aliased) `require`, then run autoload callbacks. In addition to the monkeypatch, this also requires a registry (`Zeitwerk::Registry`) to map file paths to loaders, to know which loader should be used for a given autoload-triggered `require`. In fact, Zeitwerk has to _assume_ that the monkey-patched `require` call came from an autoload trigger; there is no way to really be sure of the source. If Ruby allowed passing a block as an alternative to the explicit filepath, then I think this could be improved and would eliminate the need for a monkeypatch. So something like this: ```ruby autoload(:B) do require "lib/b" # trigger callback, etc end ``` I am implementing a gem called [Im](https://github.com/shioyama/im) which is a fork of Zeitwerk, and in the case of this gem, such a feature would be even more useful. Im implements autoloads on anonymous modules by registering an autoload and then "catching" the require and converting it into a `load`, passing the module as the second argument (see [here](https://github.com/shioyama/im/blob/44ce348639a1aae563a5be7a40602761e9698d43/lib/im/kernel.rb).) This is currently quite tricky because, again, it's hard to know _where_ a `require` came from. In addition to removing the monkeypatch (inherited from Zeitwerk), Im would further benefit from the block argument because then it could simply access the module via a closure, rather than pulling it from a registry: ```ruby mod.autoload(:Foo) do load "lib/foo.rb", mod end ``` I don't know how hard or easy this would be to implement, but if there is interest in this as a feature I'd be happy to look into implementing it. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/