lib/sprockets/base.rb in sprockets-2.0.5 vs lib/sprockets/base.rb in sprockets-2.1.0.beta
- old
+ new
@@ -1,21 +1,78 @@
require 'sprockets/asset_attributes'
require 'sprockets/bundled_asset'
require 'sprockets/caching'
-require 'sprockets/digest'
+require 'sprockets/processed_asset'
require 'sprockets/processing'
require 'sprockets/server'
require 'sprockets/static_asset'
require 'sprockets/trail'
require 'pathname'
module Sprockets
# `Base` class for `Environment` and `Index`.
class Base
- include Digest
include Caching, Processing, Server, Trail
+ # Returns a `Digest` implementation class.
+ #
+ # Defaults to `Digest::MD5`.
+ attr_reader :digest_class
+
+ # Assign a `Digest` implementation class. This maybe any Ruby
+ # `Digest::` implementation such as `Digest::MD5` or
+ # `Digest::SHA1`.
+ #
+ # environment.digest_class = Digest::SHA1
+ #
+ def digest_class=(klass)
+ expire_index!
+ @digest_class = klass
+ end
+
+ # 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.
+ attr_reader :version
+
+ # Assign an environment version.
+ #
+ # environment.version = '2.0'
+ #
+ def version=(version)
+ expire_index!
+ @version = version
+ 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.
+ 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
+
# Get and set `Logger` instance.
attr_accessor :logger
# Get `Context` class.
#
@@ -61,18 +118,14 @@
end
# Read and compute digest of filename.
#
# Subclasses may cache this method.
- def file_digest(path, data = nil)
+ def file_digest(path)
if stat = self.stat(path)
- # `data` maybe provided
- if data
- digest.update(data)
-
# If its a file, digest the contents
- elsif stat.file?
+ 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(',')
@@ -91,17 +144,25 @@
attributes_for(path).content_type
end
# Find asset by logical path or expanded path.
def find_asset(path, options = {})
- pathname = Pathname.new(path)
+ logical_path = path
+ pathname = Pathname.new(path)
- if pathname.absolute?
- build_asset(attributes_for(pathname).logical_path, pathname, options)
+ if pathname.to_s =~ /^\//
+ return unless stat(pathname)
+ logical_path = attributes_for(pathname).logical_path
else
- find_asset_in_path(pathname, options)
+ begin
+ pathname = resolve(logical_path)
+ rescue FileNotFound
+ return nil
+ end
end
+
+ build_asset(logical_path, pathname, options)
end
# Preferred `find_asset` shorthand.
#
# environment['application.js']
@@ -170,17 +231,37 @@
end
def build_asset(logical_path, pathname, options)
pathname = Pathname.new(pathname)
- return unless stat(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?
- BundledAsset.new(self, logical_path, pathname, options)
+ 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(self, logical_path, pathname)
+ StaticAsset.new(index, logical_path, pathname)
end
+ end
+
+ def cache_key_for(path, options)
+ "#{path}:#{options[:bundle] ? '1' : '0'}"
+ end
+
+ 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
end
end