8

我正在更新 Rails 3 应用程序以使用 Rails 3.1,作为其中的一部分,我正在使用新的资产管道。到目前为止,除了一个我无法解决的相当烦人的问题之外,我已经完成了所有工作。

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

在我的.css.scss文件中:

body { background-image: image-url("bg.png"); }

编译application-<md5-hash-here>.css文件中的结果:

body { background-image: url(/assets/bg.png); }

使它正常工作应该是什么:

body { background-image: url(/sub-uri/assets/bg.png); }

这种情况是不是要求太多了?如果是这样,我将不得不切换回旧的非资产流水线方式,只提供来自public. 然而,这似乎是应该考虑和解决的事情......?我错过了解决方案吗?


编辑 1:我应该注意,使用erb 解决方案会产生相同的结果,正如人们所期望的那样。


编辑 2:回应 Benoit Garret 的评论

不,问题与config.assets.prefix. 我尝试将其设置为(/sub-uri/assets而不是默认值/assets),但事实证明这是错误的做法 - 似乎此设置已经与 Rails 应用程序的根目录相关,而不是服务器。删除它(并因此返回默认值)修复了所有导致的奇怪问题(并且有很多,所有资产最终都进入了/sub-uri/sub-uri/assets- 这一切都非常奇怪)。唯一的问题是image-urlhelper 和朋友在预编译时不会拾取 sub-URI。不用说,这是合乎逻辑的,因为当它被预编译时,它不可能知道当它在Passenger下运行时,它会这样配置。我的问题是如何通知它,从而在预编译结果中得到正确的路径。如果确实可以做到。

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

4

4 回答 4

4

最后,我制定了一些解决方法/解决方案。

1)从https://github.com/rails/sass-rails/issues/17看起来这可以在 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 扩展名,然后将其替换为image-url("bg.png")无需url(<%= asset_data_uri "bg.png" %>)更改 sass-rails 即可。 asset-data-uri不作为纯 Sass 函数存在,因此您必须使用 Rails 助手asset_data_uri

于 2011-09-10T13:59:39.933 回答
2

在最新的 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"
于 2011-12-06T10:06:27.877 回答
2

我正在使用 Rails 3.1.3 并成功部署到子 URI。我没有猴子修补任何东西。

此设置的关键问题已在此处进行了更好的讨论。如您所见,该解决方案已应用于 Rails 3.2,并且从未向后移植到 3.1.4。

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

试试这个:( 我不是专家,只是想帮助解决一个困扰我几个小时的问题......)

环境.rb:

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

生产.rb:

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

路线.rb:

  Rais::Application.routes.draw do
       scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do    #see config/environment.rb
             <<resources here>>
       end
  end

如您所见,我将 assets.prefix 放在 production.rb 中,而不是在 application.rb 中,然后您可以:

rake assets:clear
rake assets:precompile

然后,使用控制台进行测试:

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\" />" 
于 2012-08-25T14:35:23.713 回答
0

经过一番挖掘,我发现了问题所在。问题出在 Rails 中,特别是 Sprockets::Helpers::RailsHelper::AssetPaths#compute_public_path。Sprockets::Helpers::RailsHelper::AssetPaths 继承自 ActionView::AssetPaths 并覆盖了许多方法。当通过 Sass::Rails::Resolver#public_path 方法调用 sass-rails 的 compute_public_path 时,rails sprocket helper 将承担解析资产的任务。Sprockets::Helpers::RailsHelper::AssetPaths#compute_public_path 遵循超级,即 ActionView::AssetPaths#compute_public_path。在这个方法中有一个has_request的条件吗?在 rewrite_relative_url_root 上,如下所示:

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 根的能力。问题是当 sprockets 为 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
于 2011-09-10T20:13:47.147 回答