Class: Sprockets::Manifest
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Instance Chain:
self,
ManifestUtils
|
|
Inherits: | Object |
Defined in: | lib/sprockets/manifest.rb |
Overview
The Manifest logs the contents of assets compiled to a single directory. It records basic attributes about the asset for fast lookup without having to compile. A pointer from each logical path indicates which fingerprinted asset is the current one.
The JSON is part of the public API and should be considered stable. This should make it easy to read from other programming languages and processes that don’t have sprockets loaded. See #assets and #files for more information about the structure.
Constant Summary
ManifestUtils
- Included
Class Method Summary
-
.new(*args) ⇒ Manifest
constructor
Create new
Manifest
associated with an #environment.
Instance Attribute Summary
-
#dir
readonly
Alias for #directory.
- #directory (also: #dir) readonly
- #environment readonly
-
#filename
(also: #path)
readonly
Returns String path to manifest.json file.
-
#path
readonly
Alias for #filename.
Instance Method Summary
-
#assets
Returns internal assets mapping.
-
#clean(count = 2, age = 3600)
Cleanup old assets in the compile directory.
-
#clobber
Wipe directive.
-
#compile(*args)
Compile asset to directory.
-
#files
Returns internal file directory listing.
-
#find(*args, &block)
Public: Find all assets matching pattern set in environment.
-
#find_sources(*args)
Public: Find the source of assets by paths.
-
#remove(filename)
Removes file from directory and from manifest.
-
#save
Persist manifest back to FS.
- #executor private
-
#exporters_for_asset(asset)
private
Given an asset, finds all exporters that match its mime-type.
- #json_decode(obj) private
- #json_encode(obj) private
- #logger private
ManifestUtils
- Included
#find_directory_manifest | Public: Find or pick a new manifest filename for target build directory. |
#generate_manifest_path | Public: Generate a new random manifest path. |
Constructor Details
.new(*args) ⇒ Manifest
Create new Manifest
associated with an #environment. #filename is a full path to the manifest json file. The file may or may not already exist. The dirname of the #filename will be used to write compiled assets to. Otherwise, if the path is a directory, the filename will default a random “.sprockets-manifest-*.json” file in that directory.
Manifest.new(environment, "./public/assets/manifest.json")
# File 'lib/sprockets/manifest.rb', line 32
def initialize(*args) if args.first.is_a?(Base) || args.first.nil? @environment = args.shift end @directory, @filename = args[0], args[1] # Whether the manifest file is using the old manifest-*.json naming convention @legacy_manifest = false # Expand paths @directory = File. (@directory) if @directory @filename = File. (@filename) if @filename # If filename is given as the second arg if @directory && File.extname(@directory) != "" @directory, @filename = nil, @directory end # Default dir to the directory of the filename @directory ||= File.dirname(@filename) if @filename # If directory is given w/o filename, pick a random manifest location if @directory && @filename.nil? @filename = find_directory_manifest(@directory, logger) end unless @directory && @filename raise ArgumentError, "manifest requires output filename" end data = {} begin if File.exist?(@filename) data = json_decode(File.read(@filename)) end rescue JSON::ParserError => e logger.error "#{@filename} is invalid: #{e.class} #{e.}" end @data = data end
Instance Attribute Details
#dir (readonly)
Alias for #directory.
# File 'lib/sprockets/manifest.rb', line 81
alias_method :dir, :directory
#directory (readonly) Also known as: #dir
[ GitHub ]# File 'lib/sprockets/manifest.rb', line 80
attr_reader :directory
#environment (readonly)
[ GitHub ]# File 'lib/sprockets/manifest.rb', line 22
attr_reader :environment
#filename (readonly) Also known as: #path
Returns String path to manifest.json file.
# File 'lib/sprockets/manifest.rb', line 77
attr_reader :filename
#path (readonly)
Alias for #filename.
# File 'lib/sprockets/manifest.rb', line 78
alias_method :path, :filename
Instance Method Details
#assets
# File 'lib/sprockets/manifest.rb', line 91
def assets @data['assets'] ||= {} end
#clean(count = 2, age = 3600)
Cleanup old assets in the compile directory. By default it will keep the latest version, 2 backups and any created within the past hour.
Examples
To force only 1 backup to be kept, set count=1 and age=0.
To only keep files created within the last 10 minutes, set count=0 and
age=600.
# File 'lib/sprockets/manifest.rb', line 246
def clean(count = 2, age = 3600) asset_versions = files.group_by { |_, attrs| attrs['logical_path'] } asset_versions.each do |logical_path, versions| current = assets[logical_path] versions.reject { |path, _| path == current }.sort_by { |_, attrs| # Sort by timestamp Time.parse(attrs['mtime']) }.reverse.each_with_index.drop_while { |(_, attrs), index| _age = [0, Time.now - Time.parse(attrs['mtime'])].max # Keep if under age or within the count limit _age < age || index < count }.each { |(path, _), _| # Remove old assets remove(path) } end end
#clobber
Wipe directive
# File 'lib/sprockets/manifest.rb', line 269
def clobber FileUtils.rm_r(directory) if File.exist?(directory) logger.info "Removed #{directory}" # if we have an environment clear the cache too environment.cache.clear if environment nil end
#compile(*args)
Compile asset to directory. The asset is written to a fingerprinted filename like application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js
. An entry is also inserted into the manifest file.
compile("application.js")
# File 'lib/sprockets/manifest.rb', line 161
def compile(*args) unless environment raise Error, "manifest requires environment for compilation" end filenames = [] concurrent_exporters = [] assets_to_export = Concurrent::Array.new find(*args) do |asset| assets_to_export << asset end assets_to_export.each do |asset| mtime = Time.now.iso8601 files[asset.digest_path] = { 'logical_path' => asset.logical_path, 'mtime' => mtime, 'size' => asset.bytesize, 'digest' => asset.hexdigest, # Deprecated: Remove beta integrity attribute in next release. # Callers should DigestUtils.hexdigest_integrity_uri to compute the # digest themselves. 'integrity' => DigestUtils.hexdigest_integrity_uri(asset.hexdigest) } assets[asset.logical_path] = asset.digest_path filenames << asset.filename promise = nil exporters_for_asset(asset) do |exporter| next if exporter.skip?(logger) if promise.nil? promise = Concurrent::Promise.new(executor: executor) { exporter.call } concurrent_exporters << promise.execute else concurrent_exporters << promise.then { exporter.call } end end end # make sure all exporters have finished before returning the main thread concurrent_exporters.each(&:wait!) save filenames end
#executor (private)
[ GitHub ]# File 'lib/sprockets/manifest.rb', line 335
def executor @executor ||= environment.export_concurrent ? :fast : :immediate end
#exporters_for_asset(asset) (private)
Given an asset, finds all exporters that match its mime-type.
Will yield each expoter to the passed in block.
array = []
puts asset.content_type # => "application/javascript"
exporters_for_asset(asset) do |exporter|
array << exporter
end
# puts array => [Exporters::FileExporter, Exporters::ZlibExporter]
# File 'lib/sprockets/manifest.rb', line 299
def exporters_for_asset(asset) exporters = [Exporters::FileExporter] environment.exporters.each do |mime_type, exporter_list| next unless asset.content_type next unless environment.match_mime_type? asset.content_type, mime_type exporter_list.each do |exporter| exporters << exporter end end exporters.uniq! exporters.each do |exporter| yield exporter.new(asset: asset, environment: environment, directory: dir) end end
#files
Returns internal file directory listing. Keys are filenames which map to an attributes array.
Fingerprint path (String):
logical_path: Logical path (String)
mtime: ISO8601 mtime (String)
digest: Base64 hex digest (String)
{ "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js" =>
{ 'logical_path' => "application.js",
'mtime' => "2011-12-13T21:47:08-06:00",
'digest' => "2e8e9a7c6b0aafa0c9bdeec90ea30213" } }
# File 'lib/sprockets/manifest.rb', line 108
def files @data['files'] ||= {} end
#find(*args, &block)
Public: Find all assets matching pattern set in environment.
Returns Enumerator of Assets.
# File 'lib/sprockets/manifest.rb', line 115
def find(*args, &block) unless environment raise Error, "manifest requires environment for compilation" end return to_enum(__method__, *args) unless block_given? environment = self.environment.cached promises = args.flatten.map do |path| Concurrent::Promise.execute(executor: executor) do environment.find_all_linked_assets(path).to_a end end promises.each do |promise| promise.value!.each(&block) end nil end
#find_sources(*args)
Public: Find the source of assets by paths.
Returns Enumerator of assets file content.
#json_decode(obj) (private)
[ GitHub ]# File 'lib/sprockets/manifest.rb', line 317
def json_decode(obj) JSON.parse(obj, create_additions: false) end
#json_encode(obj) (private)
[ GitHub ]# File 'lib/sprockets/manifest.rb', line 321
def json_encode(obj) JSON.generate(obj) end
#logger (private)
[ GitHub ]# File 'lib/sprockets/manifest.rb', line 325
def logger if environment environment.logger else logger = Logger.new($stderr) logger.level = Logger::FATAL logger end end
#remove(filename)
Removes file from directory and from manifest. #filename must be the name with any directory path.
manifest.remove("application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js")
# File 'lib/sprockets/manifest.rb', line 216
def remove(filename) path = File.join(dir, filename) gzip = "#{path}.gz" logical_path = files[filename]['logical_path'] if assets[logical_path] == filename assets.delete(logical_path) end files.delete(filename) FileUtils.rm(path) if File.exist?(path) FileUtils.rm(gzip) if File.exist?(gzip) save logger.info "Removed #{filename}" nil end
#save
Persist manifest back to FS
# File 'lib/sprockets/manifest.rb', line 278
def save data = json_encode(@data) FileUtils.mkdir_p File.dirname(@filename) PathUtils.atomic_write(@filename) do |f| f.write(data) end end