fxn/zeitwerk

(Re-)Loading modules defined as a root namespace

richardmarbach opened this issue · 2 comments

I've recently been adding packwerk to a project, and I settled on using the recommended structure from packs rails with nested packs:

packs/
   namespace1/
      app/
         models/
           namespace1.rb
           namespace1/
      packs/
         sub_namespace/
            models/namespace1/sub_namespace/

This structure does introduce quite a lot of nested directories, so I tried using .push_dir("packs/namespace1/packs/sub_namespace/models", namespace: Namespace1::SubNamespace) to help reduce nesting.

Here, I ran across the issue of namespace1.rb no longer being loaded. This is a problem in my use case since the Namespace1 module defines the ActiveRecord table prefix:

module Namespace1
  def self.table_name_prefix = "prefix_"
end

As a workaround, the prefix would need to be added to every model in namespace1.

This is documented behaviour, but I was wondering if this restriction can be lifted.

fxn commented

Hey, sorry for not replying earlier, I just forgot!

When you boot, the loader has to scan the root directories and set autoloads for the first level of things that it finds. For example, given this structure:

app/controllers/users_controller.rb
app/models/user.rb

the loader sets this up on your behalf:

Object.autoload(:UsersController, '/abspath/to/app/controllers/users_controller.rb')
Object.autoload(:User, '/abspath/to/app/models/user.rb')

That is because Object is the default root namespace, and Object is something that already exists.

Similarly, if app/models represented the Foo namespace, then it would be:

Object.autoload(:UsersController, '/abspath/to/app/controllers/users_controller.rb')
Foo.autoload(:User, '/abspath/to/app/models/user.rb')

That means, the loader needs the Foo class/module object before things are ready for being autoloaded. That is why a custom root namespace has to be passed at configuration time. And that is why it cannot be a reloadable module, it has to exist, the loader needs the instance to set autoloads on it.

Hey, thanks for taking the time to reply and clearing up the behaviour!