sprockets-2.10.1/0000755000004100000410000000000012260352334013640 5ustar www-datawww-datasprockets-2.10.1/bin/0000755000004100000410000000000012260352334014410 5ustar www-datawww-datasprockets-2.10.1/bin/sprockets0000755000004100000410000000411212260352334016351 0ustar www-datawww-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/0000755000004100000410000000000012260352334014406 5ustar www-datawww-datasprockets-2.10.1/lib/rake/0000755000004100000410000000000012260352334015330 5ustar www-datawww-datasprockets-2.10.1/lib/rake/sprocketstask.rb0000644000004100000410000000724512260352334020565 0ustar www-datawww-datarequire '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/0000755000004100000410000000000012260352334016423 5ustar www-datawww-datasprockets-2.10.1/lib/sprockets/directive_processor.rb0000644000004100000410000002765012260352334023037 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000467512260352334020124 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000077112260352334022202 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001677412260352334020575 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000563612260352334020071 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000064512260352334022705 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000115212260352334020263 0ustar www-datawww-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.rb0000644000004100000410000002110212260352334020430 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000232612260352334022646 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000164312260352334021415 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000126612260352334021647 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001122512260352334022137 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000115112260352334020765 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000256612260352334017710 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000111212260352334021642 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001402012260352334021121 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000276512260352334020101 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000135512260352334021624 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000005212260352334020432 0ustar www-datawww-datamodule Sprockets VERSION = "2.10.1" end sprockets-2.10.1/lib/sprockets/compressing.rb0000644000004100000410000000434612260352334021310 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000043512260352334021620 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000117512260352334023036 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000336412260352334021622 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000753712260352334022351 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000530712260352334020351 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000444612260352334021324 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000117012260352334022257 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000101712260352334022031 0ustar www-datawww-datarequire '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/0000755000004100000410000000000012260352334017466 5ustar www-datawww-datasprockets-2.10.1/lib/sprockets/cache/file_store.rb0000644000004100000410000000132312260352334022145 0ustar www-datawww-datarequire '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.rb0000644000004100000410000003041412260352334017664 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000470212260352334021567 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000157112260352334021430 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001712512260352334020075 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001662412260352334020267 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000362012260352334022012 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000434212260352334020403 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000310312260352334021433 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001002012260352334016741 0ustar www-datawww-datarequire '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.yml0000644000004100000410000001744212260352334016153 0ustar www-datawww-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/LICENSE0000644000004100000410000000210112260352334014637 0ustar www-datawww-dataCopyright (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.md0000644000004100000410000004115112260352334015121 0ustar www-datawww-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
In this way, all directories in the load path are merged to create a virtual filesystem whose entries are logical paths. ### Serving Assets Over HTTP ### When you mount an environment, all of its assets are accessible as logical paths underneath the *mount point*. For example, if you mount your environment at `/assets` and request the URL `/assets/application.js`, Sprockets will search your load path for the file named `application.js` and serve it. Under Rails 3.1 and later, your Sprockets environment is automatically mounted at `/assets`. If you are using Sprockets with a Rack application, you will need to mount the environment yourself. A good way to do this is with the `map` method in `config.ru`: require 'sprockets' map '/assets' do environment = Sprockets::Environment.new environment.append_path 'app/assets/javascripts' environment.append_path 'app/assets/stylesheets' run environment end map '/' do run YourRackApp end ### Accessing Assets Programmatically ### You can use the `find_asset` method (aliased as `[]`) to retrieve an asset from a Sprockets environment. Pass it a logical path and you'll get a `Sprockets::BundledAsset` instance back: environment['application.js'] # => # Call `to_s` on the resulting asset to access its contents, `length` to get its length in bytes, `mtime` to query its last-modified time, and `pathname` to get its full path on the filesystem. # Using Engines # Asset source files can be written in another language, like SCSS or CoffeeScript, and automatically compiled to CSS or JavaScript by Sprockets. Compilers for these languages are called *engines*. Engines are specified by additional extensions on the asset source filename. For example, a CSS file written in SCSS might have the name `layout.css.scss`, while a JavaScript file written in CoffeeScript might have the name `dialog.js.coffee`. ## Styling with Sass and SCSS ## [Sass](http://sass-lang.com/) is a language that compiles to CSS and adds features like nested rules, variables, mixins and selector inheritance. If the `sass` gem is available to your application, you can use Sass to write CSS assets in Sprockets. Sprockets supports both Sass syntaxes. For the original whitespace-sensitive syntax, use the extension `.css.sass`. For the new SCSS syntax, use the extension `.css.scss`. ## Styling with LESS ## [LESS](http://lesscss.org/) extends CSS with dynamic behavior such as variables, mixins, operations and functions. If the `less` gem is available to your application, you can use LESS to write CSS assets in Sprockets. Note that the LESS compiler is written in JavaScript and the `less` gem (on MRI) uses `therubyracer` which embeds the V8 JavaScript runtime in Ruby, while on JRuby you're going to need `therubyrhino` gem installed. To write CSS assets with LESS, use the extension `.css.less`. ## Scripting with CoffeeScript ## [CoffeeScript](http://jashkenas.github.com/coffee-script/) is a language that compiles to the "good parts" of JavaScript, featuring a cleaner syntax with array comprehensions, classes, and function binding. If the `coffee-script` gem is available to your application, you can use CoffeeScript to write JavaScript assets in Sprockets. Note that the CoffeeScript compiler is written in JavaScript, and you will need an [ExecJS](https://github.com/sstephenson/execjs)-supported runtime on your system to invoke it. To write JavaScript assets with CoffeeScript, use the extension `.js.coffee`. ## JavaScript Templating with EJS and Eco ## Sprockets supports *JavaScript templates* for client-side rendering of strings or markup. JavaScript templates have the special format extension `.jst` and are compiled to JavaScript functions. When loaded, a JavaScript template function can be accessed by its logical path as a property on the global `JST` object. Invoke a template function to render the template as a string. The resulting string can then be inserted into the DOM.
Hello, <%= name %>!
// application.js //= require templates/hello $("#hello").html(JST["templates/hello"]({ name: "Sam" })); Sprockets supports two JavaScript template languages: [EJS](https://github.com/sstephenson/ruby-ejs), for embedded JavaScript, and [Eco](https://github.com/sstephenson/ruby-eco), for embedded CoffeeScript. Both languages use the familiar `<% … %>` syntax for embedding logic in templates. If the `ejs` gem is available to your application, you can use EJS templates in Sprockets. EJS templates have the extension `.jst.ejs`. If the `eco` gem is available to your application, you can use [Eco templates](https://github.com/sstephenson/eco) in Sprockets. Eco templates have the extension `.jst.eco`. Note that the `eco` gem depends on the CoffeeScript compiler, so the same caveats apply as outlined above for the CoffeeScript engine. ## Invoking Ruby with ERB ## Sprockets provides an ERB engine for preprocessing assets using embedded Ruby code. Append `.erb` to a CSS or JavaScript asset's filename to enable the ERB engine. **Note**: Sprockets processes multiple engine extensions in order from right to left, so you can use multiple engines with a single asset. For example, to have a CoffeeScript asset that is first preprocessed with ERB, use the extension `.js.coffee.erb`. Ruby code embedded in an asset is evaluated in the context of a `Sprockets::Context` instance for the given asset. Common uses for ERB include: - embedding another asset as a Base64-encoded `data:` URI with the `asset_data_uri` helper - inserting the URL to another asset, such as with the `asset_path` helper provided by the Sprockets Rails plugin - embedding other application resources, such as a localized string database, in a JavaScript asset via JSON - embedding version constants loaded from another file See the [Helper Methods](#FIXME) section for more information about interacting with `Sprockets::Context` instances via ERB. ### String Interpolation Syntax ### If you need access to Ruby from an asset but cannot use ERB's `<% … %>` syntax, Sprockets also supports Ruby string interpolation syntax (`#{ … }`) with the `.str` engine extension. # Managing and Bundling Dependencies # You can create *asset bundles* -- ordered concatenations of asset source files -- by specifying dependencies in a special comment syntax at the top of each source file. Sprockets reads these comments, called *directives*, and processes them to recursively build a dependency graph. When you request an asset with dependencies, the dependencies will be included in order at the top of the file. ## The Directive Processor ## Sprockets runs the *directive processor* on each CSS and JavaScript source file. The directive processor scans for comment lines beginning with `=` in comment blocks at the top of the file. //= require jquery //= require jquery-ui //= require backbone //= require_tree . The first word immediately following `=` specifies the directive name. Any words following the directive name are treated as arguments. Arguments may be placed in single or double quotes if they contain spaces, similar to commands in the Unix shell. **Note**: Non-directive comment lines will be preserved in the final asset, but directive comments are stripped after processing. Sprockets will not look for directives in comment blocks that occur after the first line of code. ### Supported Comment Types ### The directive processor understands comment blocks in three formats: /* Multi-line comment blocks (CSS, SCSS, JavaScript) *= require foo */ // Single-line comment blocks (SCSS, JavaScript) //= require foo # Single-line comment blocks (CoffeeScript) #= require foo ## Sprockets Directives ## You can use the following directives to declare dependencies in asset source files. For directives that take a *path* argument, you may specify either a logical path or a relative path. Relative paths begin with `./` and reference files relative to the location of the current file. ### The `require` Directive ### `require` *path* inserts the contents of the asset source file specified by *path*. If the file is required multiple times, it will appear in the bundle only once. ### The `include` Directive ### `include` *path* works like `require`, but inserts the contents of the specified source file even if it has already been included or required. ### The `require_directory` Directive ### `require_directory` *path* requires all source files of the same format in the directory specified by *path*. Files are required in alphabetical order. ### The `require_tree` Directive ### `require_tree` *path* works like `require_directory`, but operates recursively to require all files in all subdirectories of the directory specified by *path*. ### The `require_self` Directive ### `require_self` tells Sprockets to insert the body of the current source file before any subsequent `require` or `include` directives. ### The `depend_on` Directive ### `depend_on` *path* declares a dependency on the given *path* without including it in the bundle. This is useful when you need to expire an asset's cache in response to a change in another file. ### The `depend_on_asset` Directive ### `depend_on_asset` *path* works like `depend_on`, but operates recursively reading the the file and following the directives found. ### The `stub` Directive ### `stub` *path* 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`. # Development # ## Contributing ## The Sprockets source code is [hosted on GitHub](https://github.com/sstephenson/sprockets). You can check out a copy of the latest code using Git: $ git clone https://github.com/sstephenson/sprockets.git If you've found a bug or have a question, please open an issue on the [Sprockets issue tracker](https://github.com/sstephenson/sprockets/issues). Or, clone the Sprockets repository, write a failing test case, fix the bug and submit a pull request. ## Version History ## **2.10.0** (May 24, 2013) * Support for `bower.json` **2.9.3** (April 20, 2013) * Fixed sass caching bug **2.9.2** (April 8, 2013) * Improve file freshness check performance * Directive processor encoding fixes **2.9.1** (April 6, 2013) * Support for Uglifier 2.x **2.9.0** (February 25, 2013) * Write out gzipped variants of bundled assets. **2.8.2** (December 10, 2012) * Fixed top level Sass constant references * Fixed manifest logger when environment is disabled **2.8.1** (October 31, 2012) * Fixed Sass importer bug **2.8.0** (October 16, 2012) * Allow manifest location to be seperated from output directory * Pass logical path and absolute path to each_logical_path iterator **2.7.0** (October 10, 2012) * Added --css-compressor and --js-compressor command line flags * Added css/js compressor shorthand * Change default manifest.json filename to be a randomized manifest-16HEXBYTES.json * Allow nil environment to be passed to manifest * Allow manifest instance to be set on rake task **2.6.0** (September 19, 2012) * Added bower component.json require support **2.5.0** (September 4, 2012) * Fixed Ruby 2.0 RegExp warning * Provide stubbed implementation of context *_path helpers * Add SassCompressor **2.4.5** (July 10, 2012) * Tweaked some logger levels **2.4.4** (July 2, 2012) * Canonicalize logical path extensions * Check absolute paths passed to depend_on **2.4.3** (May 16, 2012) * Exposed :sprockets in sass options * Include dependency paths in asset mtime **2.4.2** (May 7, 2012) * Fixed MultiJson feature detect **2.4.1** (April 26, 2012) * Fixed MultiJson API change * Fixed gzip mtime **2.4.0** (March 27, 2012) * Added global path registry * Added global processor registry **2.3.2** (March 26, 2012) * Fix Context#logical_path with dots **2.3.1** (February 11, 2012) * Added bytesize to manifest * Added Asset#bytesize alias * Security: Check path for forbidden access after unescaping **2.3.0** (January 16, 2012) * Added special Sass importer that automatically tracks any `@import`ed files. **2.2.0** (January 10, 2012) * Added `sprockets` command line utility. * Added rake/sprocketstask. * Added json manifest log of compiled assets. * Added `stub` directive that allows you to exclude files from the bundle. * Added per environment external encoding (Environment#default_external_encoding). Defaults to UTF-8. Fixes issues where LANG is not set correctly and Rubys default external is set to ASCII. **2.1.2** (November 20, 2011) * Disabled If-Modified-Since server checks. Fixes some browser caching issues when serving the asset body only. If-None-Match caching is sufficent. **2.1.1** (November 18, 2011) * Fix windows absolute path check bug. **2.1.0** (November 11, 2011) * Directive comment lines are now turned into empty lines instead of removed. This way line numbers in CoffeeScript syntax errors are correct. * Performance and caching bug fixes. **2.0.3** (October 17, 2011) * Detect format extensions from right to left. * Make JST namespace configurable. **2.0.2** (October 4, 2011) * Fixed loading stale cache from bundler gems. **2.0.1** (September 30, 2011) * Fixed bug with fingerprinting file names with multiple dots. * Decode URIs as default internal. * Fix symlinked asset directories. **2.0.0** (August 29, 2011) * Initial public release. # License # Copyright © 2011 Sam Stephenson <> Copyright © 2011 Joshua Peek <> Sprockets is distributed under an MIT-style license. See LICENSE for details.