Home > Enterprise >  uninitialized constant when reloading after enabling zeitwerk
uninitialized constant when reloading after enabling zeitwerk

Time:03-05

Having some trouble working out the migration from Classic to Zeitwerk.

After enabling zeitwerk and running rails s, everything seems to work. Then after saving a .rb file and refreshing, I'm seeing an "uninitialized constant" error when trying to require a file from the top level /lib.

Something is misconfigured w/ reloading, but I'm scratching my head trying to work out the details. I was under the impression that having a top level /lib folder is fine and using require to load files in that directory is compatible with Zeitwerk, but now I'm not so sure... ideas as to what I'm getting wrong?

Note: I'm not currently setting any specific eager_load_paths or autoload_paths

EDIT: updated with logging output as suggested by @Xavier

[email protected]: module CustomModule autovivified from directory *********/app/workers/custom_module
[email protected]: autoload set for CustomModule::Profiler, to be loaded from *********/app/workers/custom_module/profiler.rb
[email protected]: autoload set for CustomModule::AnotherProfiler, to be loaded from *********/app/workers/custom_module/another_profiler.rb

NameError - uninitialized constant CustomModule::AttributeParser
Did you mean?  NameParserConstants:
  app/models/user.rb:180:in `first_name'
  app/middleware/catch_json_parse_errors.rb:8:in `call'
  app/middleware/decompress_requests.rb:22:in `call'

CodePudding user response:

The namespace CustomModule is shared in parts of the project that are reloadable (under app), and also in parts where is not (under lib).

This is fine, it is supported. You only need to be deliberately think about load priorities, because if lib defines CustomModule::Foo and Rails believes CustomModule is reloadable, on reload nobody is loading CustomModule::Foo again, and require is idempotent, so CustomModule::Foo won't be found anymore.

The solution is to make sure lib defines the namespace, and Rails autoloaders reopen it. Essentially same as documented here. This can be done by issuing a require in an initializer that loads the namespace from lib, for example.

That way, when the autoloader scans the file system, it knows it is not responsible for managing CustomModule. It will descend. If there are child constants there everything will work as usual, and those constants will be reloaded, but the namespaces itself won't.

  • Related