How can I run SOME initializers when doing a Rails

2020-05-23 02:50发布

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! ?

5条回答
在下西门庆
2楼-- · 2020-05-23 03:10

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).

查看更多
做个烂人
3楼-- · 2020-05-23 03:15

Here's what I came up with. In the assets that need app configuration, I place this line at the very beginning:

<% require "#{Rails.root}/config/initializers/load_config.rb" unless defined?(AppConfig) %>

... and add .erb to the file name, so that video_player.js.coffee becomes video_player.js.coffee.erb. Then I can safely use AppConfig['somekey'] afterwards.

During the asset pre-compilation, it loads app config despite the initialize_on_precompile set to false, 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.

查看更多
狗以群分
4楼-- · 2020-05-23 03:21

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.

查看更多
虎瘦雄心在
5楼-- · 2020-05-23 03:22

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

查看更多
时光不老,我们不散
6楼-- · 2020-05-23 03:25

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.

  • With asset_sync and S3 you can precompile assets meaning all the assets are there ready to be served on the asset host / CDN immediately
  • You can only require the :assets bundle in application.rb on precompile, saving memory in production
  • Your app servers are NEVER hit for asset requests. You can spend expensive compute time on, you know. Computing.
  • Best practice HTTP cache headers are all set by default
  • You can enable automatic gzip compression with one extra config
查看更多
登录 后发表回答