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! ?
Rails lets you register initializers only in certain groups, but you need to use the Railtie API:
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 inconfig/initializers
) and when pre-compiling without initializing (because of the above code).Here's what I came up with. In the assets that need app configuration, I place this line at the very beginning:
... and add
.erb
to the file name, so thatvideo_player.js.coffee
becomesvideo_player.js.coffee.erb
. Then I can safely useAppConfig['somekey']
afterwards.During the asset pre-compilation, it loads app config despite the
initialize_on_precompile
set tofalse
, and does it only once (which avoids constant redefinition issues).Yes, it's a kludge, but many times nicer than embedding configs in asset files.
Although your intializer is not run when the assets are precompiling, you should still find that they run as Rails boors up as normal, however, this will be on the first hit to the application rather than at the deploy step.
I'm not entirely sure what the issue you're having is, but if you follow the Rails conventions the deploy will work as expected.
For Heroku I am running the Asset Sync gem to store my files on a CDN to avoid hitting Heroku for static images. It works nicely. I also have initialize on precompile false, but the Asset Sync runs it's own initializer so you could put your code in that. https://github.com/rumblelabs/asset_sync
Definitely check out asset_sync on github. Or our Heroku dev centre article on Using a CDN asset Host with Rails 3.1 on Heroku.
The issues with environment variables have recently been solved by a Heroku labs plugin, that makes your application's
heroku config
variables accessible during compilation time. To enable this, read about the user_env_compile plugin.Also. There is quite a big performance improvement in using asset_sync vs letting your application lazily compile assets in production or serving them precompiled directly off your app servers. However I would say that. I wrote it.