Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 3914452278
Fetching contributors…

Cannot retrieve contributors at this time

240 lines (213 sloc) 8.028 kb
require "rake"
module Rake
class Pipeline
# A Filter is added to a pipeline and converts input files
# into output files.
#
# Filters operate on FileWrappers, which abstract away the
# root directory of a file, providing a relative path and
# a mechanism for reading and writing.
#
#
# For instance, a filter to wrap the contents of each file
# in a JavaScript closure would look like:
#
# !!!ruby
# require "json"
#
# class ClosureFilter < Rake::Pipeline::Filter
# def generate_output(inputs, output)
# inputs.each do |input|
# output.write "(function() { #{input.read.to_json} })()"
# end
# end
# end
#
# A filter's files come from the input directory or the directory
# owned by the previous filter, but filters are insulated from
# this concern.
#
# You can call +path+ on a FileWrapper to get the file's relative
# path, or `fullpath` to get its absolute path, but you should,
# in general, not use `fullpath` but instead use methods of
# FileWrapper like `read` and `write` that abstract the details
# from you.
#
# @see ConcatFilter Rake::Pipeline::ConcatFilter for another
# example filter implementation.
#
# @abstract
class Filter
# @return [Array<FileWrapper>] an Array of FileWrappers that
# represent the inputs of this filter. The Pipeline will
# usually set this up.
attr_accessor :input_files
# @return [Proc] a block that returns the relative output
# filename for a particular input file. If the block accepts
# just one argument, it will be passed the input's path. If
# it accepts two, it will also be passed the input itself.
attr_accessor :output_name_generator
# @return [String] the root directory to write output files
# to. For the last filter in a pipeline, the pipeline will
# set this to the pipeline's output. For all other filters,
# the pipeline will create a temporary directory that it
# also uses when creating FileWrappers for the next filter's
# inputs.
attr_accessor :output_root
# @return [Array<Rake::Task>] an Array of Rake tasks created
# for this filter. Each unique output file will get a
# single task.
attr_reader :rake_tasks
# @return [Rake::Application] the Rake::Application that the
# filter should define new rake tasks on.
attr_writer :rake_application
# @return [Rake::Pipeline] the Rake::Pipeline that contains
# this filter.
attr_accessor :pipeline
attr_writer :file_wrapper_class
# @param [Proc] block a block to use as the Filter's
# {#output_name_generator}.
def initialize(&block)
block ||= proc { |input| input }
@output_name_generator = block
@input_files = []
end
# Invoke this method in a subclass of Filter to declare that
# it expects to work with BINARY data, and that data that is
# not valid UTF-8 should be allowed.
#
# @return [void]
def self.processes_binary_files
define_method(:encoding) { "BINARY" }
end
# @return [Class] the class to use as the wrapper for output
# files.
def file_wrapper_class
@file_wrapper_class ||= FileWrapper
end
# Set the input files to a list of FileWrappers. The filter
# will map these into equivalent FileWrappers with the
# filter's encoding applied.
#
# By default, a filter's encoding is +UTF-8+, unless
# it calls #processes_binary_files, which changes it to
# +BINARY+.
#
# @param [Array<FileWrapper>] a list of FileWrapper objects
def input_files=(files)
@input_files = files.map do |file|
file.with_encoding(encoding)
end
end
# A hash of output files pointing at their associated input
# files. The output names are created by applying the
# {#output_name_generator} to each input file.
#
# For exmaple, if you had the following input files:
#
# javascripts/jquery.js
# javascripts/sproutcore.js
# stylesheets/sproutcore.css
#
# And you had the following {#output_name_generator}:
#
# !!!ruby
# filter.output_name_generator = proc do |filename|
# # javascripts/jquery.js becomes:
# # ["javascripts", "jquery", "js"]
# directory, file, ext = file.split(/[\.\/]/)
#
# "#{directory}.#{ext}"
# end
#
# You would end up with the following hash:
#
# !!!ruby
# {
# #<FileWrapper path="javascripts.js" root="#{output_root}> => [
# #<FileWrapper path="javascripts/jquery.js" root="#{previous_filter.output_root}">,
# #<FileWrapper path="javascripts/sproutcore.js" root="#{previous_filter.output_root}">
# ],
# #<FileWrapper path="stylesheets.css" root="#{output_root}"> => [
# #<FileWrapper path="stylesheets/sproutcore.css" root=#{previous_filter.output_root}">
# ]
# }
#
# Each output file becomes a Rake task, which invokes the +#generate_output+
# method defined by the subclass of {Filter} with the Array of inputs and
# the output (all as {FileWrapper}s).
#
# @return [Hash{FileWrapper => Array<FileWrapper>}]
def outputs
hash = {}
input_files.each do |file|
output_wrappers(file).each do |output|
hash[output] ||= []
hash[output] << file
end
end
hash
end
# An Array of the {FileWrapper} objects that rerepresent this filter's
# output files. It is the same as +outputs.keys+.
#
# @see #outputs
# @return [Array<FileWrapper>]
def output_files
input_files.map { |file| output_wrappers(file) }.flatten.uniq
end
# The Rake::Application that the filter should define new tasks on.
#
# @return [Rake::Application]
def rake_application
@rake_application || Rake.application
end
# @param [FileWrapper] file wrapper to get paths for
# @return [Array<String>] array of file paths within additional dependencies
def additional_dependencies(input)
[]
end
# Generate the Rake tasks for the output files of this filter.
#
# @see #outputs #outputs (for information on how the output files are determined)
# @return [void]
def generate_rake_tasks
@rake_tasks = outputs.map do |output, inputs|
additional_paths = []
inputs.each do |input|
create_file_task(input.fullpath).dynamic do
additional_paths += additional_dependencies(input)
end
end
additional_paths.each { |path| create_file_task(path) }
create_file_task(output.fullpath, inputs.map(&:fullpath)) do
output.create { generate_output(inputs, output) }
end
end
end
private
# @attr_reader
def encoding
"UTF-8"
end
def create_file_task(output, deps=[], &block)
task = rake_application.define_task(Rake::Pipeline::DynamicFileTask, output => deps, &block)
if pipeline && pipeline.project
task.last_manifest = pipeline.project.last_manifest
task.manifest = pipeline.project.manifest
end
task
end
def output_wrappers(input)
output_paths(input).map do |path|
file_wrapper_class.new(output_root, path, encoding)
end
end
def output_paths(input)
args = [ input.path ]
args << input if output_name_generator.arity == 2
Array(output_name_generator.call(*args))
end
end
end
end
Jump to Line
Something went wrong with that request. Please try again.