为多宿主 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

这在几个方面与我以前的不同:

  • debugcompress 选项在 运行 编译之前不再设置在 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 为假