sprockets-2.10.1/ 0000755 0000041 0000041 00000000000 12260352334 013640 5 ustar www-data www-data sprockets-2.10.1/bin/ 0000755 0000041 0000041 00000000000 12260352334 014410 5 ustar www-data www-data sprockets-2.10.1/bin/sprockets 0000755 0000041 0000041 00000004112 12260352334 016351 0 ustar www-data www-data #!/usr/bin/env ruby
require 'sprockets'
require 'optparse'
require 'shellwords'
unless ARGV.delete("--noenv")
if File.exist?(path = "./.sprocketsrc")
rcflags = Shellwords.split(File.read(path))
ARGV.unshift(*rcflags)
end
end
filenames = []
environment = Sprockets::Environment.new(Dir.pwd)
manifest = nil
(ENV['SPROCKETS_PATH'] || "").split(File::PATH_SEPARATOR).each do |path|
environment.append_path path
end
OptionParser.new do |opts|
opts.summary_width = 28
opts.banner = "Usage: sprockets [options] filename [filename ...]"
def opts.show_usage
puts self
exit 1
end
opts.on("-r", "--require LIBRARY", "Require the LIBRARY before doing anything") do |lib|
require lib
end
opts.on("-I DIRECTORY", "--include=DIRECTORY", "Adds the directory to the Sprockets load path") do |directory|
environment.append_path directory
end
opts.on("-o DIRECTORY", "--output=DIRECTORY", "Copy provided assets into DIRECTORY") do |directory|
manifest = Sprockets::Manifest.new(environment, directory)
end
opts.on("--css-compressor=COMPRESSOR", "Use CSS compressor") do |compressor|
environment.css_compressor = compressor.to_sym
end
opts.on("--js-compressor=COMPRESSOR", "Use JavaScript compressor") do |compressor|
environment.js_compressor = compressor.to_sym
end
opts.on("--noenv", "Disables .sprocketsrc file") do
end
opts.on_tail("-h", "--help", "Shows this help message") do
opts.show_usage
end
opts.on_tail("-v", "--version", "Shows version") do
puts Sprockets::VERSION
exit
end
opts.show_usage if ARGV.empty?
begin
opts.order(ARGV) do |filename|
filenames << File.expand_path(filename)
end
rescue OptionParser::ParseError => e
opts.warn e.message
opts.show_usage
end
end
if environment.paths.empty?
warn "No load paths given"
warn "Usage: sprockets -Ijavascripts/ filename"
exit 1
end
if manifest
manifest.compile(filenames)
elsif filenames.length == 1
puts environment.find_asset(filenames.first).to_s
else
warn "Only one file can be compiled to stdout at a time"
exit 1
end
sprockets-2.10.1/lib/ 0000755 0000041 0000041 00000000000 12260352334 014406 5 ustar www-data www-data sprockets-2.10.1/lib/rake/ 0000755 0000041 0000041 00000000000 12260352334 015330 5 ustar www-data www-data sprockets-2.10.1/lib/rake/sprocketstask.rb 0000644 0000041 0000041 00000007245 12260352334 020565 0 ustar www-data www-data require 'rake'
require 'rake/tasklib'
require 'sprockets'
require 'logger'
module Rake
# Simple Sprockets compilation Rake task macro.
#
# Rake::SprocketsTask.new do |t|
# t.environment = Sprockets::Environment.new
# t.output = "./public/assets"
# t.assets = %w( application.js application.css )
# end
#
class SprocketsTask < Rake::TaskLib
# Name of the task. Defaults to "assets".
#
# The name will also be used to suffix the clean and clobber
# tasks, "clean_assets" and "clobber_assets".
attr_accessor :name
# `Environment` instance used for finding assets.
#
# You'll most likely want to reassign `environment` to your own.
#
# Rake::SprocketsTask.new do |t|
# t.environment = Foo::Assets
# end
#
def environment
if !@environment.is_a?(Sprockets::Base) && @environment.respond_to?(:call)
@environment = @environment.call
else
@environment
end
end
attr_writer :environment
# Returns cached indexed environment
def index
@index ||= environment.index if environment
end
# `Manifest` instance used for already compiled assets.
#
# Will be created by default if an environment and output
# directory are given
def manifest
if !@manifest.is_a?(Sprockets::Manifest) && @manifest.respond_to?(:call)
@manifest = @manifest.call
else
@manifest
end
end
attr_writer :manifest
# Directory to write compiled assets too. As well as the manifest file.
#
# t.output = "./public/assets"
#
attr_accessor :output
# Array of asset logical paths to compile.
#
# t.assets = %w( application.js jquery.js application.css )
#
attr_accessor :assets
# Number of old assets to keep.
attr_accessor :keep
# Logger to use during rake tasks. Defaults to using stderr.
#
# t.logger = Logger.new($stdout)
#
attr_accessor :logger
# Returns logger level Integer.
def log_level
@logger.level
end
# Set logger level with constant or symbol.
#
# t.log_level = Logger::INFO
# t.log_level = :debug
#
def log_level=(level)
if level.is_a?(Integer)
@logger.level = level
else
@logger.level = Logger.const_get(level.to_s.upcase)
end
end
def initialize(name = :assets)
@name = name
@environment = lambda { Sprockets::Environment.new(Dir.pwd) }
@manifest = lambda { Sprockets::Manifest.new(index, output) }
@logger = Logger.new($stderr)
@logger.level = Logger::INFO
@keep = 2
yield self if block_given?
define
end
# Define tasks
def define
desc name == :assets ? "Compile assets" : "Compile #{name} assets"
task name do
with_logger do
manifest.compile(assets)
end
end
desc name == :assets ? "Remove all assets" : "Remove all #{name} assets"
task "clobber_#{name}" do
with_logger do
manifest.clobber
end
end
task :clobber => ["clobber_#{name}"]
desc name == :assets ? "Clean old assets" : "Clean old #{name} assets"
task "clean_#{name}" do
with_logger do
manifest.clean(keep)
end
end
task :clean => ["clean_#{name}"]
end
private
# Sub out environment logger with our rake task logger that
# writes to stderr.
def with_logger
if env = manifest.environment
old_logger = env.logger
env.logger = @logger
end
yield
ensure
env.logger = old_logger if env
end
end
end
sprockets-2.10.1/lib/sprockets/ 0000755 0000041 0000041 00000000000 12260352334 016423 5 ustar www-data www-data sprockets-2.10.1/lib/sprockets/directive_processor.rb 0000644 0000041 0000041 00000027650 12260352334 023037 0 ustar www-data www-data require 'pathname'
require 'shellwords'
require 'tilt'
require 'yaml'
module Sprockets
# The `DirectiveProcessor` is responsible for parsing and evaluating
# directive comments in a source file.
#
# A directive comment starts with a comment prefix, followed by an "=",
# then the directive name, then any arguments.
#
# // JavaScript
# //= require "foo"
#
# # CoffeeScript
# #= require "bar"
#
# /* CSS
# *= require "baz"
# */
#
# The Processor is implemented as a `Tilt::Template` and is loosely
# coupled to Sprockets. This makes it possible to disable or modify
# the processor to do whatever you'd like. You could add your own
# custom directives or invent your own directive syntax.
#
# `Environment#processors` includes `DirectiveProcessor` by default.
#
# To remove the processor entirely:
#
# env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
# env.unregister_processor('application/javascript', Sprockets::DirectiveProcessor)
#
# Then inject your own preprocessor:
#
# env.register_processor('text/css', MyProcessor)
#
class DirectiveProcessor < Tilt::Template
# Directives will only be picked up if they are in the header
# of the source file. C style (/* */), JavaScript (//), and
# Ruby (#) comments are supported.
#
# Directives in comments after the first non-whitespace line
# of code will not be processed.
#
HEADER_PATTERN = /
\A (
(?m:\s*) (
(\/\* (?m:.*?) \*\/) |
(\#\#\# (?m:.*?) \#\#\#) |
(\/\/ .* \n?)+ |
(\# .* \n?)+
)
)+
/x
# Directives are denoted by a `=` followed by the name, then
# argument list.
#
# A few different styles are allowed:
#
# // =require foo
# //= require foo
# //= require "foo"
#
DIRECTIVE_PATTERN = /
^ \W* = \s* (\w+.*?) (\*\/)? $
/x
attr_reader :pathname
attr_reader :header, :body
def prepare
@pathname = Pathname.new(file)
@header = data[HEADER_PATTERN, 0] || ""
@body = $' || data
# Ensure body ends in a new line
@body += "\n" if @body != "" && @body !~ /\n\Z/m
@included_pathnames = []
@compat = false
end
# Implemented for Tilt#render.
#
# `context` is a `Context` instance with methods that allow you to
# access the environment and append to the bundle. See `Context`
# for the complete API.
def evaluate(context, locals, &block)
@context = context
@result = ""
@result.force_encoding(body.encoding) if body.respond_to?(:encoding)
@has_written_body = false
process_directives
process_source
@result
end
# Returns the header String with any directives stripped.
def processed_header
lineno = 0
@processed_header ||= header.lines.map { |line|
lineno += 1
# Replace directive line with a clean break
directives.assoc(lineno) ? "\n" : line
}.join.chomp
end
# Returns the source String with any directives stripped.
def processed_source
@processed_source ||= processed_header + body
end
# Returns an Array of directive structures. Each structure
# is an Array with the line number as the first element, the
# directive name as the second element, followed by any
# arguments.
#
# [[1, "require", "foo"], [2, "require", "bar"]]
#
def directives
@directives ||= header.lines.each_with_index.map { |line, index|
if directive = line[DIRECTIVE_PATTERN, 1]
name, *args = Shellwords.shellwords(directive)
if respond_to?("process_#{name}_directive", true)
[index + 1, name, *args]
end
end
}.compact
end
protected
attr_reader :included_pathnames
attr_reader :context
# Gathers comment directives in the source and processes them.
# Any directive method matching `process_*_directive` will
# automatically be available. This makes it easy to extend the
# processor.
#
# To implement a custom directive called `require_glob`, subclass
# `Sprockets::DirectiveProcessor`, then add a method called
# `process_require_glob_directive`.
#
# class DirectiveProcessor < Sprockets::DirectiveProcessor
# def process_require_glob_directive
# Dir["#{pathname.dirname}/#{glob}"].sort.each do |filename|
# require(filename)
# end
# end
# end
#
# Replace the current processor on the environment with your own:
#
# env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
# env.register_processor('text/css', DirectiveProcessor)
#
def process_directives
directives.each do |line_number, name, *args|
context.__LINE__ = line_number
send("process_#{name}_directive", *args)
context.__LINE__ = nil
end
end
def process_source
unless @has_written_body || processed_header.empty?
@result << processed_header << "\n"
end
included_pathnames.each do |pathname|
@result << context.evaluate(pathname)
end
unless @has_written_body
@result << body
end
if compat? && constants.any?
@result.gsub!(/<%=(.*?)%>/) { constants[$1.strip] }
end
end
# The `require` directive functions similar to Ruby's own `require`.
# It provides a way to declare a dependency on a file in your path
# and ensures its only loaded once before the source file.
#
# `require` works with files in the environment path:
#
# //= require "foo.js"
#
# Extensions are optional. If your source file is ".js", it
# assumes you are requiring another ".js".
#
# //= require "foo"
#
# Relative paths work too. Use a leading `./` to denote a relative
# path:
#
# //= require "./bar"
#
def process_require_directive(path)
if @compat
if path =~ /<([^>]+)>/
path = $1
else
path = "./#{path}" unless relative?(path)
end
end
context.require_asset(path)
end
# `require_self` causes the body of the current file to be
# inserted before any subsequent `require` or `include`
# directives. Useful in CSS files, where it's common for the
# index file to contain global styles that need to be defined
# before other dependencies are loaded.
#
# /*= require "reset"
# *= require_self
# *= require_tree .
# */
#
def process_require_self_directive
if @has_written_body
raise ArgumentError, "require_self can only be called once per source file"
end
context.require_asset(pathname)
process_source
included_pathnames.clear
@has_written_body = true
end
# The `include` directive works similar to `require` but
# inserts the contents of the dependency even if it already
# has been required.
#
# //= include "header"
#
def process_include_directive(path)
pathname = context.resolve(path)
context.depend_on_asset(pathname)
included_pathnames << pathname
end
# `require_directory` requires all the files inside a single
# directory. It's similar to `path/*` since it does not follow
# nested directories.
#
# //= require_directory "./javascripts"
#
def process_require_directory_directive(path = ".")
if relative?(path)
root = pathname.dirname.join(path).expand_path
unless (stats = stat(root)) && stats.directory?
raise ArgumentError, "require_directory argument must be a directory"
end
context.depend_on(root)
entries(root).each do |pathname|
pathname = root.join(pathname)
if pathname.to_s == self.file
next
elsif context.asset_requirable?(pathname)
context.require_asset(pathname)
end
end
else
# The path must be relative and start with a `./`.
raise ArgumentError, "require_directory argument must be a relative path"
end
end
# `require_tree` requires all the nested files in a directory.
# Its glob equivalent is `path/**/*`.
#
# //= require_tree "./public"
#
def process_require_tree_directive(path = ".")
if relative?(path)
root = pathname.dirname.join(path).expand_path
unless (stats = stat(root)) && stats.directory?
raise ArgumentError, "require_tree argument must be a directory"
end
context.depend_on(root)
each_entry(root) do |pathname|
if pathname.to_s == self.file
next
elsif stat(pathname).directory?
context.depend_on(pathname)
elsif context.asset_requirable?(pathname)
context.require_asset(pathname)
end
end
else
# The path must be relative and start with a `./`.
raise ArgumentError, "require_tree argument must be a relative path"
end
end
# Allows you to state a dependency on a file without
# including it.
#
# This is used for caching purposes. Any changes made to
# the dependency file will invalidate the cache of the
# source file.
#
# This is useful if you are using ERB and File.read to pull
# in contents from another file.
#
# //= depend_on "foo.png"
#
def process_depend_on_directive(path)
context.depend_on(path)
end
# Allows you to state a dependency on an asset without including
# it.
#
# This is used for caching purposes. Any changes that would
# invalid the asset dependency will invalidate the cache our the
# source file.
#
# Unlike `depend_on`, the path must be a requirable asset.
#
# //= depend_on_asset "bar.js"
#
def process_depend_on_asset_directive(path)
context.depend_on_asset(path)
end
# Allows dependency to be excluded from the asset bundle.
#
# The `path` must be a valid asset and may or may not already
# be part of the bundle. Once stubbed, it is blacklisted and
# can't be brought back by any other `require`.
#
# //= stub "jquery"
#
def process_stub_directive(path)
context.stub_asset(path)
end
# Enable Sprockets 1.x compat mode.
#
# Makes it possible to use the same JavaScript source
# file in both Sprockets 1 and 2.
#
# //= compat
#
def process_compat_directive
@compat = true
end
# Checks if Sprockets 1.x compat mode enabled
def compat?
@compat
end
# Sprockets 1.x allowed for constant interpolation if a
# constants.yml was present. This is only available if
# compat mode is on.
def constants
if compat?
pathname = Pathname.new(context.root_path).join("constants.yml")
stat(pathname) ? YAML.load_file(pathname) : {}
else
{}
end
end
# `provide` is stubbed out for Sprockets 1.x compat.
# Mutating the path when an asset is being built is
# not permitted.
def process_provide_directive(path)
end
private
def relative?(path)
path =~ /^\.($|\.?\/)/
end
def stat(path)
context.environment.stat(path)
end
def entries(path)
context.environment.entries(path)
end
def each_entry(root, &block)
context.environment.each_entry(root, &block)
end
end
end
sprockets-2.10.1/lib/sprockets/utils.rb 0000644 0000041 0000041 00000004675 12260352334 020124 0 ustar www-data www-data module Sprockets
# `Utils`, we didn't know where else to put it!
module Utils
# If theres encoding support (aka Ruby 1.9)
if "".respond_to?(:valid_encoding?)
# Define UTF-8 BOM pattern matcher.
# Avoid using a Regexp literal because it inheirts the files
# encoding and we want to avoid syntax errors in other interpreters.
UTF8_BOM_PATTERN = Regexp.new("\\A\uFEFF".encode('utf-8'))
def self.read_unicode(pathname, external_encoding = Encoding.default_external)
pathname.open("r:#{external_encoding}") do |f|
f.read.tap do |data|
# Eager validate the file's encoding. In most cases we
# expect it to be UTF-8 unless `default_external` is set to
# something else. An error is usually raised if the file is
# saved as UTF-16 when we expected UTF-8.
if !data.valid_encoding?
raise EncodingError, "#{pathname} has a invalid " +
"#{data.encoding} byte sequence"
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
elsif data.encoding.name == "UTF-8" && data =~ UTF8_BOM_PATTERN
data.sub!(UTF8_BOM_PATTERN, "")
end
end
end
end
else
# Define UTF-8 and UTF-16 BOM pattern matchers.
# Avoid using a Regexp literal to prevent syntax errors in other interpreters.
UTF8_BOM_PATTERN = Regexp.new("\\A\\xEF\\xBB\\xBF")
UTF16_BOM_PATTERN = Regexp.new("\\A(\\xFE\\xFF|\\xFF\\xFE)")
def self.read_unicode(pathname)
pathname.read.tap do |data|
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
if data =~ UTF8_BOM_PATTERN
data.sub!(UTF8_BOM_PATTERN, "")
# If we find a UTF-16 BOM, theres nothing we can do on
# 1.8. Only UTF-8 is supported.
elsif data =~ UTF16_BOM_PATTERN
raise EncodingError, "#{pathname} has a UTF-16 BOM. " +
"Resave the file as UTF-8 or upgrade to Ruby 1.9."
end
end
end
end
# Prepends a leading "." to an extension if its missing.
#
# normalize_extension("js")
# # => ".js"
#
# normalize_extension(".css")
# # => ".css"
#
def self.normalize_extension(extension)
extension = extension.to_s
if extension[/^\./]
extension
else
".#{extension}"
end
end
end
end
sprockets-2.10.1/lib/sprockets/sass_compressor.rb 0000644 0000041 0000041 00000000771 12260352334 022202 0 ustar www-data www-data require 'tilt'
module Sprockets
class SassCompressor < Tilt::Template
self.default_mime_type = 'text/css'
def self.engine_initialized?
defined?(::Sass::Engine)
end
def initialize_engine
require_template_library 'sass'
end
def prepare
end
def evaluate(context, locals, &block)
::Sass::Engine.new(data, {
:syntax => :scss,
:cache => false,
:read_cache => false,
:style => :compressed
}).render
end
end
end
sprockets-2.10.1/lib/sprockets/manifest.rb 0000644 0000041 0000041 00000016774 12260352334 020575 0 ustar www-data www-data require 'multi_json'
require 'securerandom'
require 'time'
module Sprockets
# The Manifest logs the contents of assets compiled to a single
# directory. It records basic attributes about the asset for fast
# lookup without having to compile. A pointer from each logical path
# indicates with fingerprinted asset is the current one.
#
# The JSON is part of the public API and should be considered
# stable. This should make it easy to read from other programming
# languages and processes that don't have sprockets loaded. See
# `#assets` and `#files` for more infomation about the structure.
class Manifest
attr_reader :environment, :path, :dir
# Create new Manifest associated with an `environment`. `path` is
# a full path to the manifest json file. The file may or may not
# already exist. The dirname of the `path` will be used to write
# compiled assets to. Otherwise, if the path is a directory, the
# filename will default a random "manifest-123.json" file in that
# directory.
#
# Manifest.new(environment, "./public/assets/manifest.json")
#
def initialize(*args)
if args.first.is_a?(Base) || args.first.nil?
@environment = args.shift
end
@dir, @path = args[0], args[1]
# Expand paths
@dir = File.expand_path(@dir) if @dir
@path = File.expand_path(@path) if @path
# If path is given as the second arg
if @dir && File.extname(@dir) != ""
@dir, @path = nil, @dir
end
# Default dir to the directory of the path
@dir ||= File.dirname(@path) if @path
# If directory is given w/o path, pick a random manifest.json location
if @dir && @path.nil?
# Find the first manifest.json in the directory
paths = Dir[File.join(@dir, "manifest*.json")]
if paths.any?
@path = paths.first
else
@path = File.join(@dir, "manifest-#{SecureRandom.hex(16)}.json")
end
end
unless @dir && @path
raise ArgumentError, "manifest requires output path"
end
data = nil
begin
if File.exist?(@path)
data = json_decode(File.read(@path))
end
rescue MultiJson::DecodeError => e
logger.error "#{@path} is invalid: #{e.class} #{e.message}"
end
@data = data.is_a?(Hash) ? data : {}
end
# Returns internal assets mapping. Keys are logical paths which
# map to the latest fingerprinted filename.
#
# Logical path (String): Fingerprint path (String)
#
# { "application.js" => "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js",
# "jquery.js" => "jquery-ae0908555a245f8266f77df5a8edca2e.js" }
#
def assets
@data['assets'] ||= {}
end
# Returns internal file directory listing. Keys are filenames
# which map to an attributes array.
#
# Fingerprint path (String):
# logical_path: Logical path (String)
# mtime: ISO8601 mtime (String)
# digest: Base64 hex digest (String)
#
# { "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js" =>
# { 'logical_path' => "application.js",
# 'mtime' => "2011-12-13T21:47:08-06:00",
# 'digest' => "2e8e9a7c6b0aafa0c9bdeec90ea30213" } }
#
def files
@data['files'] ||= {}
end
# Compile and write asset to directory. The asset is written to a
# fingerprinted filename like
# `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is
# also inserted into the manifest file.
#
# compile("application.js")
#
def compile(*args)
unless environment
raise Error, "manifest requires environment for compilation"
end
paths = environment.each_logical_path(*args).to_a +
args.flatten.select { |fn| Pathname.new(fn).absolute? if fn.is_a?(String)}
paths.each do |path|
if asset = find_asset(path)
files[asset.digest_path] = {
'logical_path' => asset.logical_path,
'mtime' => asset.mtime.iso8601,
'size' => asset.bytesize,
'digest' => asset.digest
}
assets[asset.logical_path] = asset.digest_path
target = File.join(dir, asset.digest_path)
if File.exist?(target)
logger.debug "Skipping #{target}, already exists"
else
logger.info "Writing #{target}"
asset.write_to target
asset.write_to "#{target}.gz" if asset.is_a?(BundledAsset)
end
end
end
save
paths
end
# Removes file from directory and from manifest. `filename` must
# be the name with any directory path.
#
# manifest.remove("application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js")
#
def remove(filename)
path = File.join(dir, filename)
gzip = "#{path}.gz"
logical_path = files[filename]['logical_path']
if assets[logical_path] == filename
assets.delete(logical_path)
end
files.delete(filename)
FileUtils.rm(path) if File.exist?(path)
FileUtils.rm(gzip) if File.exist?(gzip)
save
logger.info "Removed #{filename}"
nil
end
# Cleanup old assets in the compile directory. By default it will
# keep the latest version plus 2 backups.
def clean(keep = 2)
self.assets.keys.each do |logical_path|
# Get assets sorted by ctime, newest first
assets = backups_for(logical_path)
# Keep the last N backups
assets = assets[keep..-1] || []
# Remove old assets
assets.each { |path, _| remove(path) }
end
end
# Wipe directive
def clobber
FileUtils.rm_r(@dir) if File.exist?(@dir)
logger.info "Removed #{@dir}"
nil
end
protected
# Finds all the backup assets for a logical path. The latest
# version is always excluded. The return array is sorted by the
# assets mtime in descending order (Newest to oldest).
def backups_for(logical_path)
files.select { |filename, attrs|
# Matching logical paths
attrs['logical_path'] == logical_path &&
# Excluding whatever asset is the current
assets[logical_path] != filename
}.sort_by { |filename, attrs|
# Sort by timestamp
Time.parse(attrs['mtime'])
}.reverse
end
# Basic wrapper around Environment#find_asset. Logs compile time.
def find_asset(logical_path)
asset = nil
ms = benchmark do
asset = environment.find_asset(logical_path)
end
logger.debug "Compiled #{logical_path} (#{ms}ms)"
asset
end
# Persist manfiest back to FS
def save
FileUtils.mkdir_p dir
File.open(path, 'w') do |f|
f.write json_encode(@data)
end
end
private
# Feature detect newer MultiJson API
if MultiJson.respond_to?(:dump)
def json_decode(obj)
MultiJson.load(obj)
end
def json_encode(obj)
MultiJson.dump(obj)
end
else
def json_decode(obj)
MultiJson.decode(obj)
end
def json_encode(obj)
MultiJson.encode(obj)
end
end
def logger
if environment
environment.logger
else
logger = Logger.new($stderr)
logger.level = Logger::FATAL
logger
end
end
def benchmark
start_time = Time.now.to_f
yield
((Time.now.to_f - start_time) * 1000).to_i
end
end
end
sprockets-2.10.1/lib/sprockets/index.rb 0000644 0000041 0000041 00000005636 12260352334 020071 0 ustar www-data www-data require 'sprockets/base'
module Sprockets
# `Index` is a special cached version of `Environment`.
#
# The expection is that all of its file system methods are cached
# for the instances lifetime. This makes `Index` much faster. This
# behavior is ideal in production environments where the file system
# is immutable.
#
# `Index` should not be initialized directly. Instead use
# `Environment#index`.
class Index < Base
def initialize(environment)
@environment = environment
if environment.respond_to?(:default_external_encoding)
@default_external_encoding = environment.default_external_encoding
end
# Copy environment attributes
@logger = environment.logger
@context_class = environment.context_class
@cache = environment.cache
@trail = environment.trail.index
@digest = environment.digest
@digest_class = environment.digest_class
@version = environment.version
@mime_types = environment.mime_types
@engines = environment.engines
@preprocessors = environment.preprocessors
@postprocessors = environment.postprocessors
@bundle_processors = environment.bundle_processors
@compressors = environment.compressors
# Initialize caches
@assets = {}
@digests = {}
end
# No-op return self as index
def index
self
end
# Cache calls to `file_digest`
def file_digest(pathname)
key = pathname.to_s
if @digests.key?(key)
@digests[key]
else
@digests[key] = super
end
end
# Cache `find_asset` calls
def find_asset(path, options = {})
options[:bundle] = true unless options.key?(:bundle)
if asset = @assets[cache_key_for(path, options)]
asset
elsif asset = super
logical_path_cache_key = cache_key_for(path, options)
full_path_cache_key = cache_key_for(asset.pathname, options)
# Cache on Index
@assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
# Push cache upstream to Environment
@environment.instance_eval do
@assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
end
asset
end
end
protected
# Index is immutable, any methods that try to clear the cache
# should bomb.
def expire_index!
raise TypeError, "can't modify immutable index"
end
# Cache asset building in memory and in persisted cache.
def build_asset(path, pathname, options)
# Memory cache
key = cache_key_for(pathname, options)
if @assets.key?(key)
@assets[key]
else
@assets[key] = begin
# Persisted cache
cache_asset(key) do
super
end
end
end
end
end
end
sprockets-2.10.1/lib/sprockets/closure_compressor.rb 0000644 0000041 0000041 00000000645 12260352334 022705 0 ustar www-data www-data require 'tilt'
module Sprockets
class ClosureCompressor < Tilt::Template
self.default_mime_type = 'application/javascript'
def self.engine_initialized?
defined?(::Closure::Compiler)
end
def initialize_engine
require_template_library 'closure-compiler'
end
def prepare
end
def evaluate(context, locals, &block)
Closure::Compiler.new.compile(data)
end
end
end
sprockets-2.10.1/lib/sprockets/errors.rb 0000644 0000041 0000041 00000001152 12260352334 020263 0 ustar www-data www-data # Define some basic Sprockets error classes
module Sprockets
class Error < StandardError; end
class ArgumentError < Error; end
class CircularDependencyError < Error; end
class ContentTypeMismatch < Error; end
class EncodingError < Error; end
class FileNotFound < Error; end
class FileOutsidePaths < Error; end
class NotImplementedError < Error; end
class UnserializeError < Error; end
module EngineError
attr_accessor :sprockets_annotation
def message
[super, sprockets_annotation].compact.join("\n")
end
end
end
sprockets-2.10.1/lib/sprockets/context.rb 0000644 0000041 0000041 00000021102 12260352334 020430 0 ustar www-data www-data require 'base64'
require 'rack/utils'
require 'sprockets/errors'
require 'sprockets/utils'
require 'pathname'
require 'set'
module Sprockets
# `Context` provides helper methods to all `Tilt` processors. They
# are typically accessed by ERB templates. You can mix in custom
# helpers by injecting them into `Environment#context_class`. Do not
# mix them into `Context` directly.
#
# environment.context_class.class_eval do
# include MyHelper
# def asset_url; end
# end
#
# <%= asset_url "foo.png" %>
#
# The `Context` also collects dependencies declared by
# assets. See `DirectiveProcessor` for an example of this.
class Context
attr_reader :environment, :pathname
attr_reader :_required_paths, :_stubbed_assets
attr_reader :_dependency_paths, :_dependency_assets
attr_writer :__LINE__
def initialize(environment, logical_path, pathname)
@environment = environment
@logical_path = logical_path
@pathname = pathname
@__LINE__ = nil
@_required_paths = []
@_stubbed_assets = Set.new
@_dependency_paths = Set.new
@_dependency_assets = Set.new([pathname.to_s])
end
# Returns the environment path that contains the file.
#
# If `app/javascripts` and `app/stylesheets` are in your path, and
# current file is `app/javascripts/foo/bar.js`, `root_path` would
# return `app/javascripts`.
def root_path
environment.paths.detect { |path| pathname.to_s[path] }
end
# Returns logical path without any file extensions.
#
# 'app/javascripts/application.js'
# # => 'application'
#
def logical_path
@logical_path.chomp(File.extname(@logical_path))
end
# Returns content type of file
#
# 'application/javascript'
# 'text/css'
#
def content_type
environment.content_type_of(pathname)
end
# Given a logical path, `resolve` will find and return the fully
# expanded path. Relative paths will also be resolved. An optional
# `:content_type` restriction can be supplied to restrict the
# search.
#
# resolve("foo.js")
# # => "/path/to/app/javascripts/foo.js"
#
# resolve("./bar.js")
# # => "/path/to/app/javascripts/bar.js"
#
def resolve(path, options = {}, &block)
pathname = Pathname.new(path)
attributes = environment.attributes_for(pathname)
if pathname.absolute?
if environment.stat(pathname)
pathname
else
raise FileNotFound, "couldn't find file '#{pathname}'"
end
elsif content_type = options[:content_type]
content_type = self.content_type if content_type == :self
if attributes.format_extension
if content_type != attributes.content_type
raise ContentTypeMismatch, "#{path} is " +
"'#{attributes.content_type}', not '#{content_type}'"
end
end
resolve(path) do |candidate|
if self.content_type == environment.content_type_of(candidate)
return candidate
end
end
raise FileNotFound, "couldn't find file '#{path}'"
else
environment.resolve(path, {:base_path => self.pathname.dirname}.merge(options), &block)
end
end
# `depend_on` allows you to state a dependency on a file without
# including it.
#
# This is used for caching purposes. Any changes made to
# the dependency file with invalidate the cache of the
# source file.
def depend_on(path)
@_dependency_paths << resolve(path).to_s
nil
end
# `depend_on_asset` allows you to state an asset dependency
# without including it.
#
# This is used for caching purposes. Any changes that would
# invalidate the dependency asset will invalidate the source
# file. Unlike `depend_on`, this will include recursively include
# the target asset's dependencies.
def depend_on_asset(path)
filename = resolve(path).to_s
@_dependency_assets << filename
nil
end
# `require_asset` declares `path` as a dependency of the file. The
# dependency will be inserted before the file and will only be
# included once.
#
# If ERB processing is enabled, you can use it to dynamically
# require assets.
#
# <%= require_asset "#{framework}.js" %>
#
def require_asset(path)
pathname = resolve(path, :content_type => :self)
depend_on_asset(pathname)
@_required_paths << pathname.to_s
nil
end
# `stub_asset` blacklists `path` from being included in the bundle.
# `path` must be an asset which may or may not already be included
# in the bundle.
def stub_asset(path)
@_stubbed_assets << resolve(path, :content_type => :self).to_s
nil
end
# Tests if target path is able to be safely required into the
# current concatenation.
def asset_requirable?(path)
pathname = resolve(path)
content_type = environment.content_type_of(pathname)
stat = environment.stat(path)
return false unless stat && stat.file?
self.content_type.nil? || self.content_type == content_type
end
# Reads `path` and runs processors on the file.
#
# This allows you to capture the result of an asset and include it
# directly in another.
#
# <%= evaluate "bar.js" %>
#
def evaluate(path, options = {})
pathname = resolve(path)
attributes = environment.attributes_for(pathname)
processors = options[:processors] || attributes.processors
if options[:data]
result = options[:data]
else
if environment.respond_to?(:default_external_encoding)
mime_type = environment.mime_types(pathname.extname)
encoding = environment.encoding_for_mime_type(mime_type)
result = Sprockets::Utils.read_unicode(pathname, encoding)
else
result = Sprockets::Utils.read_unicode(pathname)
end
end
processors.each do |processor|
begin
template = processor.new(pathname.to_s) { result }
result = template.render(self, {})
rescue Exception => e
annotate_exception! e
raise
end
end
result
end
# Returns a Base64-encoded `data:` URI with the contents of the
# asset at the specified path, and marks that path as a dependency
# of the current file.
#
# Use `asset_data_uri` from ERB with CSS or JavaScript assets:
#
# #logo { background: url(<%= asset_data_uri 'logo.png' %>) }
#
# $('').attr('src', '<%= asset_data_uri 'avatar.jpg' %>')
#
def asset_data_uri(path)
depend_on_asset(path)
asset = environment.find_asset(path)
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
end
# Expands logical path to full url to asset.
#
# NOTE: This helper is currently not implemented and should be
# customized by the application. Though, in the future, some
# basics implemention may be provided with different methods that
# are required to be overridden.
def asset_path(path, options = {})
message = <<-EOS
Custom asset_path helper is not implemented
Extend your environment context with a custom method.
environment.context_class.class_eval do
def asset_path(path, options = {})
end
end
EOS
raise NotImplementedError, message
end
# Expand logical image asset path.
def image_path(path)
asset_path(path, :type => :image)
end
# Expand logical video asset path.
def video_path(path)
asset_path(path, :type => :video)
end
# Expand logical audio asset path.
def audio_path(path)
asset_path(path, :type => :audio)
end
# Expand logical font asset path.
def font_path(path)
asset_path(path, :type => :font)
end
# Expand logical javascript asset path.
def javascript_path(path)
asset_path(path, :type => :javascript)
end
# Expand logical stylesheet asset path.
def stylesheet_path(path)
asset_path(path, :type => :stylesheet)
end
private
# Annotates exception backtrace with the original template that
# the exception was raised in.
def annotate_exception!(exception)
location = pathname.to_s
location << ":#{@__LINE__}" if @__LINE__
exception.extend(Sprockets::EngineError)
exception.sprockets_annotation = " (in #{location})"
end
def logger
environment.logger
end
end
end
sprockets-2.10.1/lib/sprockets/charset_normalizer.rb 0000644 0000041 0000041 00000002326 12260352334 022646 0 ustar www-data www-data require 'tilt'
module Sprockets
# Some browsers have issues with stylesheets that contain multiple
# `@charset` definitions. The issue surfaces while using Sass since
# it inserts a `@charset` at the top of each file. Then Sprockets
# concatenates them together.
#
# The `CharsetNormalizer` processor strips out multiple `@charset`
# definitions.
#
# The current implementation is naive. It picks the first `@charset`
# it sees and strips the others. This works for most people because
# the other definitions are usually `UTF-8`. A more sophisticated
# approach would be to re-encode stylesheets with mixed encodings.
#
# This behavior can be disabled with:
#
# environment.unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
#
class CharsetNormalizer < Tilt::Template
def prepare
end
def evaluate(context, locals, &block)
charset = nil
# Find and strip out any `@charset` definitions
filtered_data = data.gsub(/^@charset "([^"]+)";$/) {
charset ||= $1; ""
}
if charset
# If there was a charset, move it to the top
"@charset \"#{charset}\";#{filtered_data}"
else
data
end
end
end
end
sprockets-2.10.1/lib/sprockets/eco_template.rb 0000644 0000041 0000041 00000001643 12260352334 021415 0 ustar www-data www-data require 'tilt'
module Sprockets
# Tilt engine class for the Eco compiler. Depends on the `eco` gem.
#
# For more infomation see:
#
# https://github.com/sstephenson/ruby-eco
# https://github.com/sstephenson/eco
#
class EcoTemplate < Tilt::Template
# Check to see if Eco is loaded
def self.engine_initialized?
defined? ::Eco
end
# Autoload eco library. If the library isn't loaded, Tilt will produce
# a thread safetly warning. If you intend to use `.eco` files, you
# should explicitly require it.
def initialize_engine
require_template_library 'eco'
end
def prepare
end
# Compile template data with Eco compiler.
#
# Returns a JS function definition String. The result should be
# assigned to a JS variable.
#
# # => "function(...) {...}"
#
def evaluate(scope, locals, &block)
Eco.compile(data)
end
end
end
sprockets-2.10.1/lib/sprockets/sass_importer.rb 0000644 0000041 0000041 00000001266 12260352334 021647 0 ustar www-data www-data require 'sass'
module Sprockets
# This custom importer adds sprockets dependency tracking on to Sass
# `@import` statements. This makes the Sprockets and Sass caching
# systems work together.
class SassImporter < Sass::Importers::Filesystem
def initialize(context, root)
@context = context
super root.to_s
end
def find_relative(*args)
engine = super
if engine && (filename = engine.options[:filename])
@context.depend_on(filename)
end
engine
end
def find(*args)
engine = super
if engine && (filename = engine.options[:filename])
@context.depend_on(filename)
end
engine
end
end
end
sprockets-2.10.1/lib/sprockets/processed_asset.rb 0000644 0000041 0000041 00000011225 12260352334 022137 0 ustar www-data www-data require 'sprockets/asset'
require 'sprockets/utils'
module Sprockets
class ProcessedAsset < Asset
def initialize(environment, logical_path, pathname)
super
start_time = Time.now.to_f
context = environment.context_class.new(environment, logical_path, pathname)
@source = context.evaluate(pathname)
@length = Rack::Utils.bytesize(source)
@digest = environment.digest.update(source).hexdigest
build_required_assets(environment, context)
build_dependency_paths(environment, context)
@dependency_digest = compute_dependency_digest(environment)
elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
environment.logger.debug "Compiled #{logical_path} (#{elapsed_time}ms) (pid #{Process.pid})"
end
# Interal: Used to check equality
attr_reader :dependency_digest
attr_reader :source
# Initialize `BundledAsset` from serialized `Hash`.
def init_with(environment, coder)
super
@source = coder['source']
@dependency_digest = coder['dependency_digest']
@required_assets = coder['required_paths'].map { |p|
p = expand_root_path(p)
unless environment.paths.detect { |path| p[path] }
raise UnserializeError, "#{p} isn't in paths"
end
p == pathname.to_s ? self : environment.find_asset(p, :bundle => false)
}
@dependency_paths = coder['dependency_paths'].map { |h|
DependencyFile.new(expand_root_path(h['path']), h['mtime'], h['digest'])
}
end
# Serialize custom attributes in `BundledAsset`.
def encode_with(coder)
super
coder['source'] = source
coder['dependency_digest'] = dependency_digest
coder['required_paths'] = required_assets.map { |a|
relativize_root_path(a.pathname).to_s
}
coder['dependency_paths'] = dependency_paths.map { |d|
{ 'path' => relativize_root_path(d.pathname).to_s,
'mtime' => d.mtime.iso8601,
'digest' => d.digest }
}
end
# Checks if Asset is stale by comparing the actual mtime and
# digest to the inmemory model.
def fresh?(environment)
# Check freshness of all declared dependencies
@dependency_paths.all? { |dep| dependency_fresh?(environment, dep) }
end
protected
class DependencyFile < Struct.new(:pathname, :mtime, :digest)
def initialize(pathname, mtime, digest)
pathname = Pathname.new(pathname) unless pathname.is_a?(Pathname)
mtime = Time.parse(mtime) if mtime.is_a?(String)
super
end
def eql?(other)
other.is_a?(DependencyFile) &&
pathname.eql?(other.pathname) &&
mtime.eql?(other.mtime) &&
digest.eql?(other.digest)
end
def hash
pathname.to_s.hash
end
end
private
def build_required_assets(environment, context)
@required_assets = resolve_dependencies(environment, context._required_paths + [pathname.to_s]) -
resolve_dependencies(environment, context._stubbed_assets.to_a)
end
def resolve_dependencies(environment, paths)
assets = []
cache = {}
paths.each do |path|
if path == self.pathname.to_s
unless cache[self]
cache[self] = true
assets << self
end
elsif asset = environment.find_asset(path, :bundle => false)
asset.required_assets.each do |asset_dependency|
unless cache[asset_dependency]
cache[asset_dependency] = true
assets << asset_dependency
end
end
end
end
assets
end
def build_dependency_paths(environment, context)
dependency_paths = {}
context._dependency_paths.each do |path|
dep = DependencyFile.new(path, environment.stat(path).mtime, environment.file_digest(path).hexdigest)
dependency_paths[dep] = true
end
context._dependency_assets.each do |path|
if path == self.pathname.to_s
dep = DependencyFile.new(pathname, environment.stat(path).mtime, environment.file_digest(path).hexdigest)
dependency_paths[dep] = true
elsif asset = environment.find_asset(path, :bundle => false)
asset.dependency_paths.each do |d|
dependency_paths[d] = true
end
end
end
@dependency_paths = dependency_paths.keys
end
def compute_dependency_digest(environment)
required_assets.inject(environment.digest) { |digest, asset|
digest.update asset.digest
}.hexdigest
end
end
end
sprockets-2.10.1/lib/sprockets/processor.rb 0000644 0000041 0000041 00000001151 12260352334 020765 0 ustar www-data www-data require 'tilt'
module Sprockets
# `Processor` creates an anonymous processor class from a block.
#
# register_preprocessor 'text/css', :my_processor do |context, data|
# # ...
# end
#
class Processor < Tilt::Template
# `processor` is a lambda or block
def self.processor
@processor
end
def self.name
"Sprockets::Processor (#{@name})"
end
def self.to_s
name
end
def prepare
end
# Call processor block with `context` and `data`.
def evaluate(context, locals)
self.class.processor.call(context, data)
end
end
end
sprockets-2.10.1/lib/sprockets/mime.rb 0000644 0000041 0000041 00000002566 12260352334 017710 0 ustar www-data www-data require 'rack/mime'
module Sprockets
module Mime
# Returns a `Hash` of registered mime types registered on the
# environment and those part of `Rack::Mime`.
#
# If an `ext` is given, it will lookup the mime type for that extension.
def mime_types(ext = nil)
if ext.nil?
Rack::Mime::MIME_TYPES.merge(@mime_types)
else
ext = Sprockets::Utils.normalize_extension(ext)
@mime_types[ext] || Rack::Mime::MIME_TYPES[ext]
end
end
# Returns a `Hash` of explicitly registered mime types.
def registered_mime_types
@mime_types.dup
end
if {}.respond_to?(:key)
def extension_for_mime_type(type)
mime_types.key(type)
end
else
def extension_for_mime_type(type)
mime_types.index(type)
end
end
# Register a new mime type.
def register_mime_type(mime_type, ext)
ext = Sprockets::Utils.normalize_extension(ext)
@mime_types[ext] = mime_type
end
if defined? Encoding
# Returns the correct encoding for a given mime type, while falling
# back on the default external encoding, if it exists.
def encoding_for_mime_type(type)
encoding = Encoding::BINARY if type =~ %r{^(image|audio|video)/}
encoding ||= default_external_encoding if respond_to?(:default_external_encoding)
encoding
end
end
end
end
sprockets-2.10.1/lib/sprockets/jst_processor.rb 0000644 0000041 0000041 00000001112 12260352334 021642 0 ustar www-data www-data require 'tilt'
module Sprockets
class JstProcessor < Tilt::Template
self.default_mime_type = 'application/javascript'
def self.default_namespace
'this.JST'
end
def prepare
@namespace = self.class.default_namespace
end
attr_reader :namespace
def evaluate(scope, locals, &block)
<<-JST
(function() { #{namespace} || (#{namespace} = {}); #{namespace}[#{scope.logical_path.inspect}] = #{indent(data)};
}).call(this);
JST
end
private
def indent(string)
string.gsub(/$(.)/m, "\\1 ").strip
end
end
end
sprockets-2.10.1/lib/sprockets/processing.rb 0000644 0000041 0000041 00000014020 12260352334 021121 0 ustar www-data www-data require 'sprockets/engines'
require 'sprockets/mime'
require 'sprockets/processor'
require 'sprockets/utils'
module Sprockets
# `Processing` is an internal mixin whose public methods are exposed on
# the `Environment` and `Index` classes.
module Processing
# Returns an `Array` of format extension `String`s.
#
# format_extensions
# # => ['.js', '.css']
#
def format_extensions
@trail.extensions - @engines.keys
end
# Deprecated alias for `preprocessors`.
def processors(*args)
preprocessors(*args)
end
# Returns an `Array` of `Processor` classes. If a `mime_type`
# argument is supplied, the processors registered under that
# extension will be returned.
#
# Preprocessors are ran before Postprocessors and Engine
# processors.
#
# All `Processor`s must follow the `Tilt::Template` interface. It is
# recommended to subclass `Tilt::Template`.
def preprocessors(mime_type = nil)
if mime_type
@preprocessors[mime_type].dup
else
deep_copy_hash(@preprocessors)
end
end
# Returns an `Array` of `Processor` classes. If a `mime_type`
# argument is supplied, the processors registered under that
# extension will be returned.
#
# Postprocessors are ran after Preprocessors and Engine processors.
#
# All `Processor`s must follow the `Tilt::Template` interface. It is
# recommended to subclass `Tilt::Template`.
def postprocessors(mime_type = nil)
if mime_type
@postprocessors[mime_type].dup
else
deep_copy_hash(@postprocessors)
end
end
# Deprecated alias for `register_preprocessor`.
def register_processor(*args, &block)
register_preprocessor(*args, &block)
end
# Registers a new Preprocessor `klass` for `mime_type`.
#
# register_preprocessor 'text/css', Sprockets::DirectiveProcessor
#
# A block can be passed for to create a shorthand processor.
#
# register_preprocessor 'text/css', :my_processor do |context, data|
# data.gsub(...)
# end
#
def register_preprocessor(mime_type, klass, &block)
if block_given?
name = klass.to_s
klass = Class.new(Processor) do
@name = name
@processor = block
end
end
@preprocessors[mime_type].push(klass)
end
# Registers a new Postprocessor `klass` for `mime_type`.
#
# register_postprocessor 'text/css', Sprockets::CharsetNormalizer
#
# A block can be passed for to create a shorthand processor.
#
# register_postprocessor 'text/css', :my_processor do |context, data|
# data.gsub(...)
# end
#
def register_postprocessor(mime_type, klass, &block)
if block_given?
name = klass.to_s
klass = Class.new(Processor) do
@name = name
@processor = block
end
end
@postprocessors[mime_type].push(klass)
end
# Deprecated alias for `unregister_preprocessor`.
def unregister_processor(*args)
unregister_preprocessor(*args)
end
# Remove Preprocessor `klass` for `mime_type`.
#
# unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
#
def unregister_preprocessor(mime_type, klass)
if klass.is_a?(String) || klass.is_a?(Symbol)
klass = @preprocessors[mime_type].detect { |cls|
cls.respond_to?(:name) &&
cls.name == "Sprockets::Processor (#{klass})"
}
end
@preprocessors[mime_type].delete(klass)
end
# Remove Postprocessor `klass` for `mime_type`.
#
# unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
#
def unregister_postprocessor(mime_type, klass)
if klass.is_a?(String) || klass.is_a?(Symbol)
klass = @postprocessors[mime_type].detect { |cls|
cls.respond_to?(:name) &&
cls.name == "Sprockets::Processor (#{klass})"
}
end
@postprocessors[mime_type].delete(klass)
end
# Returns an `Array` of `Processor` classes. If a `mime_type`
# argument is supplied, the processors registered under that
# extension will be returned.
#
# Bundle Processors are ran on concatenated assets rather than
# individual files.
#
# All `Processor`s must follow the `Tilt::Template` interface. It is
# recommended to subclass `Tilt::Template`.
def bundle_processors(mime_type = nil)
if mime_type
@bundle_processors[mime_type].dup
else
deep_copy_hash(@bundle_processors)
end
end
# Registers a new Bundle Processor `klass` for `mime_type`.
#
# register_bundle_processor 'text/css', Sprockets::CharsetNormalizer
#
# A block can be passed for to create a shorthand processor.
#
# register_bundle_processor 'text/css', :my_processor do |context, data|
# data.gsub(...)
# end
#
def register_bundle_processor(mime_type, klass, &block)
if block_given?
name = klass.to_s
klass = Class.new(Processor) do
@name = name
@processor = block
end
end
@bundle_processors[mime_type].push(klass)
end
# Remove Bundle Processor `klass` for `mime_type`.
#
# unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
#
def unregister_bundle_processor(mime_type, klass)
if klass.is_a?(String) || klass.is_a?(Symbol)
klass = @bundle_processors[mime_type].detect { |cls|
cls.respond_to?(:name) &&
cls.name == "Sprockets::Processor (#{klass})"
}
end
@bundle_processors[mime_type].delete(klass)
end
private
def add_engine_to_trail(ext, klass)
@trail.append_extension(ext.to_s)
if klass.respond_to?(:default_mime_type) && klass.default_mime_type
if format_ext = extension_for_mime_type(klass.default_mime_type)
@trail.alias_extension(ext.to_s, format_ext)
end
end
end
end
end
sprockets-2.10.1/lib/sprockets/paths.rb 0000644 0000041 0000041 00000002765 12260352334 020101 0 ustar www-data www-data module Sprockets
module Paths
# Returns `Environment` root.
#
# All relative paths are expanded with root as its base. To be
# useful set this to your applications root directory. (`Rails.root`)
def root
@trail.root.dup
end
# Returns an `Array` of path `String`s.
#
# These paths will be used for asset logical path lookups.
#
# Note that a copy of the `Array` is returned so mutating will
# have no affect on the environment. See `append_path`,
# `prepend_path`, and `clear_paths`.
def paths
@trail.paths.dup
end
# Prepend a `path` to the `paths` list.
#
# Paths at the end of the `Array` have the least priority.
def prepend_path(path)
@trail.prepend_path(path)
end
# Append a `path` to the `paths` list.
#
# Paths at the beginning of the `Array` have a higher priority.
def append_path(path)
@trail.append_path(path)
end
# Clear all paths and start fresh.
#
# There is no mechanism for reordering paths, so its best to
# completely wipe the paths list and reappend them in the order
# you want.
def clear_paths
@trail.paths.dup.each { |path| @trail.remove_path(path) }
end
# Returns an `Array` of extensions.
#
# These extensions maybe omitted from logical path searches.
#
# # => [".js", ".css", ".coffee", ".sass", ...]
#
def extensions
@trail.extensions.dup
end
protected
attr_reader :trail
end
end
sprockets-2.10.1/lib/sprockets/safety_colons.rb 0000644 0000041 0000041 00000001355 12260352334 021624 0 ustar www-data www-data require 'tilt'
module Sprockets
# For JS developers who are colonfobic, concatenating JS files using
# the module pattern usually leads to syntax errors.
#
# The `SafetyColons` processor will insert missing semicolons to the
# end of the file.
#
# This behavior can be disabled with:
#
# environment.unregister_postprocessor 'application/javascript', Sprockets::SafetyColons
#
class SafetyColons < Tilt::Template
def prepare
end
def evaluate(context, locals, &block)
# If the file is blank or ends in a semicolon, leave it as is
if data =~ /\A\s*\Z/m || data =~ /;\s*\Z/m
data
else
# Otherwise, append a semicolon and newline
"#{data};\n"
end
end
end
end
sprockets-2.10.1/lib/sprockets/version.rb 0000644 0000041 0000041 00000000052 12260352334 020432 0 ustar www-data www-data module Sprockets
VERSION = "2.10.1"
end
sprockets-2.10.1/lib/sprockets/compressing.rb 0000644 0000041 0000041 00000004346 12260352334 021310 0 ustar www-data www-data module Sprockets
# `Compressing` is an internal mixin whose public methods are exposed on
# the `Environment` and `Index` classes.
module Compressing
def compressors
deep_copy_hash(@compressors)
end
def register_compressor(mime_type, sym, klass)
@compressors[mime_type][sym] = klass
end
# Return CSS compressor or nil if none is set
def css_compressor
@css_compressor if defined? @css_compressor
end
# Assign a compressor to run on `text/css` assets.
#
# The compressor object must respond to `compress`.
def css_compressor=(compressor)
unregister_bundle_processor 'text/css', css_compressor if css_compressor
@css_compressor = nil
return unless compressor
if compressor.is_a?(Symbol)
compressor = compressors['text/css'][compressor] || raise(Error, "unknown compressor: #{compressor}")
end
if compressor.respond_to?(:compress)
klass = Class.new(Processor) do
@name = "css_compressor"
@processor = proc { |context, data| compressor.compress(data) }
end
@css_compressor = :css_compressor
else
@css_compressor = klass = compressor
end
register_bundle_processor 'text/css', klass
end
# Return JS compressor or nil if none is set
def js_compressor
@js_compressor if defined? @js_compressor
end
# Assign a compressor to run on `application/javascript` assets.
#
# The compressor object must respond to `compress`.
def js_compressor=(compressor)
unregister_bundle_processor 'application/javascript', js_compressor if js_compressor
@js_compressor = nil
return unless compressor
if compressor.is_a?(Symbol)
compressor = compressors['application/javascript'][compressor] || raise(Error, "unknown compressor: #{compressor}")
end
if compressor.respond_to?(:compress)
klass = Class.new(Processor) do
@name = "js_compressor"
@processor = proc { |context, data| compressor.compress(data) }
end
@js_compressor = :js_compressor
else
@js_compressor = klass = compressor
end
register_bundle_processor 'application/javascript', klass
end
end
end
sprockets-2.10.1/lib/sprockets/scss_template.rb 0000644 0000041 0000041 00000000435 12260352334 021620 0 ustar www-data www-data require 'sprockets/sass_template'
module Sprockets
# Scss handler to replace Tilt's builtin one. See `SassTemplate` and
# `SassImporter` for more infomation.
class ScssTemplate < SassTemplate
self.default_mime_type = 'text/css'
def syntax
:scss
end
end
end
sprockets-2.10.1/lib/sprockets/uglifier_compressor.rb 0000644 0000041 0000041 00000001175 12260352334 023036 0 ustar www-data www-data require 'tilt'
module Sprockets
class UglifierCompressor < Tilt::Template
self.default_mime_type = 'application/javascript'
def self.engine_initialized?
defined?(::Uglifier)
end
def initialize_engine
require_template_library 'uglifier'
end
def prepare
end
def evaluate(context, locals, &block)
# Feature detect Uglifier 2.0 option support
if Uglifier::DEFAULTS[:copyright]
# Uglifier < 2.x
Uglifier.new(:copyright => false).compile(data)
else
# Uglifier >= 2.x
Uglifier.new(:comments => :none).compile(data)
end
end
end
end
sprockets-2.10.1/lib/sprockets/sass_template.rb 0000644 0000041 0000041 00000003364 12260352334 021622 0 ustar www-data www-data require 'tilt'
module Sprockets
# This custom Tilt handler replaces the one built into Tilt. The
# main difference is that it uses a custom importer that plays nice
# with sprocket's caching system.
#
# See `SassImporter` for more infomation.
class SassTemplate < Tilt::Template
self.default_mime_type = 'text/css'
def self.engine_initialized?
defined?(::Sass::Engine) && defined?(::Sass::Script::Functions) &&
::Sass::Script::Functions < Sprockets::SassFunctions
end
def initialize_engine
# Double check constant to avoid tilt warning
unless defined? ::Sass
require_template_library 'sass'
end
# Install custom functions. It'd be great if this didn't need to
# be installed globally, but could be passed into Engine as an
# option.
::Sass::Script::Functions.send :include, Sprockets::SassFunctions
end
def prepare
end
def syntax
:sass
end
def evaluate(context, locals, &block)
# Use custom importer that knows about Sprockets Caching
cache_store = SassCacheStore.new(context.environment)
options = {
:filename => eval_file,
:line => line,
:syntax => syntax,
:cache_store => cache_store,
:importer => SassImporter.new(context, context.pathname),
:load_paths => context.environment.paths.map { |path| SassImporter.new(context, path) },
:sprockets => {
:context => context,
:environment => context.environment
}
}
::Sass::Engine.new(data, options).render
rescue ::Sass::SyntaxError => e
# Annotates exception message with parse line number
context.__LINE__ = e.sass_backtrace.first[:line]
raise e
end
end
end
sprockets-2.10.1/lib/sprockets/asset_attributes.rb 0000644 0000041 0000041 00000007537 12260352334 022351 0 ustar www-data www-data require 'pathname'
module Sprockets
# `AssetAttributes` is a wrapper similar to `Pathname` that provides
# some helper accessors.
#
# These methods should be considered internalish.
class AssetAttributes
attr_reader :environment, :pathname
def initialize(environment, path)
@environment = environment
@pathname = path.is_a?(Pathname) ? path : Pathname.new(path.to_s)
end
# Returns paths search the load path for.
def search_paths
paths = [pathname.to_s]
extension = format_extension
path_without_extension = extension ?
pathname.sub(extension, '') :
pathname
# optimization: bower.json can only be nested one level deep
if !path_without_extension.to_s.index('/')
paths << path_without_extension.join("bower.json").to_s
# DEPRECATED bower configuration file
paths << path_without_extension.join("component.json").to_s
end
if pathname.basename(extension.to_s).to_s != 'index'
paths << path_without_extension.join("index#{extension}").to_s
end
paths
end
# Reverse guess logical path for fully expanded path.
#
# This has some known issues. For an example if a file is
# shaddowed in the path, but is required relatively, its logical
# path will be incorrect.
def logical_path
if root_path = environment.paths.detect { |path| pathname.to_s[path] }
path = pathname.to_s.sub("#{root_path}/", '')
path = pathname.relative_path_from(Pathname.new(root_path)).to_s
path = engine_extensions.inject(path) { |p, ext| p.sub(ext, '') }
path = "#{path}#{engine_format_extension}" unless format_extension
path
else
raise FileOutsidePaths, "#{pathname} isn't in paths: #{environment.paths.join(', ')}"
end
end
# Returns `Array` of extension `String`s.
#
# "foo.js.coffee"
# # => [".js", ".coffee"]
#
def extensions
@extensions ||= @pathname.basename.to_s.scan(/\.[^.]+/)
end
# Returns the format extension.
#
# "foo.js.coffee"
# # => ".js"
#
def format_extension
extensions.reverse.detect { |ext|
@environment.mime_types(ext) && !@environment.engines(ext)
}
end
# Returns an `Array` of engine extensions.
#
# "foo.js.coffee.erb"
# # => [".coffee", ".erb"]
#
def engine_extensions
exts = extensions
if offset = extensions.index(format_extension)
exts = extensions[offset+1..-1]
end
exts.select { |ext| @environment.engines(ext) }
end
# Returns engine classes.
def engines
engine_extensions.map { |ext| @environment.engines(ext) }
end
# Returns all processors to run on the path.
def processors
environment.preprocessors(content_type) +
engines.reverse +
environment.postprocessors(content_type)
end
# Returns the content type for the pathname. Falls back to `application/octet-stream`.
def content_type
@content_type ||= begin
if format_extension.nil?
engine_content_type || 'application/octet-stream'
else
@environment.mime_types(format_extension) ||
engine_content_type ||
'application/octet-stream'
end
end
end
private
# Returns implicit engine content type.
#
# `.coffee` files carry an implicit `application/javascript`
# content type.
def engine_content_type
engines.reverse.each do |engine|
if engine.respond_to?(:default_mime_type) && engine.default_mime_type
return engine.default_mime_type
end
end
nil
end
def engine_format_extension
if content_type = engine_content_type
environment.extension_for_mime_type(content_type)
end
end
end
end
sprockets-2.10.1/lib/sprockets/caching.rb 0000644 0000041 0000041 00000005307 12260352334 020351 0 ustar www-data www-data module Sprockets
# `Caching` is an internal mixin whose public methods are exposed on
# the `Environment` and `Index` classes.
module Caching
# Low level cache getter for `key`. Checks a number of supported
# cache interfaces.
def cache_get(key)
# `Cache#get(key)` for Memcache
if cache.respond_to?(:get)
cache.get(key)
# `Cache#[key]` so `Hash` can be used
elsif cache.respond_to?(:[])
cache[key]
# `Cache#read(key)` for `ActiveSupport::Cache` support
elsif cache.respond_to?(:read)
cache.read(key)
else
nil
end
end
# Low level cache setter for `key`. Checks a number of supported
# cache interfaces.
def cache_set(key, value)
# `Cache#set(key, value)` for Memcache
if cache.respond_to?(:set)
cache.set(key, value)
# `Cache#[key]=value` so `Hash` can be used
elsif cache.respond_to?(:[]=)
cache[key] = value
# `Cache#write(key, value)` for `ActiveSupport::Cache` support
elsif cache.respond_to?(:write)
cache.write(key, value)
end
value
end
protected
# Cache helper method. Takes a `path` argument which maybe a
# logical path or fully expanded path. The `&block` is passed
# for finding and building the asset if its not in cache.
def cache_asset(path)
# If `cache` is not set, return fast
if cache.nil?
yield
# Check cache for `path`
elsif (asset = Asset.from_hash(self, cache_get_hash(path.to_s))) && asset.fresh?(self)
asset
# Otherwise yield block that slowly finds and builds the asset
elsif asset = yield
hash = {}
asset.encode_with(hash)
# Save the asset to its path
cache_set_hash(path.to_s, hash)
# Since path maybe a logical or full pathname, save the
# asset its its full path too
if path.to_s != asset.pathname.to_s
cache_set_hash(asset.pathname.to_s, hash)
end
asset
end
end
private
# Strips `Environment#root` from key to make the key work
# consisently across different servers. The key is also hashed
# so it does not exceed 250 characters.
def expand_cache_key(key)
File.join('sprockets', digest_class.hexdigest(key.sub(root, '')))
end
def cache_get_hash(key)
hash = cache_get(expand_cache_key(key))
if hash.is_a?(Hash) && digest.hexdigest == hash['_version']
hash
end
end
def cache_set_hash(key, hash)
hash['_version'] = digest.hexdigest
cache_set(expand_cache_key(key), hash)
hash
end
end
end
sprockets-2.10.1/lib/sprockets/environment.rb 0000644 0000041 0000041 00000004446 12260352334 021324 0 ustar www-data www-data require 'sprockets/base'
require 'sprockets/context'
require 'sprockets/index'
require 'hike'
require 'logger'
require 'pathname'
require 'tilt'
module Sprockets
class Environment < Base
# `Environment` should initialized with your application's root
# directory. This should be the same as your Rails or Rack root.
#
# env = Environment.new(Rails.root)
#
def initialize(root = ".")
@trail = Hike::Trail.new(root)
self.logger = Logger.new($stderr)
self.logger.level = Logger::FATAL
if respond_to?(:default_external_encoding)
self.default_external_encoding = Encoding::UTF_8
end
# Create a safe `Context` subclass to mutate
@context_class = Class.new(Context)
# Set MD5 as the default digest
require 'digest/md5'
@digest_class = ::Digest::MD5
@version = ''
@mime_types = Sprockets.registered_mime_types
@engines = Sprockets.engines
@preprocessors = Sprockets.preprocessors
@postprocessors = Sprockets.postprocessors
@bundle_processors = Sprockets.bundle_processors
@compressors = Sprockets.compressors
Sprockets.paths.each do |path|
append_path(path)
end
@engines.each do |ext, klass|
add_engine_to_trail(ext, klass)
end
@mime_types.each do |ext, type|
@trail.append_extension(ext)
end
expire_index!
yield self if block_given?
end
# Returns a cached version of the environment.
#
# All its file system calls are cached which makes `index` much
# faster. This behavior is ideal in production since the file
# system only changes between deploys.
def index
Index.new(self)
end
# Cache `find_asset` calls
def find_asset(path, options = {})
options[:bundle] = true unless options.key?(:bundle)
# Ensure inmemory cached assets are still fresh on every lookup
if (asset = @assets[cache_key_for(path, options)]) && asset.fresh?(self)
asset
elsif asset = index.find_asset(path, options)
# Cache is pushed upstream by Index#find_asset
asset
end
end
protected
def expire_index!
# Clear digest to be recomputed
@digest = nil
@assets = {}
end
end
end
sprockets-2.10.1/lib/sprockets/sass_cache_store.rb 0000644 0000041 0000041 00000001170 12260352334 022257 0 ustar www-data www-data require 'sass'
module Sprockets
class SassCacheStore < ::Sass::CacheStores::Base
attr_reader :environment
def initialize(environment)
@environment = environment
end
def _store(key, version, sha, contents)
environment.cache_set("sass/#{key}", {:version => version, :sha => sha, :contents => contents})
end
def _retrieve(key, version, sha)
if obj = environment.cache_get("sass/#{key}")
return unless obj[:version] == version
return unless obj[:sha] == sha
obj[:contents]
else
nil
end
end
def path_to(key)
key
end
end
end
sprockets-2.10.1/lib/sprockets/yui_compressor.rb 0000644 0000041 0000041 00000001017 12260352334 022031 0 ustar www-data www-data require 'tilt'
module Sprockets
class YUICompressor < Tilt::Template
def self.engine_initialized?
defined?(::YUI)
end
def initialize_engine
require_template_library 'yui/compressor'
end
def prepare
end
def evaluate(context, locals, &block)
case context.content_type
when 'application/javascript'
YUI::JavaScriptCompressor.new.compress(data)
when 'text/css'
YUI::CssCompressor.new.compress(data)
else
data
end
end
end
end
sprockets-2.10.1/lib/sprockets/cache/ 0000755 0000041 0000041 00000000000 12260352334 017466 5 ustar www-data www-data sprockets-2.10.1/lib/sprockets/cache/file_store.rb 0000644 0000041 0000041 00000001323 12260352334 022145 0 ustar www-data www-data require 'digest/md5'
require 'fileutils'
require 'pathname'
module Sprockets
module Cache
# A simple file system cache store.
#
# environment.cache = Sprockets::Cache::FileStore.new("/tmp")
#
class FileStore
def initialize(root)
@root = Pathname.new(root)
end
# Lookup value in cache
def [](key)
pathname = @root.join(key)
pathname.exist? ? pathname.open('rb') { |f| Marshal.load(f) } : nil
end
# Save value to cache
def []=(key, value)
# Ensure directory exists
FileUtils.mkdir_p @root.join(key).dirname
@root.join(key).open('w') { |f| Marshal.dump(value, f)}
value
end
end
end
end
sprockets-2.10.1/lib/sprockets/base.rb 0000644 0000041 0000041 00000030414 12260352334 017664 0 ustar www-data www-data require 'sprockets/asset_attributes'
require 'sprockets/bundled_asset'
require 'sprockets/caching'
require 'sprockets/errors'
require 'sprockets/processed_asset'
require 'sprockets/server'
require 'sprockets/static_asset'
require 'multi_json'
require 'pathname'
module Sprockets
# `Base` class for `Environment` and `Index`.
class Base
include Caching, Paths, Mime, Processing, Compressing, Engines, Server
# 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.
#
# This class maybe mutated and mixed in with custom helpers.
#
# environment.context_class.instance_eval do
# include MyHelpers
# def asset_url; end
# end
#
attr_reader :context_class
# Get persistent cache store
attr_reader :cache
# Set persistent cache store
#
# The cache store must implement a pair of getters and
# setters. Either `get(key)`/`set(key, value)`,
# `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
def cache=(cache)
expire_index!
@cache = cache
end
def prepend_path(path)
# Overrides the global behavior to expire the index
expire_index!
super
end
def append_path(path)
# Overrides the global behavior to expire the index
expire_index!
super
end
def clear_paths
# 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.
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
# 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
# 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_preprocessor(mime_type, klass, &block)
# Overrides the global behavior to expire the index
expire_index!
super
end
def unregister_preprocessor(mime_type, klass)
# Overrides the global behavior to expire the index
expire_index!
super
end
def register_postprocessor(mime_type, klass, &block)
# Overrides the global behavior to expire the index
expire_index!
super
end
def unregister_postprocessor(mime_type, klass)
# Overrides the global behavior to expire the index
expire_index!
super
end
def register_bundle_processor(mime_type, klass, &block)
# Overrides the global behavior to expire the index
expire_index!
super
end
def unregister_bundle_processor(mime_type, klass)
# Overrides the global behavior to expire the index
expire_index!
super
end
# Return an `Index`. Must be implemented by the subclass.
def index
raise NotImplementedError
end
if defined? Encoding.default_external
# Define `default_external_encoding` accessor on 1.9.
# Defaults to UTF-8.
attr_accessor :default_external_encoding
end
# Works like `Dir.entries`.
#
# Subclasses may cache this method.
def entries(pathname)
@trail.entries(pathname)
end
# Works like `File.stat`.
#
# Subclasses may cache this method.
def stat(path)
@trail.stat(path)
end
# Read and compute digest of filename.
#
# Subclasses may cache this method.
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
# Internal. Return a `AssetAttributes` for `path`.
def attributes_for(path)
AssetAttributes.new(self, path)
end
# Internal. Return content type of `path`.
def content_type_of(path)
attributes_for(path).content_type
end
# 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
# Preferred `find_asset` shorthand.
#
# environment['application.js']
#
def [](*args)
find_asset(*args)
end
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
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)
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
# 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
protected
# Clear index after mutating state. Must be implemented by the subclass.
def expire_index!
raise NotImplementedError
end
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_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
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)
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
# Feature detect newer MultiJson API
if MultiJson.respond_to?(:dump)
def json_decode(obj)
MultiJson.load(obj)
end
else
def json_decode(obj)
MultiJson.decode(obj)
end
end
end
end
sprockets-2.10.1/lib/sprockets/bundled_asset.rb 0000644 0000041 0000041 00000004702 12260352334 021567 0 ustar www-data www-data require 'sprockets/asset'
require 'sprockets/errors'
require 'fileutils'
require 'set'
require 'zlib'
module Sprockets
# `BundledAsset`s are used for files that need to be processed and
# concatenated with other assets. Use for `.js` and `.css` files.
class BundledAsset < Asset
attr_reader :source
def initialize(environment, logical_path, pathname)
super(environment, logical_path, pathname)
@processed_asset = environment.find_asset(pathname, :bundle => false)
@required_assets = @processed_asset.required_assets
@dependency_paths = @processed_asset.dependency_paths
# Explode Asset into parts and gather the dependency bodies
@source = to_a.map { |dependency| dependency.to_s }.join
# Run bundle processors on concatenated source
context = environment.context_class.new(environment, logical_path, pathname)
@source = context.evaluate(pathname, :data => @source,
:processors => environment.bundle_processors(content_type))
@mtime = (to_a + @dependency_paths).map(&:mtime).max
@length = Rack::Utils.bytesize(source)
@digest = environment.digest.update(source).hexdigest
end
# Initialize `BundledAsset` from serialized `Hash`.
def init_with(environment, coder)
super
@processed_asset = environment.find_asset(pathname, :bundle => false)
@required_assets = @processed_asset.required_assets
if @processed_asset.dependency_digest != coder['required_assets_digest']
raise UnserializeError, "processed asset belongs to a stale environment"
end
@source = coder['source']
end
# Serialize custom attributes in `BundledAsset`.
def encode_with(coder)
super
coder['source'] = source
coder['required_assets_digest'] = @processed_asset.dependency_digest
end
# Get asset's own processed contents. Excludes any of its required
# dependencies but does run any processors or engines on the
# original file.
def body
@processed_asset.source
end
# Return an `Array` of `Asset` files that are declared dependencies.
def dependencies
to_a.reject { |a| a.eql?(@processed_asset) }
end
# Expand asset into an `Array` of parts.
def to_a
required_assets
end
# Checks if Asset is stale by comparing the actual mtime and
# digest to the inmemory model.
def fresh?(environment)
@processed_asset.fresh?(environment)
end
end
end
sprockets-2.10.1/lib/sprockets/ejs_template.rb 0000644 0000041 0000041 00000001571 12260352334 021430 0 ustar www-data www-data require 'tilt'
module Sprockets
# Tilt engine class for the EJS compiler. Depends on the `ejs` gem.
#
# For more infomation see:
#
# https://github.com/sstephenson/ruby-ejs
#
class EjsTemplate < Tilt::Template
# Check to see if EJS is loaded
def self.engine_initialized?
defined? ::EJS
end
# Autoload ejs library. If the library isn't loaded, Tilt will produce
# a thread safetly warning. If you intend to use `.ejs` files, you
# should explicitly require it.
def initialize_engine
require_template_library 'ejs'
end
def prepare
end
# Compile template data with EJS compiler.
#
# Returns a JS function definition String. The result should be
# assigned to a JS variable.
#
# # => "function(obj){...}"
#
def evaluate(scope, locals, &block)
EJS.compile(data)
end
end
end
sprockets-2.10.1/lib/sprockets/asset.rb 0000644 0000041 0000041 00000017125 12260352334 020075 0 ustar www-data www-data require 'time'
require 'set'
module Sprockets
# `Asset` is the base class for `BundledAsset` and `StaticAsset`.
class Asset
# Internal initializer to load `Asset` from serialized `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
attr_reader :logical_path, :pathname
attr_reader :content_type, :mtime, :length, :digest
alias_method :bytesize, :length
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
# Initialize `Asset` from serialized `Hash`.
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
# Copy serialized attributes to the coder object
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
# Return logical path with digest spliced in.
#
# "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
#
def digest_path
logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
end
# Return an `Array` of `Asset` files that are declared dependencies.
def dependencies
[]
end
# Expand asset into an `Array` of parts.
#
# Appending all of an assets body parts together should give you
# the asset's contents as a whole.
#
# This allows you to link to individual files for debugging
# purposes.
def to_a
[self]
end
# `body` is aliased to source by default if it can't have any dependencies.
def body
source
end
# Return `String` of concatenated source.
def to_s
source
end
# Add enumerator to allow `Asset` instances to be used as Rack
# compatible body objects.
def each
yield to_s
end
# Checks if Asset is fresh by comparing the actual mtime and
# digest to the inmemory model.
#
# Used to test if cached models need to be rebuilt.
def fresh?(environment)
# Check current mtime and digest
dependency_fresh?(environment, self)
end
# Checks if Asset is stale by comparing the actual mtime and
# digest to the inmemory model.
#
# Subclass must override `fresh?` or `stale?`.
def stale?(environment)
!fresh?(environment)
end
# Save asset to disk.
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
# Pretty inspect
def inspect
"#<#{self.class}:0x#{object_id.to_s(16)} " +
"pathname=#{pathname.to_s.inspect}, " +
"mtime=#{mtime.inspect}, " +
"digest=#{digest.inspect}" +
">"
end
def hash
digest.hash
end
# Assets are equal if they share the same path, mtime and digest.
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
alias_method :==, :eql?
protected
# Internal: String paths that are marked as dependencies after processing.
#
# Default to an empty `Array`.
def dependency_paths
@dependency_paths ||= []
end
# Internal: `ProccessedAsset`s that are required after processing.
#
# Default to an empty `Array`.
def required_assets
@required_assets ||= []
end
# Get pathname with its root stripped.
def relative_pathname
@relative_pathname ||= Pathname.new(relativize_root_path(pathname))
end
# Replace `$root` placeholder with actual environment root.
def expand_root_path(path)
path.to_s.sub(/^\$root/, @root)
end
# Replace actual environment root with `$root` placeholder.
def relativize_root_path(path)
path.to_s.sub(/^#{Regexp.escape(@root)}/, '$root')
end
# Check if dependency is fresh.
#
# `dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
#
# A `Hash` is used rather than other `Asset` object because we
# want to test non-asset files and directories.
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
end
end
sprockets-2.10.1/lib/sprockets/server.rb 0000644 0000041 0000041 00000016624 12260352334 020267 0 ustar www-data www-data require 'time'
require 'uri'
module Sprockets
# `Server` is a concern mixed into `Environment` and
# `Index` that provides a Rack compatible `call`
# interface and url generation helpers.
module Server
# `call` implements the Rack 1.x specification which accepts an
# `env` Hash and returns a three item tuple with the status code,
# headers, and body.
#
# Mapping your environment at a url prefix will serve all assets
# in the path.
#
# map "/assets" do
# run Sprockets::Environment.new
# end
#
# A request for `"/assets/foo/bar.js"` will search your
# environment for `"foo/bar.js"`.
def call(env)
start_time = Time.now.to_f
time_elapsed = lambda { ((Time.now.to_f - start_time) * 1000).to_i }
msg = "Served asset #{env['PATH_INFO']} -"
# Mark session as "skipped" so no `Set-Cookie` header is set
env['rack.session.options'] ||= {}
env['rack.session.options'][:defer] = true
env['rack.session.options'][:skip] = true
# Extract the path from everything after the leading slash
path = unescape(env['PATH_INFO'].to_s.sub(/^\//, ''))
# URLs containing a `".."` are rejected for security reasons.
if forbidden_request?(path)
return forbidden_response
end
# Strip fingerprint
if fingerprint = path_fingerprint(path)
path = path.sub("-#{fingerprint}", '')
end
# Look up the asset.
asset = find_asset(path, :bundle => !body_only?(env))
# `find_asset` returns nil if the asset doesn't exist
if asset.nil?
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
# Return a 404 Not Found
not_found_response
# Check request headers `HTTP_IF_NONE_MATCH` against the asset digest
elsif etag_match?(asset, env)
logger.info "#{msg} 304 Not Modified (#{time_elapsed.call}ms)"
# Return a 304 Not Modified
not_modified_response(asset, env)
else
logger.info "#{msg} 200 OK (#{time_elapsed.call}ms)"
# Return a 200 with the asset contents
ok_response(asset, env)
end
rescue Exception => e
logger.error "Error compiling asset #{path}:"
logger.error "#{e.class.name}: #{e.message}"
case content_type_of(path)
when "application/javascript"
# Re-throw JavaScript asset exceptions to the browser
logger.info "#{msg} 500 Internal Server Error\n\n"
return javascript_exception_response(e)
when "text/css"
# Display CSS asset exceptions in the browser
logger.info "#{msg} 500 Internal Server Error\n\n"
return css_exception_response(e)
else
raise
end
end
private
def forbidden_request?(path)
# Prevent access to files elsewhere on the file system
#
# http://example.org/assets/../../../etc/passwd
#
path.include?("..")
end
# Returns a 403 Forbidden response tuple
def forbidden_response
[ 403, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Forbidden" ] ]
end
# Returns a 404 Not Found response tuple
def not_found_response
[ 404, { "Content-Type" => "text/plain", "Content-Length" => "9", "X-Cascade" => "pass" }, [ "Not found" ] ]
end
# Returns a JavaScript response that re-throws a Ruby exception
# in the browser
def javascript_exception_response(exception)
err = "#{exception.class.name}: #{exception.message}"
body = "throw Error(#{err.inspect})"
[ 200, { "Content-Type" => "application/javascript", "Content-Length" => Rack::Utils.bytesize(body).to_s }, [ body ] ]
end
# Returns a CSS response that hides all elements on the page and
# displays the exception
def css_exception_response(exception)
message = "\n#{exception.class.name}: #{exception.message}"
backtrace = "\n #{exception.backtrace.first}"
body = <<-CSS
html {
padding: 18px 36px;
}
head {
display: block;
}
body {
margin: 0;
padding: 0;
}
body > * {
display: none !important;
}
head:after, body:before, body:after {
display: block !important;
}
head:after {
font-family: sans-serif;
font-size: large;
font-weight: bold;
content: "Error compiling CSS asset";
}
body:before, body:after {
font-family: monospace;
white-space: pre-wrap;
}
body:before {
font-weight: bold;
content: "#{escape_css_content(message)}";
}
body:after {
content: "#{escape_css_content(backtrace)}";
}
CSS
[ 200, { "Content-Type" => "text/css;charset=utf-8", "Content-Length" => Rack::Utils.bytesize(body).to_s }, [ body ] ]
end
# Escape special characters for use inside a CSS content("...") string
def escape_css_content(content)
content.
gsub('\\', '\\\\005c ').
gsub("\n", '\\\\000a ').
gsub('"', '\\\\0022 ').
gsub('/', '\\\\002f ')
end
# Compare the requests `HTTP_IF_NONE_MATCH` against the assets digest
def etag_match?(asset, env)
env["HTTP_IF_NONE_MATCH"] == etag(asset)
end
# Test if `?body=1` or `body=true` query param is set
def body_only?(env)
env["QUERY_STRING"].to_s =~ /body=(1|t)/
end
# Returns a 304 Not Modified response tuple
def not_modified_response(asset, env)
[ 304, {}, [] ]
end
# Returns a 200 OK response tuple
def ok_response(asset, env)
[ 200, headers(env, asset, asset.length), asset ]
end
def headers(env, asset, length)
Hash.new.tap do |headers|
# Set content type and length headers
headers["Content-Type"] = asset.content_type
headers["Content-Length"] = length.to_s
# Set caching headers
headers["Cache-Control"] = "public"
headers["Last-Modified"] = asset.mtime.httpdate
headers["ETag"] = etag(asset)
# If the request url contains a fingerprint, set a long
# expires on the response
if path_fingerprint(env["PATH_INFO"])
headers["Cache-Control"] << ", max-age=31536000"
# Otherwise set `must-revalidate` since the asset could be modified.
else
headers["Cache-Control"] << ", must-revalidate"
end
end
end
# Gets digest fingerprint.
#
# "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
# # => "0aa2105d29558f3eb790d411d7d8fb66"
#
def path_fingerprint(path)
path[/-([0-9a-f]{7,40})\.[^.]+$/, 1]
end
# URI.unescape is deprecated on 1.9. We need to use URI::Parser
# if its available.
if defined? URI::DEFAULT_PARSER
def unescape(str)
str = URI::DEFAULT_PARSER.unescape(str)
str.force_encoding(Encoding.default_internal) if Encoding.default_internal
str
end
else
def unescape(str)
URI.unescape(str)
end
end
# Helper to quote the assets digest for use as an ETag.
def etag(asset)
%("#{asset.digest}")
end
end
end
sprockets-2.10.1/lib/sprockets/sass_functions.rb 0000644 0000041 0000041 00000003620 12260352334 022012 0 ustar www-data www-data require 'sass'
module Sprockets
module SassFunctions
def asset_path(path)
Sass::Script::String.new(sprockets_context.asset_path(path.value), :string)
end
def asset_url(path)
Sass::Script::String.new("url(" + sprockets_context.asset_path(path.value) + ")")
end
def image_path(path)
Sass::Script::String.new(sprockets_context.image_path(path.value), :string)
end
def image_url(path)
Sass::Script::String.new("url(" + sprockets_context.image_path(path.value) + ")")
end
def video_path(path)
Sass::Script::String.new(sprockets_context.video_path(path.value), :string)
end
def video_url(path)
Sass::Script::String.new("url(" + sprockets_context.video_path(path.value) + ")")
end
def audio_path(path)
Sass::Script::String.new(sprockets_context.audio_path(path.value), :string)
end
def audio_url(path)
Sass::Script::String.new("url(" + sprockets_context.audio_path(path.value) + ")")
end
def font_path(path)
Sass::Script::String.new(sprockets_context.font_path(path.value), :string)
end
def font_url(path)
Sass::Script::String.new("url(" + sprockets_context.font_path(path.value) + ")")
end
def javascript_path(path)
Sass::Script::String.new(sprockets_context.javascript_path(path.value), :string)
end
def javascript_url(path)
Sass::Script::String.new("url(" + sprockets_context.javascript_path(path.value) + ")")
end
def stylesheet_path(path)
Sass::Script::String.new(sprockets_context.stylesheet_path(path.value), :string)
end
def stylesheet_url(path)
Sass::Script::String.new("url(" + sprockets_context.stylesheet_path(path.value) + ")")
end
protected
def sprockets_context
options[:sprockets][:context]
end
def sprockets_environment
options[:sprockets][:environment]
end
end
end
sprockets-2.10.1/lib/sprockets/engines.rb 0000644 0000041 0000041 00000004342 12260352334 020403 0 ustar www-data www-data require 'sprockets/eco_template'
require 'sprockets/ejs_template'
require 'sprockets/jst_processor'
require 'sprockets/utils'
require 'tilt'
module Sprockets
# `Engines` provides a global and `Environment` instance registry.
#
# An engine is a type of processor that is bound to an filename
# extension. `application.js.coffee` indicates that the
# `CoffeeScriptTemplate` engine will be ran on the file.
#
# Extensions can be stacked and will be evaulated from right to
# left. `application.js.coffee.erb` will first run `ERBTemplate`
# then `CoffeeScriptTemplate`.
#
# All `Engine`s must follow the `Tilt::Template` interface. It is
# recommended to subclass `Tilt::Template`.
#
# Its recommended that you register engine changes on your local
# `Environment` instance.
#
# environment.register_engine '.foo', FooProcessor
#
# The global registry is exposed for plugins to register themselves.
#
# Sprockets.register_engine '.sass', SassTemplate
#
module Engines
# Returns a `Hash` of `Engine`s registered on the `Environment`.
# If an `ext` argument is supplied, the `Engine` associated with
# that extension will be returned.
#
# environment.engines
# # => {".coffee" => CoffeeScriptTemplate, ".sass" => SassTemplate, ...}
#
# environment.engines('.coffee')
# # => CoffeeScriptTemplate
#
def engines(ext = nil)
if ext
ext = Sprockets::Utils.normalize_extension(ext)
@engines[ext]
else
@engines.dup
end
end
# Returns an `Array` of engine extension `String`s.
#
# environment.engine_extensions
# # => ['.coffee', '.sass', ...]
def engine_extensions
@engines.keys
end
# Registers a new Engine `klass` for `ext`. If the `ext` already
# has an engine registered, it will be overridden.
#
# environment.register_engine '.coffee', CoffeeScriptTemplate
#
def register_engine(ext, klass)
ext = Sprockets::Utils.normalize_extension(ext)
@engines[ext] = klass
end
private
def deep_copy_hash(hash)
initial = Hash.new { |h, k| h[k] = [] }
hash.inject(initial) { |h, (k, a)| h[k] = a.dup; h }
end
end
end
sprockets-2.10.1/lib/sprockets/static_asset.rb 0000644 0000041 0000041 00000003103 12260352334 021433 0 ustar www-data www-data require 'sprockets/asset'
require 'fileutils'
require 'zlib'
module Sprockets
# `StaticAsset`s are used for files that are served verbatim without
# any processing or concatenation. These are typical images and
# other binary files.
class StaticAsset < Asset
# Returns file contents as its `source`.
def source
# File is read everytime to avoid memory bloat of large binary files
pathname.open('rb') { |f| f.read }
end
# Implemented for Rack SendFile support.
def to_path
pathname.to_s
end
# Save asset to disk.
def write_to(filename, options = {})
# Gzip contents if filename has '.gz'
options[:compress] ||= File.extname(filename) == '.gz'
FileUtils.mkdir_p File.dirname(filename)
if options[:compress]
# Open file and run it through `Zlib`
pathname.open('rb') do |rd|
File.open("#{filename}+", 'wb') do |wr|
gz = Zlib::GzipWriter.new(wr, Zlib::BEST_COMPRESSION)
gz.mtime = mtime.to_i
buf = ""
while rd.read(16384, buf)
gz.write(buf)
end
gz.close
end
end
else
# If no compression needs to be done, we can just copy it into place.
FileUtils.cp(pathname, "#{filename}+")
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
end
end
sprockets-2.10.1/lib/sprockets.rb 0000644 0000041 0000041 00000010020 12260352334 016741 0 ustar www-data www-data require 'sprockets/version'
module Sprockets
# Environment
autoload :Base, "sprockets/base"
autoload :Environment, "sprockets/environment"
autoload :Index, "sprockets/index"
autoload :Manifest, "sprockets/manifest"
# Assets
autoload :Asset, "sprockets/asset"
autoload :BundledAsset, "sprockets/bundled_asset"
autoload :ProcessedAsset, "sprockets/processed_asset"
autoload :StaticAsset, "sprockets/static_asset"
# Processing
autoload :Context, "sprockets/context"
autoload :EcoTemplate, "sprockets/eco_template"
autoload :EjsTemplate, "sprockets/ejs_template"
autoload :JstProcessor, "sprockets/jst_processor"
autoload :Processor, "sprockets/processor"
autoload :SassCacheStore, "sprockets/sass_cache_store"
autoload :SassFunctions, "sprockets/sass_functions"
autoload :SassImporter, "sprockets/sass_importer"
autoload :SassTemplate, "sprockets/sass_template"
autoload :ScssTemplate, "sprockets/scss_template"
# Internal utilities
autoload :ArgumentError, "sprockets/errors"
autoload :AssetAttributes, "sprockets/asset_attributes"
autoload :CircularDependencyError, "sprockets/errors"
autoload :ContentTypeMismatch, "sprockets/errors"
autoload :EngineError, "sprockets/errors"
autoload :Error, "sprockets/errors"
autoload :FileNotFound, "sprockets/errors"
autoload :Utils, "sprockets/utils"
module Cache
autoload :FileStore, "sprockets/cache/file_store"
end
# Extend Sprockets module to provide global registry
require 'hike'
require 'sprockets/engines'
require 'sprockets/mime'
require 'sprockets/processing'
require 'sprockets/compressing'
require 'sprockets/paths'
extend Engines, Mime, Processing, Compressing, Paths
@trail = Hike::Trail.new(File.expand_path('..', __FILE__))
@mime_types = {}
@engines = {}
@preprocessors = Hash.new { |h, k| h[k] = [] }
@postprocessors = Hash.new { |h, k| h[k] = [] }
@bundle_processors = Hash.new { |h, k| h[k] = [] }
@compressors = Hash.new { |h, k| h[k] = {} }
register_mime_type 'text/css', '.css'
register_mime_type 'application/javascript', '.js'
require 'sprockets/directive_processor'
register_preprocessor 'text/css', DirectiveProcessor
register_preprocessor 'application/javascript', DirectiveProcessor
require 'sprockets/safety_colons'
register_postprocessor 'application/javascript', SafetyColons
require 'sprockets/charset_normalizer'
register_bundle_processor 'text/css', CharsetNormalizer
require 'sprockets/sass_compressor'
register_compressor 'text/css', :sass, SassCompressor
register_compressor 'text/css', :scss, SassCompressor
require 'sprockets/yui_compressor'
register_compressor 'text/css', :yui, YUICompressor
require 'sprockets/closure_compressor'
register_compressor 'application/javascript', :closure, ClosureCompressor
require 'sprockets/uglifier_compressor'
register_compressor 'application/javascript', :uglifier, UglifierCompressor
register_compressor 'application/javascript', :uglify, UglifierCompressor
require 'sprockets/yui_compressor'
register_compressor 'application/javascript', :yui, YUICompressor
# Cherry pick the default Tilt engines that make sense for
# Sprockets. We don't need ones that only generate html like HAML.
# Mmm, CoffeeScript
register_engine '.coffee', Tilt::CoffeeScriptTemplate
# JST engines
register_engine '.jst', JstProcessor
register_engine '.eco', EcoTemplate
register_engine '.ejs', EjsTemplate
# CSS engines
register_engine '.less', Tilt::LessTemplate
register_engine '.sass', SassTemplate
register_engine '.scss', ScssTemplate
# Other
register_engine '.erb', Tilt::ERBTemplate
register_engine '.str', Tilt::StringTemplate
end
sprockets-2.10.1/metadata.yml 0000644 0000041 0000041 00000017442 12260352334 016153 0 ustar www-data www-data --- !ruby/object:Gem::Specification
name: sprockets
version: !ruby/object:Gem::Version
hash: 37
prerelease:
segments:
- 2
- 10
- 1
version: 2.10.1
platform: ruby
authors:
- Sam Stephenson
- Joshua Peek
autorequire:
bindir: bin
cert_chain: []
date: 2013-11-22 00:00:00 -08:00
default_executable:
dependencies:
- !ruby/object:Gem::Dependency
name: hike
prerelease: false
requirement: &id001 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 11
segments:
- 1
- 2
version: "1.2"
type: :runtime
version_requirements: *id001
- !ruby/object:Gem::Dependency
name: multi_json
prerelease: false
requirement: &id002 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 15
segments:
- 1
- 0
version: "1.0"
type: :runtime
version_requirements: *id002
- !ruby/object:Gem::Dependency
name: rack
prerelease: false
requirement: &id003 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 15
segments:
- 1
- 0
version: "1.0"
type: :runtime
version_requirements: *id003
- !ruby/object:Gem::Dependency
name: tilt
prerelease: false
requirement: &id004 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 13
segments:
- 1
- 1
version: "1.1"
- - "!="
- !ruby/object:Gem::Version
hash: 27
segments:
- 1
- 3
- 0
version: 1.3.0
type: :runtime
version_requirements: *id004
- !ruby/object:Gem::Dependency
name: closure-compiler
prerelease: false
requirement: &id005 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
type: :development
version_requirements: *id005
- !ruby/object:Gem::Dependency
name: coffee-script
prerelease: false
requirement: &id006 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 3
segments:
- 2
- 0
version: "2.0"
type: :development
version_requirements: *id006
- !ruby/object:Gem::Dependency
name: coffee-script-source
prerelease: false
requirement: &id007 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 11
segments:
- 1
- 2
version: "1.2"
type: :development
version_requirements: *id007
- !ruby/object:Gem::Dependency
name: eco
prerelease: false
requirement: &id008 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 15
segments:
- 1
- 0
version: "1.0"
type: :development
version_requirements: *id008
- !ruby/object:Gem::Dependency
name: ejs
prerelease: false
requirement: &id009 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 15
segments:
- 1
- 0
version: "1.0"
type: :development
version_requirements: *id009
- !ruby/object:Gem::Dependency
name: execjs
prerelease: false
requirement: &id010 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 15
segments:
- 1
- 0
version: "1.0"
type: :development
version_requirements: *id010
- !ruby/object:Gem::Dependency
name: json
prerelease: false
requirement: &id011 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
type: :development
version_requirements: *id011
- !ruby/object:Gem::Dependency
name: rack-test
prerelease: false
requirement: &id012 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
type: :development
version_requirements: *id012
- !ruby/object:Gem::Dependency
name: rake
prerelease: false
requirement: &id013 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
type: :development
version_requirements: *id013
- !ruby/object:Gem::Dependency
name: sass
prerelease: false
requirement: &id014 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 5
segments:
- 3
- 1
version: "3.1"
type: :development
version_requirements: *id014
- !ruby/object:Gem::Dependency
name: uglifier
prerelease: false
requirement: &id015 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
type: :development
version_requirements: *id015
- !ruby/object:Gem::Dependency
name: yui-compressor
prerelease: false
requirement: &id016 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
type: :development
version_requirements: *id016
description: Sprockets is a Rack-based asset packaging system that concatenates and serves JavaScript, CoffeeScript, CSS, LESS, Sass, and SCSS.
email:
- sstephenson@gmail.com
- josh@joshpeek.com
executables:
- sprockets
extensions: []
extra_rdoc_files: []
files:
- README.md
- LICENSE
- lib/rake/sprocketstask.rb
- lib/sprockets/asset.rb
- lib/sprockets/asset_attributes.rb
- lib/sprockets/base.rb
- lib/sprockets/bundled_asset.rb
- lib/sprockets/cache/file_store.rb
- lib/sprockets/caching.rb
- lib/sprockets/charset_normalizer.rb
- lib/sprockets/closure_compressor.rb
- lib/sprockets/compressing.rb
- lib/sprockets/context.rb
- lib/sprockets/directive_processor.rb
- lib/sprockets/eco_template.rb
- lib/sprockets/ejs_template.rb
- lib/sprockets/engines.rb
- lib/sprockets/environment.rb
- lib/sprockets/errors.rb
- lib/sprockets/index.rb
- lib/sprockets/jst_processor.rb
- lib/sprockets/manifest.rb
- lib/sprockets/mime.rb
- lib/sprockets/paths.rb
- lib/sprockets/processed_asset.rb
- lib/sprockets/processing.rb
- lib/sprockets/processor.rb
- lib/sprockets/safety_colons.rb
- lib/sprockets/sass_cache_store.rb
- lib/sprockets/sass_compressor.rb
- lib/sprockets/sass_functions.rb
- lib/sprockets/sass_importer.rb
- lib/sprockets/sass_template.rb
- lib/sprockets/scss_template.rb
- lib/sprockets/server.rb
- lib/sprockets/static_asset.rb
- lib/sprockets/uglifier_compressor.rb
- lib/sprockets/utils.rb
- lib/sprockets/version.rb
- lib/sprockets/yui_compressor.rb
- lib/sprockets.rb
- bin/sprockets
has_rdoc: true
homepage: http://getsprockets.org/
licenses:
- MIT
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
required_rubygems_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
requirements: []
rubyforge_project: sprockets
rubygems_version: 1.6.2
signing_key:
specification_version: 3
summary: Rack-based asset packaging system
test_files: []
sprockets-2.10.1/LICENSE 0000644 0000041 0000041 00000002101 12260352334 014637 0 ustar www-data www-data Copyright (c) 2011 Sam Stephenson
Copyright (c) 2011 Joshua Peek
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
sprockets-2.10.1/README.md 0000644 0000041 0000041 00000041151 12260352334 015121 0 ustar www-data www-data # Sprockets: Rack-based asset packaging
Sprockets is a Ruby library for compiling and serving web assets.
It features declarative dependency management for JavaScript and CSS
assets, as well as a powerful preprocessor pipeline that allows you to
write assets in languages like CoffeeScript, Sass, SCSS and LESS.
# Installation #
Install Sprockets from RubyGems:
$ gem install sprockets
Or include it in your project's `Gemfile` with Bundler:
gem 'sprockets', '~> 2.0'
# Understanding the Sprockets Environment #
You'll need an instance of the `Sprockets::Environment` class to
access and serve assets from your application. Under Rails 3.1 and
later, `YourApp::Application.assets` is a preconfigured
`Sprockets::Environment` instance. For Rack-based applications, create
an instance in `config.ru`.
The Sprockets `Environment` has methods for retrieving and serving
assets, manipulating the load path, and registering processors. It is
also a Rack application that can be mounted at a URL to serve assets
over HTTP.
## The Load Path ##
The *load path* is an ordered list of directories that Sprockets uses
to search for assets.
In the simplest case, a Sprockets environment's load path will consist
of a single directory containing your application's asset source
files. When mounted, the environment will serve assets from this
directory as if they were static files in your public root.
The power of the load path is that it lets you organize your source
files into multiple directories -- even directories that live outside
your application -- and combine those directories into a single
virtual filesystem. That means you can easily bundle JavaScript, CSS
and images into a Ruby library and import them into your application.
### Manipulating the Load Path ###
To add a directory to your environment's load path, use the
`append_path` and `prepend_path` methods. Directories at the beginning
of the load path have precedence over subsequent directories.
environment = Sprockets::Environment.new
environment.append_path 'app/assets/javascripts'
environment.append_path 'lib/assets/javascripts'
environment.append_path 'vendor/assets/jquery'
In general, you should append to the path by default and reserve
prepending for cases where you need to override existing assets.
## Accessing Assets ##
Once you've set up your environment's load path, you can mount the
environment as a Rack server and request assets via HTTP. You can also
access assets programmatically from within your application.
### Logical Paths ###
Assets in Sprockets are always referenced by their *logical path*.
The logical path is the path of the asset source file relative to its
containing directory in the load path. For example, if your load path
contains the directory `app/assets/javascripts`:
Asset source file | Logical path |
---|---|
app/assets/javascripts/application.js | application.js |
app/assets/javascripts/models/project.js | models/project.js |