fxn / zeitwerk

Efficient and thread-safe code loader for Ruby
MIT License
1.99k stars 118 forks source link

(Re-)Loading modules defined as a root namespace #276

Closed richardmarbach closed 10 months ago

richardmarbach commented 12 months ago

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 10 months ago

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.

richardmarbach commented 10 months ago

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