ruby-on-railsautoloadruby-on-rails-7rails-engineszeitwerk

Rails 7 way of auto-loading methods into controllers via engine


I'm looking into updating one of my favorite CMSs to Rails 7 that have been archived on github (PushType). Only I haven't coded Rails since Rails 6. Apparently, something about autoloading methods changed in Rails 7. I am getting this error:

NameError: uninitialized constant PushType::ApplicationControllerMethods
          include PushType::ApplicationControllerMethods
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

for this line in the engine:

      initializer 'push_type.application_controller' do
        ActiveSupport.on_load :action_controller do
          # ActionController::Base.send :include, PushType::ApplicationControllerMethods
          include PushType::ApplicationControllerMethods
        end
      end

I don't know what I'm doing given my hiatus from the language. but in my attempt to resolve this I have tried autoloading that concerns directory within the gem's engine like this:

    config.autoload_paths << File.expand_path("../../../app/controllers/concerns", __FILE__) <<
                             # File.expand_path("../../../app/controllers/concerns/push_type", __FILE__)
                             File.expand_path("../../../app/helpers", __FILE__)

the full engine.rb file looks like this:

module PushType
  module Core
    class Engine < ::Rails::Engine
      isolate_namespace PushType
      engine_name 'push_type'

      config.autoload_paths << File.expand_path("../../../app/controllers/concerns", __FILE__) <<
                             # File.expand_path("../../../app/controllers/concerns/push_type", __FILE__)
                             File.expand_path("../../../app/helpers", __FILE__)

      # config.autoload_paths << "#{root}/app/controllers/concerns" <<
      #                         "#{root}/app/controllers/concerns/push_type" <<
      #                         "#{root}/app/helpers"

      # lib = root.join("lib")
      # config.autoload_once_paths.ignore(
      #   lib.join("assets"),
      #   lib.join("tasks"),
      #   lib.join("generators")
      # )

      config.generators do |g|
        g.assets false
        g.helper false
        g.test_framework  :test_unit, fixture: false
        g.hidden_namespaces << 'push_type:dummy' << 'push_type:field'
      end

      config.assets.precompile += %w(
        *.gif *.jpg *.png *.svg *.eot *.ttf *.woff *.woff2
      )

      config.to_prepare do
        Rails.application.eager_load! unless Rails.application.config.cache_classes
      end

      initializer 'push_type.dragonfly_config' do
        PushType.config.dragonfly_secret ||= Rails.application.secrets.secret_key_base
        PushType.dragonfly_app_setup!
      end

      initializer 'push_type.application_controller' do
        ActiveSupport.on_load :action_controller do
          # ActionController::Base.send :include, PushType::ApplicationControllerMethods
          include PushType::ApplicationControllerMethods
        end
      end

      initializer 'push_type.menu_helpers' do
        ActiveSupport.on_load :action_view do
          include PushType::MenuBuilder::Helpers
        end
      end
    end
  end
end

Solution

  • It is too early, autoloading of reloadable code is not yet ready in Rails 7.

    However, as it happens, that is catching a gotcha. This is why Rails 7 does not allow access to reloadable classes that early. Since the module is included in a non-reloadable class (AC::Base), it makes no sense for it to be reloadable, because reloads would have no effect for that included module.

    Please, delete the custom autoload_paths configuration, and add the concerns and helpers directories of the engine to the autoload_once_paths. The non-reloadable classes and modules in that collection are available earlier.