rake assets:precompile:nodigest in Rails 4

倾然丶 夕夏残阳落幕 提交于 2019-12-09 02:41:55

问题


In Rails 3 there was a rake assets:precompile:nodigest task which was compiling assets and writing them without the digest part in /public/assets directory. In Rails 4 this has been removed, and by default all assets are precompiled with digest only. For various reasons I need also non-digested version of some of the assets. Is there any easy way to enable back the old behavior?


回答1:


The version of sprockets-rails used in Rails 4.0.0 no longer supports non-digest assets.

Per sprocket-rails's Readme:

Only compiles digest filenames. Static non-digest assets should simply live in public

So any assets that need to be static can be manually put into your public folder. If you need to copy compiled assets such as SCSS files, etc., this rake task may help (source):

task non_digested: :environment do
  assets = Dir.glob(File.join(Rails.root, 'public/assets/**/*'))
  regex = /(-{1}[a-z0-9]{32}*\.{1}){1}/
  assets.each do |file|
    next if File.directory?(file) || file !~ regex

    source = file.split('/')
    source.push(source.pop.gsub(regex, '.'))

    non_digested = File.join(source)
    FileUtils.cp(file, non_digested)
  end
end



回答2:


There is also a gem to do this for you: the somewhat provocatively named non-stupid-digest-assets.

gem "non-stupid-digest-assets"



回答3:


As the best answer suggested, I recommend go reading the issue first to fully understand the background story, if you're not in a hurry. I like this idea most, https://github.com/rails/sprockets-rails/issues/49#issuecomment-24837265.

And below is my personal take, basically I took the code from the answer above. In my case I only have 2 files that I want to be non digested, widget.js and widget.css. So I use sprockets method to find the digested files and then symlink it to public folder.

# config/environments/production.rb
config.assets.precompile += %w[v1/widget.js v1/widget.css]

# lib/tasks/assets.rake
namespace :assets do
  desc 'symlink digested widget-xxx.js and widget-xxx.css to widget.js and widget.css'
  task symlink_widget: :environment do
    next if Rails.env.development? || Rails.env.test?

    digested_files = []
    # e.g. /webapps/myapp/public/assets
    assets_path = File.join(Rails.root, 'public', Rails.configuration.assets.prefix)

    %w(v1/widget.js v1/widget.css).each do |asset|
      # e.g. 'v1/widget-b61b9eaaa5ef0d92bd537213138eb0c9.js'
      logical_path = Rails.application.assets.find_asset(asset).digest_path
      digested_files << File.join(assets_path, logical_path)
    end

    fingerprint_regex = /(-{1}[a-z0-9]{32}*\.{1}){1}/
    digested_files.each do |file|
      next unless file =~ fingerprint_regex

      # remove fingerprint & '/assets' from file path
      non_digested = file.gsub(fingerprint_regex, '.')
                         .gsub(Rails.configuration.assets.prefix, '')

      puts "Symlinking #{file} to #{non_digested}"
      system "ln -nfs #{file} #{non_digested}"
    end
  end
end



回答4:


My enormous analysis of all the available options is here:

https://bibwild.wordpress.com/2014/10/02/non-digested-asset-names-in-rails-4-your-options/

I decided to add a custom rake task, using custom configuration to determine certain assets to produce as non-digested versions, and using the Sprockets manifest to find the digested assets and copy them with non-digested names.

https://github.com/team-umlaut/umlaut/blob/5edcc609389edf833a79caa6f3ef92982312f0c5/lib/tasks/umlaut_asset_compile.rake

# Rails4 doesn't create un-fingerprinted assets anymore, but we
# need a couple for umlaut's API. Let's try to hook in and make
# symlinks. 
#
# list of what file(globs) need non-digest-named copies is kept in
#     Umlaut::Engine.config.non_digest_named_assets
# defined in lib/umlaut.rb, but app can modify it if desired. 

require 'umlaut'
require 'pathname'


# Every time assets:precompile is called, trigger umlaut:create_non_digest_assets afterwards. 
Rake::Task["assets:precompile"].enhance do
  Rake::Task["umlaut:create_non_digest_assets"].invoke
end

namespace :umlaut do 

  # This seems to be basically how ordinary asset precompile
  # is logging, ugh. 
  logger = Logger.new($stderr)  

  # Based on suggestion at https://github.com/rails/sprockets-rails/issues/49#issuecomment-20535134
  # but limited to files in umlaut's namespaced asset directories. 
  task :create_non_digest_assets => :"assets:environment"  do    
    manifest_path = Dir.glob(File.join(Rails.root, 'public/assets/manifest-*.json')).first
    manifest_data = JSON.load(File.new(manifest_path))

    manifest_data["assets"].each do |logical_path, digested_path|
      logical_pathname = Pathname.new logical_path

      if Umlaut::Engine.config.non_digest_named_assets.any? {|testpath| logical_pathname.fnmatch?(testpath, File::FNM_PATHNAME) }
        full_digested_path    = File.join(Rails.root, 'public/assets', digested_path)
        full_nondigested_path = File.join(Rails.root, 'public/assets', logical_path)

        logger.info "(Umlaut) Copying to #{full_nondigested_path}"

        # Use FileUtils.copy_file with true third argument to copy
        # file attributes (eg mtime) too, as opposed to FileUtils.cp
        # Making symlnks with FileUtils.ln_s would be another option, not
        # sure if it would have unexpected issues. 
        FileUtils.copy_file full_digested_path, full_nondigested_path, true      
      end
    end

  end
end



回答5:


Thank you Dylan Markow, I follow his answer, but I found that there are several versions of an asset (e.g. application-0a*.css, application-9c*.css, ...) when using Capistrano so that latest one should be non-digested. Here the logic is:

namespace :my_app do
  task non_digested: :environment do
    assets = Dir.glob(File.join(Rails.root, 'public/assets/**/*'))
    regex = /(?<!manifest)(-{1}[a-z0-9]{32}\.{1}){1}/
    candidates = {}

    # gather files info
    assets.each do |file|
      next if File.directory?(file) || file !~ regex

      source = file.split('/')
      source.push(source.pop.gsub(regex, '.'))
      non_digested = File.join(source)
      file_mtime = File.stat(file).mtime
      c = candidates[non_digested]
      if c.blank? || file_mtime > c[:mtime]
        candidates[non_digested] = {orig: file, mtime: file_mtime}
      end
    end

    # genearate
    for non_digested, val in candidates do
      FileUtils.cp(val[:orig], non_digested)
    end
  end
end

Rake::Task['assets:precompile'].enhance do
  Rake::Task['my_app:non_digested'].invoke
end

As wells as, I applied 907th's regex comment, and add hook let this task executes after precompile.




回答6:


Set this property config.assets.digest = false in `development.rb' file



来源:https://stackoverflow.com/questions/17536023/rake-assetsprecompilenodigest-in-rails-4

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!