Ruby on rails 3.1 部署到子URI时,Rails 3.1中的预编译资产已损坏

Ruby on rails 3.1 部署到子URI时,Rails 3.1中的预编译资产已损坏,ruby-on-rails-3.1,asset-pipeline,passenger,assets,sprockets,Ruby On Rails 3.1,Asset Pipeline,Passenger,Assets,Sprockets,我正在更新Rails 3应用程序以使用Rails 3.1,作为这一过程的一部分,我正在利用新的资产管道。到目前为止,除了一个我无法解决的恼人问题外,我的一切都正常工作 该应用程序及其所有资产在开发过程中运行良好,但在生产过程中,它使用Passenger(http://the-host/sub-uri/)。这方面的问题是,资产是在部署期间预编译的,我的一个CSS(嗯,它是.CSS.scss文件)文件正在使用sass railsgem中的图像url帮助程序。由于在预编译过程中,路径硬编码到预编译的C

我正在更新Rails 3应用程序以使用Rails 3.1,作为这一过程的一部分,我正在利用新的资产管道。到目前为止,除了一个我无法解决的恼人问题外,我的一切都正常工作

该应用程序及其所有资产在开发过程中运行良好,但在生产过程中,它使用Passenger(
http://the-host/sub-uri/
)。这方面的问题是,资产是在部署期间预编译的,我的一个CSS(嗯,它是
.CSS.scss
文件)文件正在使用
sass rails
gem中的
图像url
帮助程序。由于在预编译过程中,路径硬编码到预编译的CSS文件中,因此不考虑子uri:

在我的
.css.scss
文件中:

body { background-image: image-url("bg.png"); }
body { background-image: url(/assets/bg.png); }
编译的
应用程序-.css
文件中的结果:

body { background-image: image-url("bg.png"); }
body { background-image: url(/assets/bg.png); }
如何使其正常工作:

body { background-image: url(/sub-uri/assets/bg.png); }
这是不是要求太多了?如果是这样,我将不得不切换回旧的非资产管道方式,只从
public
提供我的图像和CSS。然而,这似乎是应该考虑和解决的事情。。。?我是否错过了解决方案


编辑1:我应该注意,使用的结果与预期的相同


编辑2:回应Benoit Garret的评论

不,问题与
config.assets.prefix
无关。我尝试将其设置为
/sub-uri/assets
,而不是默认设置为
/assets
),但结果是这样做是错误的-似乎此设置已经与Rails应用程序的根相关,而不是与服务器相关。删除它(并因此返回默认设置)修复了所有导致的奇怪问题(有很多,所有的资产都以
/sub-uri/sub-uri/assets
-这一切都很奇怪)。唯一的问题是,
图像url
助手和朋友在预编译时没有获取子URI。不用说,这是合乎逻辑的,因为当它被预编译时,它不可能知道当它在Passenger下运行时,它将以这种方式配置。我的问题是如何告知它这一点,从而在预编译结果中得到正确的路径。如果确实可以做到的话


我目前的解决方法是像这样引用CSS中的iamge:
url(../images/bg.png)
并将其放置在非流水线
public/images
位置。这并不理想,因为它不能从指纹识别和管道提供的一切中获益。

最后,我想出了一些解决方法

1) 从它看来,这可能会在sass rails中得到修复。我已经按照上面链接中建议的补丁程序对helpers.rb进行了修改。我只需在
deploy.rb
中的资产预编译行中设置所需的环境变量

我在一个文件
config/initializers/gem_patches.rb
中完成所有的猴子补丁。在此文件中,我将此方法修补为:

module Sass
  module Rails
    module Helpers
      protected
      def public_path(asset, kind)
        path = options[:custom][:resolver].public_path(asset, kind.pluralize)
        path = ENV['PRODUCTION_URI'] + path if ENV['PRODUCTION_URI']
        path
      end
    end
  end
end

2) 或者,如果您可以在CSS中嵌入图像,则将样式表更改为具有.erb扩展名,并将
图像url(“bg.png”)
替换为
url()
,这将不需要更改sass rails<代码>资产数据uri不作为纯Sass函数存在,因此您必须使用Rails助手
资产数据uri

经过一番挖掘,我发现了这个问题。问题出现在Rails中,特别是链轮::Helpers::RailsHelper::AssetPath#compute_public_path。链轮::Helpers::RailsHelper::AssetPath继承自ActionView::AssetPath并重写许多方法。当通过Sass::Rails::Resolver调用compute_public_path时,公共_path方法是Sass Rails,Rails链轮帮助器将承担解析资源的任务。链轮::Helpers::RailsHelper::AssetPath#compute#public(计算公共)路径服从于super,即ActionView::AssetPath#compute#public(计算公共)路径。在这个方法中有一个条件has_request?在重写\u相对\u url\u根时,如下所示:

def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
  ...
  source = rewrite_relative_url_root(source, relative_url_root) if has_request?
  ...
end

def relative_url_root
  config = controller.config if controller.respond_to?(:config)
  config ||= config.action_controller if config.action_controller.present?
  config ||= config
  config.relative_url_root
end
如果您查看rewrite_relative_url_root的内部结构,它依赖于要出现的请求以及从控制器变量派生该请求以解析相对url root的能力。问题是,当链轮为sass解析这些资产时,它没有控制器,因此没有请求

上面的解决方案在我的开发模式下不起作用。以下是我目前正在使用的解决方案:

module Sass
  module Rails
    module Helpers
      protected
      def public_path(asset, kind)
        resolver = options[:custom][:resolver]
        asset_paths = resolver.context.asset_paths
        path = resolver.public_path(asset, kind.pluralize)
        if !asset_paths.send(:has_request?) && ENV['RAILS_RELATIVE_URL_ROOT']
          path = ENV['RAILS_RELATIVE_URL_ROOT'] + path
        end
        path
      end
    end
  end
end

在最新的Rails 3.1.3中,您需要立即对另一个模块进行猴子补丁,以使其正常工作

这就是我所做的

module Sprockets
  module Helpers
    module RailsHelper

      def asset_path(source, options = {})
        source = source.logical_path if source.respond_to?(:logical_path)
        path = asset_paths.compute_public_path(source, asset_prefix, options.merge(:body => true))
        path = options[:body] ? "#{path}?body=1" : path
        if !asset_paths.send(:has_request?)
          path = ENV['RAILS_RELATIVE_URL_ROOT'] + path if ENV['RAILS_RELATIVE_URL_ROOT']
        end
        path
      end

    end 
  end
end
在我的deploy.rb中,我有:

desc "precompile the assets"
namespace :assets do
  task :precompile_assets do
    run "cd #{release_path} && rm -rf public/assets/* && RAILS_ENV=production bundle exec rake assets:precompile RAILS_RELATIVE_URL_ROOT='/my_sub_uri'"
  end
end
before "deploy:symlink", "assets:precompile_assets"

我正在使用Rails 3.1.3并成功地部署到子URI。 我什么也没补

对该设置的关键问题进行了更好的讨论。正如您所看到的,该解决方案应用于Rails 3.2,从未向后移植到3.1.4

但是,我使用Rails 3.1.3找到了一个适合我的设置的解决方案

试试这个:(我不是专家,只是想为解决一个困扰了我好几个小时的问题做出贡献…)

environment.rb:

#at top:
ENV['RAILS_RELATIVE_URL_ROOT'] = '/rais'
production.rb:

config.assets.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] ? ENV['RAILS_RELATIVE_URL_ROOT'] + '/assets' : '/assets'
routes.rb:

  Rais::Application.routes.draw do
       scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do    #see config/environment.rb
             <<resources here>>
       end
  end
然后,使用控制台进行测试:

RAILS_ENV=production rails console
结果:

foo = ActionView::Base.new
foo.stylesheet_link_tag 'application'
 => "<link href=\"/rais/assets/layout.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"/rais/assets/application.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />" 
foo.image_tag('arrow-up.png')
 => "<img alt=\"Arrow-up\" src=\"/rais/assets/arrow-up-ca314ad9b991768ad2b9dcbeeb8760de.png\" />" 
foo=ActionView::Base.new
foo.stylesheet\u link\u标记“application”
=>“\n”
foo.image_标记('arrow-up.png'))
=> "" 

您尝试过这个吗?应用程序本身部署得很好(它确实使用RailsBaseURI方法,因为这是乘客文档推荐的方法)。在运行中的应用程序中链接到的所有资产,如使用
image\u标记的图像等,都是正常的。唯一的问题是CSS中引用的图像-在预编译资产时,它不知道子URI。就是为了这个