class Sprockets::Asset
‘Asset` is the base class for `BundledAsset` and `StaticAsset`.
def self.from_hash(environment, hash)
def self.from_hash(environment, hash) return unless hash.is_a?(Hash) klass = case hash['class'] when 'BundledAsset' BundledAsset when 'ProcessedAsset' ProcessedAsset when 'StaticAsset' StaticAsset else nil end if klass asset = klass.allocate asset.init_with(environment, hash) asset end rescue UnserializeError nil end
def body
def body source end
def dependencies
def dependencies [] end
def dependency_fresh?(environment, dep)
A `Hash` is used rather than other `Asset` object because we
`dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
Check if dependency is fresh.
def dependency_fresh?(environment, dep) path, mtime, hexdigest = dep.pathname.to_s, dep.mtime, dep.digest stat = environment.stat(path) # If path no longer exists, its definitely stale. if stat.nil? return false end # Compare dependency mtime to the actual mtime. If the # dependency mtime is newer than the actual mtime, the file # hasn't changed since we created this `Asset` instance. # # However, if the mtime is newer it doesn't mean the asset is # stale. Many deployment environments may recopy or recheckout # assets on each deploy. In this case the mtime would be the # time of deploy rather than modified time. # # Note: to_i is used in eql? and write_to we assume fidelity of 1 second # if people save files more frequently than 1 second sprockets may # not pick it up, by design if mtime.to_i >= stat.mtime.to_i return true end digest = environment.file_digest(path) # If the mtime is newer, do a full digest comparsion. Return # fresh if the digests match. if hexdigest == digest.hexdigest return true end # Otherwise, its stale. false end
def dependency_paths
Internal: String paths that are marked as dependencies after processing.
def dependency_paths @dependency_paths ||= [] end
def digest_path
"foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
Return logical path with digest spliced in.
def digest_path logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" } end
def each
Add enumerator to allow `Asset` instances to be used as Rack
def each yield to_s end
def encode_with(coder)
def encode_with(coder) coder['class'] = self.class.name.sub(/Sprockets::/, '') coder['logical_path'] = logical_path coder['pathname'] = relativize_root_path(pathname).to_s coder['content_type'] = content_type coder['mtime'] = mtime.to_i coder['length'] = length coder['digest'] = digest end
def eql?(other)
def eql?(other) other.class == self.class && other.logical_path == self.logical_path && other.mtime.to_i == self.mtime.to_i && other.digest == self.digest end
def expand_root_path(path)
def expand_root_path(path) path.to_s.sub(/^\$root/, @root) end
def fresh?(environment)
digest to the inmemory model.
Checks if Asset is fresh by comparing the actual mtime and
def fresh?(environment) # Check current mtime and digest dependency_fresh?(environment, self) end
def hash
def hash digest.hash end
def init_with(environment, coder)
def init_with(environment, coder) @root = environment.root @logical_path = coder['logical_path'] @content_type = coder['content_type'] @digest = coder['digest'] if pathname = coder['pathname'] # Expand `$root` placeholder and wrapper string in a `Pathname` @pathname = Pathname.new(expand_root_path(pathname)) end if mtime = coder['mtime'] @mtime = Time.at(mtime) end if length = coder['length'] # Convert length to an `Integer` @length = Integer(length) end end
def initialize(environment, logical_path, pathname)
def initialize(environment, logical_path, pathname) raise ArgumentError, "Asset logical path has no extension: #{logical_path}" if File.extname(logical_path) == "" @root = environment.root @logical_path = logical_path.to_s @pathname = Pathname.new(pathname) @content_type = environment.content_type_of(pathname) # drop precision to 1 second, same pattern followed elsewhere @mtime = Time.at(environment.stat(pathname).mtime.to_i) @length = environment.stat(pathname).size @digest = environment.file_digest(pathname).hexdigest end
def inspect
def inspect "#<#{self.class}:0x#{object_id.to_s(16)} " + "pathname=#{pathname.to_s.inspect}, " + "mtime=#{mtime.inspect}, " + "digest=#{digest.inspect}" + ">" end
def relative_pathname
def relative_pathname @relative_pathname ||= Pathname.new(relativize_root_path(pathname)) end
def relativize_root_path(path)
def relativize_root_path(path) path.to_s.sub(/^#{Regexp.escape(@root)}/, '$root') end
def required_assets
Internal: `ProccessedAsset`s that are required after processing.
def required_assets @required_assets ||= [] end
def stale?(environment)
digest to the inmemory model.
Checks if Asset is stale by comparing the actual mtime and
def stale?(environment) !fresh?(environment) end
def to_a
This allows you to link to individual files for debugging
the asset's contents as a whole.
Appending all of an assets body parts together should give you
Expand asset into an `Array` of parts.
def to_a [self] end
def to_s
def to_s source end
def write_to(filename, options = {})
def write_to(filename, options = {}) # Gzip contents if filename has '.gz' options[:compress] ||= File.extname(filename) == '.gz' FileUtils.mkdir_p File.dirname(filename) File.open("#{filename}+", 'wb') do |f| if options[:compress] # Run contents through `Zlib` gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION) gz.mtime = mtime.to_i gz.write to_s gz.close else # Write out as is f.write to_s end end # Atomic write FileUtils.mv("#{filename}+", filename) # Set mtime correctly File.utime(mtime, mtime, filename) nil ensure # Ensure tmp file gets cleaned up FileUtils.rm("#{filename}+") if File.exist?("#{filename}+") end