0

I'm upgrading my Rails app from v5 to v6. The files are structured as:

lib
 |- container
   |- my_module_a
   |  |- my_class.rb
   |  |- class_two.rb
   |- my_module_b

my_module directory has no files in it, only my_module_a and my_module_b directories.

class_one.rb's contents:

module MyModuleA
  class MyClass
    # definitions...
  end
end

config/application.rb has:

 config.eager_load_paths += %W[
    #{config.root}/lib/container
    #{config.root}/lib/container/my_module_a
 ]

bin/rails zeitwerk:check prints All is good!

However, when running the rails server and it reaches the call of the MyClass class

obj = Container::MyModule::MyClass.new(...)

it prints

uninitialized constant Container::MyModuleA
valk
  • 9,363
  • 12
  • 59
  • 79
  • 1
    Remove the `MyApp::` part from your call, all the classes and modules that you define in your app are not nested in the app top level name. When you define a new `User` model, you're using it directly by calling `User` not `MyApp::User`. – Sébastien P. Feb 06 '23 at 13:26
  • @SébastienP. I updated the question's directory structure. It was confusing... – valk Feb 06 '23 at 13:35

2 Answers2

1

If you want to call Container::MyModuleA::MyClassA, the class definition should be:

module Container
  module MyModuleA
    class MyClass
      # definitions...
    end
  end
end

Your current directory structure and the class definition are different container/my_module_a/my_class vs MyModuleA::MyClass, the best practice is to prefix your classes with a module name that matches the directory tree. In development rails is disabling eager loading meaning that code is loaded on the fly (to reduce booting time) and when you call Container::MyModuleA::MyClassA, it will lookup for the container/my_module_a/my_class_a file, check the content to find Container::MyModuleA::MyClassA which is not defined right now are the class definition is not nested in the Container module.

More information about autoloading vs eager loading here: https://www.bigbinary.com/books/learn-rubyonrails-book/loading-behavior-of-ruby-on-rails-in-depth

Sébastien P.
  • 526
  • 3
  • 14
  • Please, note that this answer is not taking into account that `lib/container` is in the autoload paths, and therefore there is no `Container` namespace. – Xavier Noria Feb 07 '23 at 11:15
1

I suspect the description above has multiple typos:

  • I don't see a my_module directory
  • Since lib/container/my_module_a is in the autoload paths, the my_class.rb file in that directory is expected to define a top-level MyClass class, not MyModuleA::MyClass as shown above
  • Yet, zeitwerk:check passes

Would be cool to have real names and real code to avoid assuming things. If not possible, at least double-check and update the problem statement.

The main thing to take into account is that autoload paths have to comply with the documented project structure conventions. Anything pushed to the eager load paths is considered to be an autoload path too.

You should remove the configuration for #{config.root}/lib/container/my_module_a unless you know what you are doing and really need nested root directories.

Then, if you leave #{config.root}/lib/container in the configuration, the code shown above is mostly good because this configuration says lib/container is a root directory containing top-level constants. But then, existing code should accordingly NOT use a Container constant nobody is defining.

So,

obj = Container::MyModule::MyClass.new(...)

does not square with what you have, should be

obj = MyModuleA::MyClass.new(...)

Please don't hesitate to follow up if more help is needed.

Xavier Noria
  • 1,640
  • 1
  • 8
  • 10