class Sprockets::Base

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

def [](*args)


environment['application.js']

Preferred `find_asset` shorthand.
def [](*args)
  find_asset(*args)
end

def append_path(path)

def append_path(path)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def attributes_for(path)

Internal. Return a `AssetAttributes` for `path`.
def attributes_for(path)
  AssetAttributes.new(self, path)
end

def build_asset(logical_path, pathname, options)

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

def cache=(cache)

`[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
setters. Either `get(key)`/`set(key, value)`,
The cache store must implement a pair of getters and

Set persistent cache store
def cache=(cache)
  expire_index!
  @cache = cache
end

def cache_key_for(path, options)

def cache_key_for(path, options)
  "#{path}:#{options[:bundle] ? '1' : '0'}"
end

def circular_call_protection(path)

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

def clear_paths

def clear_paths
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def content_type_of(path)

Internal. Return content type of `path`.
def content_type_of(path)
  attributes_for(path).content_type
end

def digest

its assets.
digests. Any change in the environment digest will affect all of
The value also provides a seed digest for all `Asset`

cached assets.
than in the same. Two equal `Environment`s can share the same
useful for comparing environment states between processes rather
same digest value they can be treated as equal. This is more
This value serves two purposes. If two `Environment`s have the

Returns a `Digest` instance for the `Environment`.
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

def digest_class=(klass)


environment.digest_class = Digest::SHA1

`Digest::SHA1`.
`Digest::` implementation such as `Digest::MD5` or
Assign a `Digest` implementation class. This maybe any Ruby
def digest_class=(klass)
  expire_index!
  @digest_class = klass
end

def each_entry(root, &block)

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

def each_file

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

def each_logical_path(*args, &block)

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

def entries(pathname)

Subclasses may cache this method.

Works like `Dir.entries`.
def entries(pathname)
  @trail.entries(pathname)
end

def expire_index!

Clear index after mutating state. Must be implemented by the subclass.
def expire_index!
  raise NotImplementedError
end

def file_digest(path)

Subclasses may cache this method.

Read and compute digest of filename.
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

def find_asset(path, options = {})

Find asset by logical path or expanded path.
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

def index

Return an `Index`. Must be implemented by the subclass.
def index
  raise NotImplementedError
end

def inspect

Pretty inspect
def inspect
  "#<#{self.class}:0x#{object_id.to_s(16)} " +
    "root=#{root.to_s.inspect}, " +
    "paths=#{paths.inspect}, " +
    "digest=#{digest.to_s.inspect}" +
    ">"
end

def json_decode(obj)

def json_decode(obj)
  MultiJson.load(obj)
end

def json_decode(obj)

def json_decode(obj)
  MultiJson.decode(obj)
end

def logical_path_for_filename(filename, filters)

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

def matches_filter(filters, logical_path, filename)

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

def prepend_path(path)

def prepend_path(path)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def register_bundle_processor(mime_type, klass, &block)

def register_bundle_processor(mime_type, klass, &block)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def register_engine(ext, klass)

Registers a new Engine `klass` for `ext`.
def register_engine(ext, klass)
  # Overrides the global behavior to expire the index
  expire_index!
  add_engine_to_trail(ext, klass)
  super
end

def register_mime_type(mime_type, ext)

Register a new mime type.
def register_mime_type(mime_type, ext)
  # Overrides the global behavior to expire the index
  expire_index!
  @trail.append_extension(ext)
  super
end

def register_postprocessor(mime_type, klass, &block)

def register_postprocessor(mime_type, klass, &block)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def register_preprocessor(mime_type, klass, &block)

def register_preprocessor(mime_type, klass, &block)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def resolve(logical_path, options = {})

A `FileNotFound` exception is raised if the file does not exist.

# => "/path/to/app/javascripts/application.js.coffee"
resolve("application.js")

searching the environment's paths.
Finds the expanded real path for a given logical path by
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 %w( bower.json component.json ).include?(pathname.basename.to_s)
        bower = json_decode(pathname.read)
        case bower['main']
        when String
          yield pathname.dirname.join(bower['main'])
        when Array
          extname = File.extname(logical_path)
          bower['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

def stat(path)

Subclasses may cache this method.

Works like `File.stat`.
def stat(path)
  @trail.stat(path)
end

def unregister_bundle_processor(mime_type, klass)

def unregister_bundle_processor(mime_type, klass)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def unregister_postprocessor(mime_type, klass)

def unregister_postprocessor(mime_type, klass)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def unregister_preprocessor(mime_type, klass)

def unregister_preprocessor(mime_type, klass)
  # Overrides the global behavior to expire the index
  expire_index!
  super
end

def version=(version)


environment.version = '2.0'

Assign an environment version.
def version=(version)
  expire_index!
  @version = version
end