Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rails 3.1 can't compile assets on prod due to asset host config

My production asset_host config looks like this:

  config.action_controller.asset_host = Proc.new { |source, request| 
    if request.ssl? 
      "#{request.protocol}#{request.host_with_port}" 
    else 
      "#{request.protocol}assets#{(source.length % 4) + 1}.example.com" 
    end 
  } 

...which is more or less straight from the docs:

http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html

When I go to assets:precompile, I get this:

$ RAILS_ENV=production bundle exec rake assets:precompile 
rake aborted! 
This asset host cannot be computed without a request in scope. Remove 
the second argument to your asset_host Proc if you do not need the 
request. 

....except that I can't really remove the 2nd arg because I need to know if the request is ssl or not. That said, I understand that a request isn't present during the rake task to generate the assets....

So how do I get out of this catch 22?

like image 390
jsharpe Avatar asked Sep 06 '11 18:09

jsharpe


People also ask

How do you Precompile rails assets?

To compile your assets locally, run the assets:precompile task locally on your app. Make sure to use the production environment so that the production version of your assets are generated. A public/assets directory will be created. Inside this directory you'll find a manifest.

What does rake assets Clean do?

rake assets:clean Only removes old assets (keeps the most recent 3 copies) from public/assets . Useful when doing rolling deploys that may still be serving old assets while the new ones are being compiled.

What is assets Precompile?

rails assets:precompile is the task that does the compilation (concatenation, minification, and preprocessing). When the task is run, Rails first looks at the files in the config.assets.precompile array. By default, this array includes application.js and application.css .


2 Answers

This will happen when (1) your assets use paths, for example:

background:url(image_path('awesome-background.gif'))

and (2) your asset_host is set to a lambda/proc that requires the second argument (request).

Your options are to either remove the request argument (if you don't actually use it) or make it optional (and handle the case where it is nil). This is easy in Ruby 1.9 (and should be easier, see notes):

config.action_controller.asset_host = ->(source, request = nil, *_){
  # ... just be careful that request can be nil
}

If you want to be compatible with Ruby 1.8, there is no direct way to create a Proc/lambda with parameters with defaults, but you can use:

config.action_controller.asset_host = Proc.new do |*args|
  source, request = args
  # ...
end

Or do it using a method:

def MyApp.compute_asset_host(source, request = nil)
  # ...
end

config.action_controller.asset_host = MyApp.method(:compute_asset_host)

Notes:

  1. Your block can return nil to signify the "default host", no need to use "#{request.protocol}#{request.host_with_port}"
  2. In theory you don't need to specify the protocol; a url starting with // should use the default protocol (http or https). I'm saying "should" as it looks like IE <= 8 will download the css assets twice and I've ran into problems with PDFkit.

So in your particular case, your asset_host can be simplified to:

config.action_controller.asset_host = Proc.new { |source, request = nil, *_| 
  "//assets#{(source.length % 4) + 1}.example.com" if request && !request.ssl? 
}

Edit: Use a lambda or else the *_ to avoid a bug feature of Ruby.

like image 153
Marc-André Lafortune Avatar answered Oct 21 '22 12:10

Marc-André Lafortune


For ruby 1.8.x, @Marc-Andre's method(:compute_asset_host) technique didn't work for me. Even though the method was defined directly above, NameError: undefined method `compute_asset_host' for class `Object' was raised.

Here's what worked for me:

config.action_controller.asset_host = Proc.new do |*args|
  source, request = args
  if request.try(:ssl?)
    'ssl.cdn.mysite.com'
  else
    'cdn%d.mysite.com' % (source.hash % 4)
  end
end
like image 42
Zubin Avatar answered Oct 21 '22 11:10

Zubin