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
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
Set this property config.assets.digest = false
in `development.rb' file
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
There is also a gem to do this for you: the somewhat provocatively named non-stupid-digest-assets.
gem "non-stupid-digest-assets"
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
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.