class Sprockets::Base

`Base` class for `Environment` and `Index`.

Attributes

cache[R]

Get persistent cache store

context_class[R]

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
digest_class[R]

Returns a `Digest` implementation class.

Defaults to `Digest::MD5`.

logger[RW]

Get and set `Logger` instance.

version[R]

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.

Public Instance Methods

[](*args) click to toggle source

Preferred `find_asset` shorthand.

environment['application.js']
# File lib/sprockets/base.rb, line 294
def [](*args)
  find_asset(*args)
end
append_path(path) click to toggle source
Calls superclass method Sprockets::Paths#append_path
# File lib/sprockets/base.rb, line 107
def append_path(path)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end
attributes_for(path) click to toggle source

Internal. Return a `AssetAttributes` for `path`.

# File lib/sprockets/base.rb, line 252
def attributes_for(path)
  AssetAttributes.new(self, path)
end
cache=(cache) click to toggle source

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
clear_paths() click to toggle source
Calls superclass method Sprockets::Paths#clear_paths
# File lib/sprockets/base.rb, line 113
def clear_paths
  # Overrides the global behavior to expire the index
  expire_index!
  super
end
content_type_of(path) click to toggle source

Internal. Return content type of `path`.

# File lib/sprockets/base.rb, line 257
def content_type_of(path)
  attributes_for(path).content_type
end
digest() click to toggle source

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
digest_class=(klass) click to toggle source

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
each_entry(root, &block) click to toggle source
# 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
each_file() { |path| ... } click to toggle source
# 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
each_logical_path(*args) { |logical_path, filename| ... } click to toggle source
# 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
entries(pathname) click to toggle source

Works like `Dir.entries`.

Subclasses may cache this method.

# File lib/sprockets/base.rb, line 223
def entries(pathname)
  @trail.entries(pathname)
end
file_digest(path) click to toggle source

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(path, options = {}) click to toggle source

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
index() click to toggle source

Return an `Index`. Must be implemented by the subclass.

# File lib/sprockets/base.rb, line 210
def index
  raise NotImplementedError
end
inspect() click to toggle source

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
prepend_path(path) click to toggle source
Calls superclass method Sprockets::Paths#prepend_path
# File lib/sprockets/base.rb, line 101
def prepend_path(path)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end
register_bundle_processor(mime_type, klass, &block) click to toggle source
# 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
register_engine(ext, klass) click to toggle source

Registers a new Engine `klass` for `ext`.

Calls superclass method Sprockets::Engines#register_engine
# 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_mime_type(mime_type, ext) click to toggle source

Register a new mime type.

Calls superclass method Sprockets::Mime#register_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
register_postprocessor(mime_type, klass, &block) click to toggle source
# 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
register_preprocessor(mime_type, klass, &block) click to toggle source
# 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
resolve(logical_path, options = {}) { |dirname.join(component)| ... } click to toggle source

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
stat(path) click to toggle source

Works like `File.stat`.

Subclasses may cache this method.

# File lib/sprockets/base.rb, line 230
def stat(path)
  @trail.stat(path)
end
unregister_bundle_processor(mime_type, klass) click to toggle source
# 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
unregister_postprocessor(mime_type, klass) click to toggle source
# 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
unregister_preprocessor(mime_type, klass) click to toggle source
# 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
version=(version) click to toggle source

Assign an environment version.

environment.version = '2.0'
# File lib/sprockets/base.rb, line 48
def version=(version)
  expire_index!
  @version = version
end

Protected Instance Methods

build_asset(logical_path, pathname, options) click to toggle source
# 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
cache_key_for(path, options) click to toggle source
# File lib/sprockets/base.rb, line 384
def cache_key_for(path, options)
  "#{path}:#{options[:bundle] ? '1' : '0'}"
end
circular_call_protection(path) { || ... } click to toggle source
# 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
expire_index!() click to toggle source

Clear index after mutating state. Must be implemented by the subclass.

# File lib/sprockets/base.rb, line 362
def expire_index!
  raise NotImplementedError
end
json_decode(obj) click to toggle source
# File lib/sprockets/base.rb, line 438
def json_decode(obj)
  MultiJson.load(obj)
end
logical_path_for_filename(filename, filters) click to toggle source
# 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
matches_filter(filters, logical_path, filename) click to toggle source
# 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