开发者

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 seco开发者_如何学Pythonnd 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?


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.


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


Rails bug. Fixed.

https://github.com/rails/rails/issues/2947

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜