我正在更新Rails 3应用程序以使用Rails 3.1,并且作为其中的一部分,我正在利用新的资产管道。到目前为止,我已经把一切都解决了一个我无法解决的烦人问题。
应用程序及其所有资产在开发中运行良好,但在生产中,它使用Passenger(http://the-host/sub-uri/
)部署到子URI。这个问题是资产是在部署期间预编译的,我的一个CSS(好吧,它是一个.css.scss
文件)文件正在使用来自image-url
的{{1}}帮助器宝石。由于在预编译过程中,路径被硬编码到预编译的CSS文件中,因此不考虑子uri:
在我的sass-rails
文件中:
.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); }
提供我的图像和CSS。然而它似乎应该被考虑和解决的东西......?我错过了解决方案吗?
编辑1:我应该注意,使用erb solution会产生与预期相同的结果。
编辑2:回应Benoit Garret的评论
不,问题与public
无关。我尝试设置(到config.assets.prefix
而不是默认的/sub-uri/assets
)但事实证明这是错误的做法 - 看起来这个设置已经与Rails应用程序的根有关了,而不是服务器。删除它(从而返回到默认值)已经解决了导致的所有奇怪问题(并且有很多,所有资产都在/assets
中结束 - 这一切都非常奇怪)。唯一的问题是/sub-uri/sub-uri/assets
助手和朋友在预编译时不会获取子URI。毋庸置疑,这是合乎逻辑的,因为当它被预编译时,它不可能知道当它在Passenger下运行时,它将以这种方式配置。我的问题是如何告知它,从而最终得到预编译结果中的正确路径。如果确实可以做到。
我目前的解决方法是在CSS中引用iamge,如下所示:image-url
并将其放在非流水线url(../images/bg.png)
位置。非常理想,因为它不会从指纹识别和管道提供的所有内容中受益。
答案 0 :(得分:4)
最后,我制定了几个解决方法/解决方案。
1)从https://github.com/rails/sass-rails/issues/17来看,这可能会在sass-rails中修复。我已经修好了帮手。我自己沿着上面链接中提出的补丁行。我只需在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-轨道。 asset-data-uri
不作为纯Sass函数存在,因此您必须使用Rails助手asset_data_uri
。
答案 1 :(得分: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"
答案 2 :(得分:2)
我正在使用Rails 3.1.3并成功部署到子URI。 我没有修补任何东西。
此设置的关键问题已得到更好的讨论here。如您所见,该解决方案已应用于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
正如您所看到的,我将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\" />"
答案 3 :(得分:0)
经过一番挖掘后,我发现了这个问题。问题出在Rails中,特别是Sprockets :: Helpers :: RailsHelper :: AssetPaths#compute_public_path。 Sprockets :: Helpers :: RailsHelper :: AssetPaths继承自ActionView :: AssetPaths并覆盖了许多方法。当通过Sass :: Rails :: Resolver调用compute_public_path #sublic_path方法是sass-rails时,rails sprocket helper接受解析资产的任务。 Sprockets :: Helpers :: RailsHelper :: AssetPaths#compute_public_path遵循super,即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