class Sprockets::DirectiveProcessor
env.register_processor(‘text/css’, MyProcessor)
Then inject your own preprocessor:
env.unregister_processor(‘application/javascript’, Sprockets::DirectiveProcessor)
env.unregister_processor(‘text/css’, Sprockets::DirectiveProcessor)
To remove the processor entirely:
‘Environment#processors` includes `DirectiveProcessor` by default.
custom directives or invent your own directive syntax.
the processor to do whatever you’d like. You could add your own
coupled to Sprockets. This makes it possible to disable or modify
The Processor is implemented as a ‘Tilt::Template` and is loosely
*/
*= require “baz”
/* CSS
#= require “bar”
# CoffeeScript
//= require “foo”
// JavaScript
then the directive name, then any arguments.
A directive comment starts with a comment prefix, followed by an “=”,
directive comments in a source file.
The `DirectiveProcessor` is responsible for parsing and evaluating
def compat?
def compat? @compat end
def constants
constants.yml was present. This is only available if
Sprockets 1.x allowed for constant interpolation if a
def constants if compat? pathname = Pathname.new(context.root_path).join("constants.yml") stat(pathname) ? YAML.load_file(pathname) : {} else {} end end
def directives
[[1, "require", "foo"], [2, "require", "bar"]]
arguments.
directive name as the second element, followed by any
is an Array with the line number as the first element, the
Returns an Array of directive structures. Each structure
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
def each_entry(root, &block)
def each_entry(root, &block) context.environment.each_entry(root, &block) end
def entries(path)
def entries(path) context.environment.entries(path) end
def evaluate(context, locals, &block)
access the environment and append to the bundle. See `Context`
`context` is a `Context` instance with methods that allow you to
Implemented for Tilt#render.
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
def prepare
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
def process_compat_directive
//= compat
file in both Sprockets 1 and 2.
Makes it possible to use the same JavaScript source
Enable Sprockets 1.x compat mode.
def process_compat_directive @compat = true end
def process_depend_on_asset_directive(path)
//= depend_on_asset "bar.js"
Unlike `depend_on`, the path must be a requirable asset.
source file.
invalid the asset dependency will invalidate the cache our the
This is used for caching purposes. Any changes that would
it.
Allows you to state a dependency on an asset without including
def process_depend_on_asset_directive(path) context.depend_on_asset(path) end
def process_depend_on_directive(path)
//= depend_on "foo.png"
in contents from another file.
This is useful if you are using ERB and File.read to pull
source file.
the dependency file will invalidate the cache of the
This is used for caching purposes. Any changes made to
including it.
Allows you to state a dependency on a file without
def process_depend_on_directive(path) context.depend_on(path) end
def process_directives
env.register_processor('text/css', DirectiveProcessor)
env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
Replace the current processor on the environment with your own:
end
end
end
require(filename)
Dir["#{pathname.dirname}/#{glob}"].sort.each do |filename|
def process_require_glob_directive
class DirectiveProcessor < Sprockets::DirectiveProcessor
`process_require_glob_directive`.
`Sprockets::DirectiveProcessor`, then add a method called
To implement a custom directive called `require_glob`, subclass
processor.
automatically be available. This makes it easy to extend the
Any directive method matching `process_*_directive` will
Gathers comment directives in the source and processes them.
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_include_directive(path)
//= include "header"
has been required.
inserts the contents of the dependency even if it already
The `include` directive works similar to `require` but
def process_include_directive(path) pathname = context.resolve(path) context.depend_on_asset(pathname) included_pathnames << pathname end
def process_provide_directive(path)
Mutating the path when an asset is being built is
`provide` is stubbed out for Sprockets 1.x compat.
def process_provide_directive(path) end
def process_require_directive(path)
//= require "./bar"
path:
Relative paths work too. Use a leading `./` to denote a relative
//= require "foo"
assumes you are requiring another ".js".
Extensions are optional. If your source file is ".js", it
//= require "foo.js"
`require` works with files in the environment path:
and ensures its only loaded once before the source file.
It provides a way to declare a dependency on a file in your path
The `require` directive functions similar to Ruby's own `require`.
def process_require_directive(path) if @compat if path =~ /<([^>]+)>/ path = $1 else path = "./#{path}" unless relative?(path) end end context.require_asset(path) end
def process_require_directory_directive(path = ".")
//= require_directory "./javascripts"
nested directories.
directory. It's similar to `path/*` since it does not follow
`require_directory` requires all the files inside a single
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
def process_require_self_directive
*/
*= require_tree .
*= require_self
/*= require "reset"
before other dependencies are loaded.
index file to contain global styles that need to be defined
directives. Useful in CSS files, where it's common for the
inserted before any subsequent `require` or `include`
`require_self` causes the body of the current file to be
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
def process_require_tree_directive(path = ".")
//= require_tree "./public"
Its glob equivalent is `path/**/*`.
`require_tree` requires all the nested files in a directory.
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
def process_source
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
def process_stub_directive(path)
//= stub "jquery"
can't be brought back by any other `require`.
be part of the bundle. Once stubbed, it is blacklisted and
The `path` must be a valid asset and may or may not already
Allows dependency to be excluded from the asset bundle.
def process_stub_directive(path) context.stub_asset(path) end
def processed_header
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
def processed_source
def processed_source @processed_source ||= processed_header + body end
def relative?(path)
def relative?(path) path =~ /^\.($|\.?\/)/ end
def stat(path)
def stat(path) context.environment.stat(path) end