Search code examples
ruby-on-railsautoloaderzeitwerk

Rails custom project structure autoloading configs


I have a standard app that contains of sub-apps that we will slowly migrate into separate gems. The main problem is that each sub-app basically has an almost identical schema and very similar business logic that sometimes is really pretty much the same.

Currently, as a first step, we created a subfolder in lib/client with a structure like a typical rails app.

As an example, a concern for a ClientA looks like this/

module Client
  module ClientA
    module Concerns
      module MyConcern
       ...
    end 
  end
end

This all works fine and gets autoloaded by Zeitwerk.

However, when I want to create an initializer in a Rails way, I don't want to wrap it inside modules and make a class around it and this is where I am getting lib/clients/ClientA/config/initializers/custom_initializer.rb to define constant Clients::ClientA::Config::Initializers::CustomInitializer, but didn't (Zeitwerk::NameError)

This folder gets autoloaded like this

   config.autoload_paths += %W(#{config.root}/lib)
   config.autoload_paths += %W(#{config.root}/lib/client/**/*)

Is there a way how to

  • Blacklist the folder from being autoloaded by Zeitwerk?
  • Load it using require or in any other way without wrapping it in modules?

Solution

  • You can ignore parts of the project in zeitwerk using Loader#ignore. You can access rails zeitwerk autoloader via Rails.autoloaders.main.

    So you should be able to add something like this to your application.rb:

    Rails.autoloaders.main.ignore(Rails.root.join('lib/clients/*/config/initializers/*.rb'))
    

    You'll then have to require them manually, maybe in an initializer in the main app directory. You could create a config/initializers/clients.rb with something like:

    Dir[Rails.root.join('lib/clients/*/config/initializers/*.rb')].each do |filename|
      require filename
    end