I have a Rails folder with two files:
app/hub_spot/client.rb:
module HubSpot
class Client
app/hub_spot/create_contact_form.rb:
module HubSpot
class CreateContactForm
when I run the zeitwerk check I get:
expected file app/hub_spot/client.rb to define constant Client, but didn't
if I add:
config.autoload_paths << Rails.root.join("app/hub_spot")
config.eager_load_paths << Rails.root.join("app/hub_spot")
then I get a new error:
expected file app/hub_spot/create_contact_form.rb to define constant CreateContactForm, but didn't
it seems to complain about both files differently? Any way to tell Zeitwerk to look at the folder for the module name?
Rails uses a pretty quirky autoloader setup for historical/lazyness reasons. It's really more oriented around just organizing top level constants by purpose than pesky stuff like encapsulation.
Every subdirectory of the app
directory is automatically added as an root directory in Zeitwerk. Root directories are where Zeitwork expects top level constants to be found and are ignored when resolving a file path into a constant. Rails still uses the terms autoloading_paths
and eager_load_paths
which is a holdover from the classic autoloader used up until Rails 6 which are roughly equivilent.
Thus if you move your file into app/foo/hub_spot/client.rb
Zeitwork will have no problem finding it. Note that the name of the root level folder is actually inconsequential (except for us humans).
You can compare this to the typical gem setup which just has a single root directory (lib
) and where the code is expected to be nested in module and folder with the name of the gem.
Any way to tell Zeitwerk to look at the folder for the module name?
While you could add the app
directory itself as an autoloading root I would be wary of potential bugs.
Really the only reason for putting code in app
(except the expectation of other developers) is that you want to use the Rails autoloader setup so just embrace the wierdness and add another level of directory nesting so it's at least consistently wierd.
You could also just setup Zeitwerk to load your code from lib/my_namespace
to mirror how gems are setup (which is a lot less quirky).
it seems to complain about both files differently?
It doesn't. It's telling you that it expects the constants to be defined at the top level as the file path from it's perspective is just /create_contact_form.rb
. It's just checking different files.
config.autoload_paths << Rails.root.join("app/hub_spot")
is completely redundant as that happens by default anyways.
See: