ruby-on-rails

Migrating Rails 5.2 to 6.1, Rails.autoloaders.main nil except when load_defaults 6.1 present in application.rb


I am migrating an ancient app from Rails 5.2 to Rails 6.1 (preparatory to migrating it to 7). I have got the old project structure with a directory called decorators containing files extending models that have grown very fat (via ModelName.class_eval do...). In order to keep the structure working I have had to add some lines to application.rb:

module MyApp
  class Application < Rails::Application
    config.load_defaults 6.1 # do not delete this line

    # Don't eager load / autoload the decorators. They don't define classes but extend some.
    decorator_dir = "#{Rails.root}/app/decorators"
    Rails.autoloaders.main.ignore(decorator_dir)
    config.to_prepare do
      Dir.glob("#{decorator_dir}/**/*_decorator.rb").sort.each do |override|
        load override
      end
    end
...

When I delete the line config.load_defaults 6.1 the application does not start up any more. It complains undefined method 'ignore' for nil:NilClass. I have had a look at a new_framework_defaults_6_1.rb file, and it doesn't contain any mention of Rails.autoloaders.

I'd like to understand what is happening here. Can anybody help me?


Solution

  • The exception is saying that Rails.autoloaders.main is returning nil. You can use this to see where that method is defined:

    Rails.autoloaders.method(:main).source_location
    

    The code is here and it does this:

    def main
      if zeitwerk_enabled?
        @main ||= Zeitwerk::Loader.new.tap do |loader|
          loader.tag = "rails.main"
          loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
        end
      end
    end
    

    When you use config.load_defaults 6.1 that loads the Rails 6.1 defaults here. One thing that happens there is that the autoloader is set to :zeitwerk:

    self.autoloader = :zeitwerk if %w[ruby truffleruby].include?(RUBY_ENGINE)
    

    Rails.autoloaders.main only returns something if :zeitwerk is enabled and it's only enabled for you when you are using the 6.1 defaults. :zeitwerk was also the default in Rails 6.0 for ruby.

    I would recommend upgrading from 5.2 to 6.0 first and work through the Upgrading from Rails 5.2 to Rails 6.0 documentation before you move on to Rails 6.1. This section has more info on the autoloading changes. And this explains how to migrate from Classic to Zeitwerk.