Rails 3.1无法在生产环境中编译资产,原因是资产主机配置。

13

我的生产环境下的asset_host配置如下:

  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 
  } 

...这段代码基本上是从文档中获取的:

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

当我执行assets:precompile时,会出现以下情况:

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

......但是我实际上不能删除第二个参数,因为我需要知道请求是否是ssl。 也就是说,我了解到在生成资产的rake任务期间不存在请求......

那么我该如何摆脱这个进退两难的局面呢?

3个回答

18

当你的资源使用路径时,比如:

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

如果您的asset_host设置为需要第二个参数(request)的lambda/proc函数,那么就会出现这种错误。

您可以选择去掉request参数(如果实际上不使用它),或使其变为可选(并处理其值为nil的情况)。在Ruby 1.9中,这很容易实现(并且应该更加容易,参见注释):

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

如果你想与Ruby 1.8兼容,没有直接的方法来创建带有默认值参数的Proc/lambda函数,但你可以使用:

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

或者使用一个方法来实现:

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

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

注意:

  1. 你的代码块可以返回nil来表示“默认主机”,不需要使用"#{request.protocol}#{request.host_with_port}"
  2. 理论上你不需要指定协议;以//开头的URL应该使用默认的协议(http或https)。我说“应该”是因为看起来 IE <= 8会下载两次css资源,而且我在PDFkit中遇到了问题。

所以在你的特定情况下,你的asset_host可以简化为:

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

编辑: 使用lambda或者*_来避免Ruby的bug功能。


那个 if request && request.ssl? 应该改成 unless request && request.ssl?. - Eric Koslow
@EricKoslow:哦,对了,我把条件的一部分反了。我已经修复了,应该没问题了 :-) - Marc-André Lafortune
1
这个回答太棒了,请标记为正确答案! - Mikey Hogarth

3
对于 Ruby 1.8.x 版本,@Marc-Andre 所提供的 method(:compute_asset_host) 技巧对我并不起作用。即使该方法被直接定义在上面,也会出现 NameError: undefined method 'compute_asset_host' for class 'Object' 错误。
以下是对我有效的解决方案:
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

我已经编辑了我的示例,加入了明确的作用域,以使其更清晰。你提出的Proc很好,所以我也加入了它。谢谢。 - Marc-André Lafortune

-3

1
你知道的,这个修复已经被撤销了! - Marc-André Lafortune

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接