Search code examples
rubyglobzeitwerk

Push single file to Zeitwerk loader


For a Roda app, I'd like to include a single file for autoloading by Zeitwerk while ignoring all the others in that directory. The simplified layout looks like this:

config
  application.rb
  puma.rb
  (...)
lib
  my_class.rb
  (...)

To setup Zeitwerk, this would be perfect but not possible:

loader = Zeitwerk::Loader.new
loader.push_dir('lib')
loader.push_file('config/application.rb')       # <--- no such thing as push_file

if ENV['RACK_ENV'] == 'development'
  loader.enable_reloading
  loader.setup
  run ->(env) do
    loader.reload                               # <--- full reload on every request
    Application.call(env)
  end
else
  Zeitwerk::Loader.eager_load_all
  loader.setup
  run Application.freeze.app
end

Excluding all but application.rb using loader.ignore won't cut it neither, Ruby glob patterns don't implement negation it appears.

I could of course move application.rb into lib/ (and I might have to), but I'd rather have it in config/. Any way to make this fly?


Solution

  • First, to answer the question, there is no way to push one single file. I might consider adding that feature, but need to think about it.

    Then, in Roda, you could argue that the application class is not configuration. That class dispatches and has application logic. I'd consider moving that file out of the config directory for conceptual reasons, regardless.

    If you totally want to keep it in that directory, you can "reload" manually in development (untested, written on the spot):

    loader.reload
    load "#{__dir__}/config/application.rb"
    Application.call(env)
    

    Point is that load, unlike require, executes always the file.

    I am not familiar with Roda and it could be the case that running the file multiple times messes with internal state. If that was the case, you could remove the constant before the load call:

    Object.send(:remove_const, :Application) if defined?(Application)
    

    I've seen Roda has an inherited hook, but does not seem to cache subclasses in internal state.

    Additionally, for production, you normally want to run loader.setup before eager_load_all, so that your application is eager loaded too.