为多宿主 Rails 应用程序预编译资产
Precompiling assets for a multi-homed Rails application
我有一个多宿主 Rails 3.2 应用程序,其中每个 "home" 都有自己的皮肤。这些资产是使用自定义 Rake 任务预编译的:
require "fileutils"
namespace :assets do
namespace :precompile do
task :homes, [:short_name] => ["assets:environment"] do |t, args|
include Sprockets::Helpers::RailsHelper
homes = args.short_name ? home.where(:short_name => args.short_name) : home.all
homes.each do |home|
appenv = Rails.application.assets
appenv.clear_paths
paths = [
"app/assets/homes/#{home.short_name}/images",
"app/assets/homes/#{home.short_name}/stylesheets",
"app/assets/homes/#{home.short_name}/javascripts"
]
paths.each{|x| appenv.append_path x}
config = Rails.application.config
config.assets.prefix = home.short_name
config.assets.digest = true
config.assets.debug = false
config.assets.compress = true
target = File.join(Rails.public_path, config.assets.prefix)
puts "Compiling assets for #{home.name} into #{target}"
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: true, manifest: true).compile
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: false, manifest: true).compile
end
end
end
end
这个 Rake 任务接受一个可选的 "home" 参数,并且只会编译 "home" 的资产(如果提供的话)——如果没有,它会循环遍历所有 "homes" 并为 "homes" 编译资产每一个:
rake assets precompile:homes[my_home]
或
rake assets precompile:homes
在我的 ApplicationController 中,我像这样动态设置资产路径:
MyApp::Application.config.assets.prefix = @home.short_name
MyApp::Application.config.assets.digests = YAML.load_file(File.join(Rails.public_path, @home.short_name, "manifest.yml"))
这非常有效 - 只要我 运行 在开发环境中执行 Rake 任务 - 但是在生产环境中 运行 我在尝试修改环境时遇到错误,在 appenv.clear_paths
:
$ rake assets precompile:homes RAILS_ENV=production
TypeError: can't modify immutable index
/var/rails/MyApp/vendor/ruby/1.9.1/gems/sprockets-2.2.3/lib/sprockets/index.rb:80:in `expire_index!'
/var/rails/MyApp/vendor/ruby/1.9.1/gems/sprockets-2.2.3/lib/sprockets/trail.rb:49:in `clear_paths'
/var/rails/MyApp/lib/tasks/assets.rake:10:in `block (4 levels) in <top (required)>
我理解这是因为 Sprockets 在生产中使用不同的对象来表示 appenv,在开发中这是完整的环境 (Sprockets::Environment),而在生产中它是一个减少和受限的子集 (Sprockets: :Index) 是只读的。但这导致了陷阱 22:为了能够在任务中动态设置资产路径,我需要在开发中 运行 它,但为了压缩和缩小资产,并获得摘要工作,我必须在生产中 运行 它。如您所见,我在开发过程中也试图强制它压缩和生成摘要,但这似乎没有效果:
config.assets.digest = true
config.assets.debug = false
config.assets.compress = true
几天来我一直在努力寻找解决方案,但一直无法想出任何可行的办法——得了重感冒而且感觉有点疲惫也无济于事:( 在我看来,这应该是一个相当普遍的情况,所以我希望这里有人知道该怎么做......
我很感激 Rails 3.2 有点过时了,Rails 5 having been announced 等等,但直到有人捐出一个月的空闲时间(联系我了解详情) 我必须保留这个东西 运行ning。幸运的是,一旦我的感冒痊愈,解决我遇到的问题并不太难——这是固定的 Rake 任务的样子:
require "fileutils"
namespace :assets do
namespace :precompile do
task :homes, [:short_name] => ["assets:environment"] do |t, args|
include Sprockets::Helpers::RailsHelper
homes = args.short_name ? home.where(:short_name => args.short_name) : home.all
homes.each do |home|
appenv = Rails.application.assets
appenv.clear_paths
paths = [
"app/assets/homes/#{home.short_name}/images",
"app/assets/homes/#{home.short_name}/stylesheets",
"app/assets/homes/#{home.short_name}/javascripts"
]
paths.each{|x| appenv.append_path x}
config = Rails.application.config
config.assets.prefix = home.short_name
config.assets.digest = true
target = File.join(Rails.public_path, config.assets.prefix)
puts "Compiling assets for #{home.name} into #{target}"
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: false, manifest: false).compile
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: true, manifest: true).compile
end
end
end
end
这在几个方面与我以前的不同:
debug
和 compress
选项在 运行 编译之前不再设置在 Rails.application.assets
上,因为这些显然没有效果。
- digest/non-digest 资产的编译顺序已切换,因此摘要资产在非摘要资产之后创建。
第二个更改解决了缺少摘要的问题,environments/development.rb
中的以下内容被缩小,CSS 文件不再包含 SASS 行号注释:
config.assets.debug = false
config.assets.compress = true
所以这一切都按照我想要的方式工作,只要它在开发环境中 运行,这很好 - 问题 已解决.
编辑: 所以,我注意到 SCSS 文件中的资产引用没有被赋予摘要文件名,一些进一步的测试表明 config.assets.digest = true
毕竟需要设置吗?上面的代码已更新以反映这一点。否则当 actionpack/lib/sprockets/helpers/rails_helper.rb 检查 if digest_assets && options[:digest] != false
时 digest_assets
为假
我有一个多宿主 Rails 3.2 应用程序,其中每个 "home" 都有自己的皮肤。这些资产是使用自定义 Rake 任务预编译的:
require "fileutils"
namespace :assets do
namespace :precompile do
task :homes, [:short_name] => ["assets:environment"] do |t, args|
include Sprockets::Helpers::RailsHelper
homes = args.short_name ? home.where(:short_name => args.short_name) : home.all
homes.each do |home|
appenv = Rails.application.assets
appenv.clear_paths
paths = [
"app/assets/homes/#{home.short_name}/images",
"app/assets/homes/#{home.short_name}/stylesheets",
"app/assets/homes/#{home.short_name}/javascripts"
]
paths.each{|x| appenv.append_path x}
config = Rails.application.config
config.assets.prefix = home.short_name
config.assets.digest = true
config.assets.debug = false
config.assets.compress = true
target = File.join(Rails.public_path, config.assets.prefix)
puts "Compiling assets for #{home.name} into #{target}"
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: true, manifest: true).compile
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: false, manifest: true).compile
end
end
end
end
这个 Rake 任务接受一个可选的 "home" 参数,并且只会编译 "home" 的资产(如果提供的话)——如果没有,它会循环遍历所有 "homes" 并为 "homes" 编译资产每一个:
rake assets precompile:homes[my_home]
或
rake assets precompile:homes
在我的 ApplicationController 中,我像这样动态设置资产路径:
MyApp::Application.config.assets.prefix = @home.short_name
MyApp::Application.config.assets.digests = YAML.load_file(File.join(Rails.public_path, @home.short_name, "manifest.yml"))
这非常有效 - 只要我 运行 在开发环境中执行 Rake 任务 - 但是在生产环境中 运行 我在尝试修改环境时遇到错误,在 appenv.clear_paths
:
$ rake assets precompile:homes RAILS_ENV=production
TypeError: can't modify immutable index
/var/rails/MyApp/vendor/ruby/1.9.1/gems/sprockets-2.2.3/lib/sprockets/index.rb:80:in `expire_index!'
/var/rails/MyApp/vendor/ruby/1.9.1/gems/sprockets-2.2.3/lib/sprockets/trail.rb:49:in `clear_paths'
/var/rails/MyApp/lib/tasks/assets.rake:10:in `block (4 levels) in <top (required)>
我理解这是因为 Sprockets 在生产中使用不同的对象来表示 appenv,在开发中这是完整的环境 (Sprockets::Environment),而在生产中它是一个减少和受限的子集 (Sprockets: :Index) 是只读的。但这导致了陷阱 22:为了能够在任务中动态设置资产路径,我需要在开发中 运行 它,但为了压缩和缩小资产,并获得摘要工作,我必须在生产中 运行 它。如您所见,我在开发过程中也试图强制它压缩和生成摘要,但这似乎没有效果:
config.assets.digest = true
config.assets.debug = false
config.assets.compress = true
几天来我一直在努力寻找解决方案,但一直无法想出任何可行的办法——得了重感冒而且感觉有点疲惫也无济于事:( 在我看来,这应该是一个相当普遍的情况,所以我希望这里有人知道该怎么做......
我很感激 Rails 3.2 有点过时了,Rails 5 having been announced 等等,但直到有人捐出一个月的空闲时间(联系我了解详情) 我必须保留这个东西 运行ning。幸运的是,一旦我的感冒痊愈,解决我遇到的问题并不太难——这是固定的 Rake 任务的样子:
require "fileutils"
namespace :assets do
namespace :precompile do
task :homes, [:short_name] => ["assets:environment"] do |t, args|
include Sprockets::Helpers::RailsHelper
homes = args.short_name ? home.where(:short_name => args.short_name) : home.all
homes.each do |home|
appenv = Rails.application.assets
appenv.clear_paths
paths = [
"app/assets/homes/#{home.short_name}/images",
"app/assets/homes/#{home.short_name}/stylesheets",
"app/assets/homes/#{home.short_name}/javascripts"
]
paths.each{|x| appenv.append_path x}
config = Rails.application.config
config.assets.prefix = home.short_name
config.assets.digest = true
target = File.join(Rails.public_path, config.assets.prefix)
puts "Compiling assets for #{home.name} into #{target}"
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: false, manifest: false).compile
Sprockets::StaticCompiler.new(appenv, target, ["*"], digest: true, manifest: true).compile
end
end
end
end
这在几个方面与我以前的不同:
debug
和compress
选项在 运行 编译之前不再设置在Rails.application.assets
上,因为这些显然没有效果。- digest/non-digest 资产的编译顺序已切换,因此摘要资产在非摘要资产之后创建。
第二个更改解决了缺少摘要的问题,environments/development.rb
中的以下内容被缩小,CSS 文件不再包含 SASS 行号注释:
config.assets.debug = false
config.assets.compress = true
所以这一切都按照我想要的方式工作,只要它在开发环境中 运行,这很好 - 问题 已解决.
编辑: 所以,我注意到 SCSS 文件中的资产引用没有被赋予摘要文件名,一些进一步的测试表明 config.assets.digest = true
毕竟需要设置吗?上面的代码已更新以反映这一点。否则当 actionpack/lib/sprockets/helpers/rails_helper.rb 检查 if digest_assets && options[:digest] != false
digest_assets
为假