`Base` class for `Environment` and `Index`.
Get persistent cache store
Get `Context` class.
This class maybe mutated and mixed in with custom helpers.
environment.context_class.instance_eval do include MyHelpers def asset_url; end end
Returns a `Digest` implementation class.
Defaults to `Digest::MD5`.
Get and set `Logger` instance.
The `Environment#version` is a custom value used for manually expiring all asset caches.
Sprockets is able to track most file and directory changes and will take care of expiring the cache for you. However, its impossible to know when any custom helpers change that you mix into the `Context`.
It would be wise to increment this value anytime you make a configuration change to the `Environment` object.
Preferred `find_asset` shorthand.
environment['application.js']
# File lib/sprockets/base.rb, line 294 def [](*args) find_asset(*args) end
# File lib/sprockets/base.rb, line 107 def append_path(path) # Overrides the global behavior to expire the index expire_index! super end
Internal. Return a `AssetAttributes` for `path`.
# File lib/sprockets/base.rb, line 252 def attributes_for(path) AssetAttributes.new(self, path) end
Set persistent cache store
The cache store must implement a pair of getters and setters. Either `get(key)`/`set(key, value)`, `[key]`/`=value`, `read(key)`/`write(key, value)`.
# File lib/sprockets/base.rb, line 96 def cache=(cache) expire_index! @cache = cache end
# File lib/sprockets/base.rb, line 113 def clear_paths # Overrides the global behavior to expire the index expire_index! super end
Internal. Return content type of `path`.
# File lib/sprockets/base.rb, line 257 def content_type_of(path) attributes_for(path).content_type end
Returns a `Digest` instance for the `Environment`.
This value serves two purposes. If two `Environment`s have the same digest value they can be treated as equal. This is more useful for comparing environment states between processes rather than in the same. Two equal `Environment`s can share the same cached assets.
The value also provides a seed digest for all `Asset` digests. Any change in the environment digest will affect all of its assets.
# File lib/sprockets/base.rb, line 64 def digest # Compute the initial digest using the implementation class. The # Sprockets release version and custom environment version are # mixed in. So any new releases will affect all your assets. @digest ||= digest_class.new.update(VERSION).update(version.to_s) # Returned a dupped copy so the caller can safely mutate it with `.update` @digest.dup end
Assign a `Digest` implementation class. This maybe any Ruby `Digest::` implementation such as `Digest::MD5` or `Digest::SHA1`.
environment.digest_class = Digest::SHA1
# File lib/sprockets/base.rb, line 27 def digest_class=(klass) expire_index! @digest_class = klass end
# File lib/sprockets/base.rb, line 298 def each_entry(root, &block) return to_enum(__method__, root) unless block_given? root = Pathname.new(root) unless root.is_a?(Pathname) paths = [] entries(root).sort.each do |filename| path = root.join(filename) paths << path if stat(path).directory? each_entry(path) do |subpath| paths << subpath end end end paths.sort_by(&:to_s).each(&block) nil end
# File lib/sprockets/base.rb, line 319 def each_file return to_enum(__method__) unless block_given? paths.each do |root| each_entry(root) do |path| if !stat(path).directory? yield path end end end nil end
# File lib/sprockets/base.rb, line 331 def each_logical_path(*args, &block) return to_enum(__method__, *args) unless block_given? filters = args.flatten files = {} each_file do |filename| if logical_path = logical_path_for_filename(filename, filters) unless files[logical_path] if block.arity == 2 yield logical_path, filename.to_s else yield logical_path end end files[logical_path] = true end end nil end
Works like `Dir.entries`.
Subclasses may cache this method.
# File lib/sprockets/base.rb, line 223 def entries(pathname) @trail.entries(pathname) end
Read and compute digest of filename.
Subclasses may cache this method.
# File lib/sprockets/base.rb, line 237 def file_digest(path) if stat = self.stat(path) # If its a file, digest the contents if stat.file? digest.file(path.to_s) # If its a directive, digest the list of filenames elsif stat.directory? contents = self.entries(path).join(',') digest.update(contents) end end end
Find asset by logical path or expanded path.
# File lib/sprockets/base.rb, line 262 def find_asset(path, options = {}) logical_path = path pathname = Pathname.new(path) if pathname.absolute? return unless stat(pathname) logical_path = attributes_for(pathname).logical_path else begin pathname = resolve(logical_path) # If logical path is missing a mime type extension, append # the absolute path extname so it has one. # # Ensures some consistency between finding "foo/bar" vs # "foo/bar.js". if File.extname(logical_path) == "" expanded_logical_path = attributes_for(pathname).logical_path logical_path += File.extname(expanded_logical_path) end rescue FileNotFound return nil end end build_asset(logical_path, pathname, options) end
Return an `Index`. Must be implemented by the subclass.
# File lib/sprockets/base.rb, line 210 def index raise NotImplementedError end
Pretty inspect
# File lib/sprockets/base.rb, line 352 def inspect "#<#{self.class}:0x#{object_id.to_s(16)} " + "root=#{root.to_s.inspect}, " + "paths=#{paths.inspect}, " + "digest=#{digest.to_s.inspect}" + ">" end
# File lib/sprockets/base.rb, line 101 def prepend_path(path) # Overrides the global behavior to expire the index expire_index! super end
# File lib/sprockets/base.rb, line 197 def register_bundle_processor(mime_type, klass, &block) # Overrides the global behavior to expire the index expire_index! super end
Registers a new Engine `klass` for `ext`.
# File lib/sprockets/base.rb, line 166 def register_engine(ext, klass) # Overrides the global behavior to expire the index expire_index! add_engine_to_trail(ext, klass) super end
Register a new mime type.
# File lib/sprockets/base.rb, line 158 def register_mime_type(mime_type, ext) # Overrides the global behavior to expire the index expire_index! @trail.append_extension(ext) super end
# File lib/sprockets/base.rb, line 185 def register_postprocessor(mime_type, klass, &block) # Overrides the global behavior to expire the index expire_index! super end
# File lib/sprockets/base.rb, line 173 def register_preprocessor(mime_type, klass, &block) # Overrides the global behavior to expire the index expire_index! super end
Finds the expanded real path for a given logical path by searching the environment's paths.
resolve("application.js") # => "/path/to/app/javascripts/application.js.coffee"
A `FileNotFound` exception is raised if the file does not exist.
# File lib/sprockets/base.rb, line 126 def resolve(logical_path, options = {}) # If a block is given, preform an iterable search if block_given? args = attributes_for(logical_path).search_paths + [options] @trail.find(*args) do |path| pathname = Pathname.new(path) if pathname.basename.to_s == 'component.json' component = json_decode(pathname.read) case component['main'] when String yield pathname.dirname.join(component['main']) when Array extname = File.extname(logical_path) component['main'].each do |fn| if extname == "" || extname == File.extname(fn) yield pathname.dirname.join(fn) end end end else yield pathname end end else resolve(logical_path, options) do |pathname| return pathname end raise FileNotFound, "couldn't find file '#{logical_path}'" end end
Works like `File.stat`.
Subclasses may cache this method.
# File lib/sprockets/base.rb, line 230 def stat(path) @trail.stat(path) end
# File lib/sprockets/base.rb, line 203 def unregister_bundle_processor(mime_type, klass) # Overrides the global behavior to expire the index expire_index! super end
# File lib/sprockets/base.rb, line 191 def unregister_postprocessor(mime_type, klass) # Overrides the global behavior to expire the index expire_index! super end
# File lib/sprockets/base.rb, line 179 def unregister_preprocessor(mime_type, klass) # Overrides the global behavior to expire the index expire_index! super end
Assign an environment version.
environment.version = '2.0'
# File lib/sprockets/base.rb, line 48 def version=(version) expire_index! @version = version end
# File lib/sprockets/base.rb, line 366 def build_asset(logical_path, pathname, options) pathname = Pathname.new(pathname) # If there are any processors to run on the pathname, use # `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary. if attributes_for(pathname).processors.any? if options[:bundle] == false circular_call_protection(pathname.to_s) do ProcessedAsset.new(index, logical_path, pathname) end else BundledAsset.new(index, logical_path, pathname) end else StaticAsset.new(index, logical_path, pathname) end end
# File lib/sprockets/base.rb, line 384 def cache_key_for(path, options) "#{path}:#{options[:bundle] ? '1' : '0'}" end
# File lib/sprockets/base.rb, line 388 def circular_call_protection(path) reset = Thread.current[:sprockets_circular_calls].nil? calls = Thread.current[:sprockets_circular_calls] ||= Set.new if calls.include?(path) raise CircularDependencyError, "#{path} has already been required" end calls << path yield ensure Thread.current[:sprockets_circular_calls] = nil if reset end
Clear index after mutating state. Must be implemented by the subclass.
# File lib/sprockets/base.rb, line 362 def expire_index! raise NotImplementedError end
# File lib/sprockets/base.rb, line 438 def json_decode(obj) MultiJson.load(obj) end
# File lib/sprockets/base.rb, line 400 def logical_path_for_filename(filename, filters) logical_path = attributes_for(filename).logical_path.to_s if matches_filter(filters, logical_path, filename) return logical_path end # If filename is an index file, retest with alias if File.basename(logical_path)[/[^\.]+/, 0] == 'index' path = logical_path.sub(/\/index\./, '.') if matches_filter(filters, path, filename) return path end end nil end
# File lib/sprockets/base.rb, line 418 def matches_filter(filters, logical_path, filename) return true if filters.empty? filters.any? do |filter| if filter.is_a?(Regexp) filter.match(logical_path) elsif filter.respond_to?(:call) if filter.arity == 1 filter.call(logical_path) else filter.call(logical_path, filename.to_s) end else File.fnmatch(filter.to_s, logical_path) end end end