Railtie initializer executed on startup, but block is never invoked

I’m trying to reorganize some view helper methods and corresponding ERb templates in preparation for packaging as a gem. So I moved the helper logic into lib/bulma_form, and created the following railtie.rb file:

require 'bulma_form/helpers'

module BulmaForm
  class Railtie < Rails::Railtie
    initializer 'bulma_form.helpers' do |_app|
      ActionView::Base.send :include, Helpers
      # ActiveSupport.on_load(:action_view) do |_app|
      #   include Helpers
      # end
    end
  end
end

I can see that on Rails startup or test execution, the initializer method is executed properly, but the specified block is never invoked. As anyone can see, I’ve been experimenting with different logic within the block, but I imagine that’s irrelevant since it’s never executed anyway. What am I doing wrong?

I just found a way that works, though I don’t know if it’s considered the right or best way:

require 'bulma_form/helpers'

module BulmaForm
  class Railtie < Rails::Railtie
    config.to_prepare do
        ActionView::Helpers.send :include, Helpers
    end
  end
end

I think you might have missed the group in your first attempt:

initializer 'bulma_form.helpers', group: :all do |_app|

As for the second, another option is:

config.after_initialize do |app|
end
1 Like

Thanks Breno! I can’t find anything about the group option to initializer in the API docs, but the config.after_initialize method seems to work just fine.

Yeah, I only know about group because I checked the source code of other Rails gems to see how they handled this.

1 Like

Without testing it myself, I’d say your first snippet should work as you expect. And there, the on_load variant is more correct that the uncommented one.

When does the application load the railtie?

Yeah, I kinda wondered whether an official initializer via on_load would be the more kosher option, if I could get it to work. I load the railtie from this file in lib:

module BulmaForm
  require 'bulma_form_rails/railtie' if defined?(Rails)
end

I originally loaded this via an explicit require, but now that I’ve moved everything into a gem, the Gemfile takes care of that. And interestingly, the on_load syntax now works just fine.

So something is different about initializing from a gem that makes the kosher way work?

The key point is when is the railtie defined during the boot process. Whether that is in a gem or not does not matter. Let me explain.

One of the first files executed when Rails boots is config/environment.rb:

# config/environment.rb
require_relative "application"
Rails.application.initialize!

As you see, that loads config/application.rb and executes Rails.application.initialize!, which is responsible for booting the application. As part of that process, the initializers of railties and engines are executed, but as you see, they have to be known at that point! If you load them later, the time is gone.

Since config/application.rb has this line

Bundler.require(*Rails.groups)

the entrypoints of the gems declared in the Gemfile are required (mod require: false). Typically, those entrypoints load the corresponding railtie in most gems like yours, and when initialize! runs, all is set to be run as expected.

If your code is in lib, as you can see the initializers will run as long as the file is loaded in config/application.rb. For example, if you throw:

class MyRailtie < Rails::Railtie
  initializer "my_railtie" do
    p 1
  end
end

anywhere in config/application.rb, you’ll see the initializer running.

1 Like

Ah, I think I understand. No initializer will run if it’s created after Rails.application.initialize! is executed. I must’ve done something like that in my earlier attempts.

I just looked at my repo history, and I see that I had the following line in config/initializers/application_init.rb:

require 'bulma_form_rails'

So that’s where I was defining the railtie. Doesn’t that mean I was trying to define an initializer from another that was already running? Is that too late in the process?

Exactly.

Running the application initializers that exist in config/initializers is one of the things the initialize! method does. Indeed, that is too late to define a railtie.

1 Like

Makes perfect sense. Thanks for jumping into the conversation!