ruby-on-railsruby-on-rails-3ruby-on-rails-pluginsrailtie

Railtie Initializer not running in plugin


I recently switched from the gem version of resources_controller to a plugin as the gem version relied on git.

Inside the vendor/plugins/plugin/lib/plugin.rb file, the Railtie is as follows:

module Ardes
  module ResourcesController
    class Railtie < Rails::Railtie
      initializer 'ardes.resources_controller' do
        ActiveSupport.on_load(:action_controller) do
          extend Ardes::ResourcesController
          include Ardes::ResourcesController::RequestPathIntrospection
        end

        ActiveSupport.on_load(:active_record) do
          include Ardes::ActiveRecord::Saved
        end
      end
    end
  end
end

I've added a require 'resources_controller' in one of my initializers and it's properly loading this file. The problem is that although the Railtie is evaluated (a puts in the class block will hit), it never seems to actually call the initializer block itself. This is important of course as this is where it extends ActionController to include the resources_controller_for method.

This question is appears to have come up here and here. Though in both cases they found other ways around the problem and no direct answer was given for why the block wasn't being called.

From what I can tell in the Rails docs you can name your initializer block anything you'd like and it should run. I don't think it matters, but I first noticed the problem when running in production rails s -e production though I believe the same problem exists in development mode.

What may be going on?

For reference, full plugin is here: https://github.com/ianwhite/resources_controller


Solution

  • The problem you're having here is that you can't add new initializers once the initializer process has started.

    Here, you're requiring the code that registers the initializers during the initializer process. When you use gems in the Gemfile, the initializers are registered in this code:

    if defined?(Bundler)
      # If you precompile assets before deploying to production, use this line
      Bundler.require(*Rails.groups(:assets => %w(development test)))
      # If you want your assets lazily compiled in production, use this line
      # Bundler.require(:default, :assets, Rails.env)
    end
    

    This code executes before the initializers begin. In contrast, you are requiring the resources_controller code in an initializer file, which runs during the initialization process. As a result, it's too late to register new initializers.

    What complicates the situation is that the load paths inside vendor/plugins are also set up during the initialization process, so you won't be able to require resources_controller in application.rb.

    The easiest solution to your problem would be to use the :path feature in bundler. After installing the plugin, add this line to your Gemfile:

    gem 'resources_controller', :path => "vendor/plugins/resources_controller"
    

    You can then remove the require line from your initializer, and bundler will recognize that the plugin is a locally checked out gem and do what it would do if you used git.