Heroku – How to run SOME initializers when doing a Rails assets:precompile

asset-pipelineherokuruby-on-rails-3ruby-on-rails-3.1ruby-on-rails-3.2

Background

I have an app that I recently updated to Rails 3.2.1 (from Rails 3.0.x) and have refactored the JS and CSS assets to make use of the new asset pipeline. The app is hosted on Heroku with the Celadon Cedar stack.

App Config

I keep application specific configuration in a YAML file called app_config.yml and load it into a global APP_CONFIG variable using an initializer:

# config/initializers/load_app_config.rb

app_config_contents = YAML.load_file("#{Rails.root.to_s}/config/app_config.yml")
app_config_contents["default"] ||= {}
APP_CONFIG = app_config_contents["default"].merge(
                       app_config_contents[Rails.env] || {} ).symbolize_keys

Asset Compilation on Heroku

Heroku has support for the Rails asset pipeline built into the Cedar stack. When you push an app to Heroku it automatically calls rake assets:precompile on the server as a step in the deploy process. However it does this in a sandboxed environment without database access or normal ENV vars.

If the application is allowed to initialize normally during asset precompilation an error is thrown trying to connect to the database. This is easily solved by adding the following to the application.rb file:

    # Do not load entire app when precompiling assets
    config.assets.initialize_on_precompile = false

My Problem

When initialize_on_precompile = false is set, none of the initializers in config/initializers/* are run. The problem I am running into is that I need the APP_CONFIG variable to be available during asset precompilation.

How can I get load_app_config.rb to be loaded during asset compilation without initializing the entire app? Can I do something with the group parameter passed to Rails::Application.initialize! ?

Best Answer

Rails lets you register initializers only in certain groups, but you need to use the Railtie API:

# in config/application.rb

module AssetsInitializers
  class Railtie < Rails::Railtie
    initializer "assets_initializers.initialize_rails",
                :group => :assets do |app|
      require "#{Rails.root}/config/initializers/load_config.rb"
    end
  end
end

You don't need to check if AppConfig is defined since this will only run in the assets group.

And you can (and should) continue to use initialize_on_precompile = false. The load_config.rb initializer will be run when initializing the app (since it's in config/initializers) and when pre-compiling without initializing (because of the above code).

Related Topic