rake-pipeline 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.yardopts +2 -0
- data/Gemfile +10 -0
- data/LICENSE +20 -0
- data/README.markdown +4 -0
- data/README.yard +149 -0
- data/Rakefile +12 -0
- data/bin/rakep +27 -0
- data/lib/rake-pipeline.rb +365 -0
- data/lib/rake-pipeline/dsl.rb +146 -0
- data/lib/rake-pipeline/error.rb +17 -0
- data/lib/rake-pipeline/file_wrapper.rb +173 -0
- data/lib/rake-pipeline/filter.rb +209 -0
- data/lib/rake-pipeline/filters.rb +1 -0
- data/lib/rake-pipeline/filters/concat.rb +63 -0
- data/lib/rake-pipeline/matcher.rb +106 -0
- data/lib/rake-pipeline/middleware.rb +70 -0
- data/lib/rake-pipeline/rails_plugin.rb +8 -0
- data/lib/rake-pipeline/railtie.rb +17 -0
- data/lib/rake-pipeline/version.rb +6 -0
- data/rails/init.rb +2 -0
- data/rake-pipeline.gemspec +22 -0
- data/spec/concat_filter_spec.rb +60 -0
- data/spec/dsl_spec.rb +86 -0
- data/spec/encoding_spec.rb +106 -0
- data/spec/file_wrapper_spec.rb +105 -0
- data/spec/filter_spec.rb +216 -0
- data/spec/matcher_spec.rb +105 -0
- data/spec/middleware_spec.rb +149 -0
- data/spec/pipeline_spec.rb +160 -0
- data/spec/rake_acceptance_spec.rb +240 -0
- data/spec/spec_helper.rb +74 -0
- metadata +123 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-cfs -r spec_helper.rb
|
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (C) 2011 by LivingSocial, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.markdown
ADDED
data/README.yard
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
= Rake::Pipeline
|
2
|
+
|
3
|
+
Rake::Pipeline is a system for packaging assets for deployment to the
|
4
|
+
web. It uses Rake under the hood for dependency management and updating
|
5
|
+
output files based on input changes.
|
6
|
+
|
7
|
+
= Usage
|
8
|
+
|
9
|
+
The easiest way to use Rake::Pipeline is via a +Assetfile+ file in the
|
10
|
+
root of your project.
|
11
|
+
|
12
|
+
A sample +Assetfile+ looks like this:
|
13
|
+
|
14
|
+
!!!ruby
|
15
|
+
input "assets"
|
16
|
+
output "public"
|
17
|
+
|
18
|
+
# this block will take all JS inputs, wrap them in a closure,
|
19
|
+
# add some additional metadata, and concatenate them all into
|
20
|
+
# application.scripts.js.
|
21
|
+
match "*.js" do
|
22
|
+
filter ClosureWrapper
|
23
|
+
filter DataWrapper
|
24
|
+
filter Rake::Pipeline::ConcatFilter, "application.scripts.js"
|
25
|
+
end
|
26
|
+
|
27
|
+
# this block will take all HTML and CSS inputs, convert them
|
28
|
+
# into JavaScript
|
29
|
+
match "*/*.{html,css}" do
|
30
|
+
filter DataWrapper
|
31
|
+
filter Rake::Pipeline::ConcatFilter, "application.assets.js"
|
32
|
+
end
|
33
|
+
|
34
|
+
match "*.js" do
|
35
|
+
filter Rake::Pipeline::ConcatFilter, "application.js"
|
36
|
+
end
|
37
|
+
|
38
|
+
# copy any unprocessed files over to the output directory
|
39
|
+
filter Rake::Pipeline::ConcatFilter
|
40
|
+
|
41
|
+
The available options are:
|
42
|
+
|
43
|
+
* {Rake::Pipeline::DSL#input input}: the directory containing your input files
|
44
|
+
* {Rake::Pipeline::DSL#output output}: the directory to place your output files
|
45
|
+
like to process
|
46
|
+
* if you do not specify a block, the files will be
|
47
|
+
copied over directly.
|
48
|
+
|
49
|
+
= Filters
|
50
|
+
|
51
|
+
A filter is a simple class that inherits from
|
52
|
+
{Rake::Pipeline::Filter}. A filter must implement a single
|
53
|
+
method, called +generate_output+, which takes
|
54
|
+
two parameters: a list of input files and the output file.
|
55
|
+
|
56
|
+
Both the input and output files are {Rake::Pipeline::FileWrapper} objects.
|
57
|
+
The most important methods on a {Rake::Pipeline::FileWrapper FileWrapper} are:
|
58
|
+
|
59
|
+
* {Rake::Pipeline::FileWrapper#path path}: the path of the file, relative to its input root
|
60
|
+
* {Rake::Pipeline::FileWrapper#read read}: read the contents of the file
|
61
|
+
* {Rake::Pipeline::FileWrapper#write write(string)}: write a String to the file
|
62
|
+
|
63
|
+
For example, a simple concatenation filter would look like:
|
64
|
+
|
65
|
+
!!!ruby
|
66
|
+
class ConcatFilter < Rake::Pipeline::Filter
|
67
|
+
def generate_output(inputs, output)
|
68
|
+
inputs.each do |input|
|
69
|
+
output.write input.read
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
If you had a series of input files like:
|
75
|
+
|
76
|
+
* +app/javascripts/one.js+
|
77
|
+
* +app/javascripts/two.js+
|
78
|
+
* +app/javascripts/three.js+
|
79
|
+
|
80
|
+
and you specified the +ConcatFilter+ in your
|
81
|
+
+AssetFile+ like:
|
82
|
+
|
83
|
+
!!!ruby
|
84
|
+
filter ConcatFilter, "application.js"
|
85
|
+
|
86
|
+
The filter would receive a single call to
|
87
|
+
+generate_output+ with an Array of {Rake::Pipeline::FileWrapper FileWrapper}s
|
88
|
+
representing each of the three files, and a {Rake::Pipeline::FileWrapper FileWrapper}
|
89
|
+
representing +application.js+.
|
90
|
+
|
91
|
+
== Binary Data
|
92
|
+
|
93
|
+
If your filter is operating on binary data, like images,
|
94
|
+
rather than textual data, like source code, you can specify
|
95
|
+
that in your filter:
|
96
|
+
|
97
|
+
!!!ruby
|
98
|
+
class ConcatFilter < Rake::Pipeline::Filter
|
99
|
+
processes_binary_files
|
100
|
+
|
101
|
+
def generate_output(inputs, output)
|
102
|
+
inputs.each do |input|
|
103
|
+
output.write input.read
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
This will stop `Rake::Pipeline` from trying to interpret the
|
109
|
+
input files as `UTF-8`, which obviously will not work on
|
110
|
+
binary data.
|
111
|
+
|
112
|
+
= Built-In Filters
|
113
|
+
|
114
|
+
At the current time, +Rake::Pipeline+ comes with a single built-in
|
115
|
+
filter: {Rake::Pipeline::ConcatFilter}. Its implementation is
|
116
|
+
the same as the `ConcatFilter` shown above.
|
117
|
+
|
118
|
+
= Preview Server
|
119
|
+
|
120
|
+
To start up the preview server, run +rakep+. This will start up
|
121
|
+
a server that automatically recompiles files for you on the fly
|
122
|
+
and serves up the files you need.
|
123
|
+
|
124
|
+
This should allow you to have a single index.html file pointing
|
125
|
+
at the same files in both development and production.
|
126
|
+
|
127
|
+
= Compiling Assets
|
128
|
+
|
129
|
+
To compile all assets before deployment, simply run:
|
130
|
+
|
131
|
+
$ rakep build
|
132
|
+
|
133
|
+
= Encodings
|
134
|
+
|
135
|
+
If a filter does not specify that it processes binary files,
|
136
|
+
+Rake::Pipeline+ will open all inputs and outputs as +UTF-8+.
|
137
|
+
|
138
|
+
This means that if you have files encoded in other encodings,
|
139
|
+
like +Latin-1+, +Rake::Pipeline+ will raise an exception. In
|
140
|
+
this situation, you need to open the offending file in your
|
141
|
+
text editor and re-save it as +UTF-8+.
|
142
|
+
|
143
|
+
= Public Release Requirement
|
144
|
+
|
145
|
+
Before publicly releasing this code, we need to properly support
|
146
|
+
encodings other than UTF-8. That means using the
|
147
|
+
+default_external+ instead of hardcoding to UTF-8 and
|
148
|
+
providing a mechanism for specifying the encoding of a file using
|
149
|
+
a magic comment.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
directory "doc"
|
5
|
+
|
6
|
+
task :docs => Dir["lib/**"] do
|
7
|
+
sh "devbin/yard doc --readme README.yard --hide-void-return"
|
8
|
+
end
|
9
|
+
|
10
|
+
task :graph => ["doc", :docs] do
|
11
|
+
sh "devbin/yard graph --dependencies | dot -Tpng -o doc/arch.png"
|
12
|
+
end
|
data/bin/rakep
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rake-pipeline"
|
4
|
+
require "rake-pipeline/middleware"
|
5
|
+
require "rack/server"
|
6
|
+
|
7
|
+
module Rake
|
8
|
+
class Pipeline
|
9
|
+
class Server < Rack::Server
|
10
|
+
def app
|
11
|
+
not_found = proc { [404, { "Content-Type" => "text/plain" }, ["not found"]] }
|
12
|
+
config = "Assetfile"
|
13
|
+
|
14
|
+
Middleware.new(not_found, config)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if ARGV[0] == "build"
|
21
|
+
config = "Assetfile"
|
22
|
+
pipeline_source = File.read(config)
|
23
|
+
pipeline = Rake::Pipeline.class_eval "build do\n#{pipeline_source}\nend", config, 1
|
24
|
+
pipeline.invoke
|
25
|
+
else
|
26
|
+
Rake::Pipeline::Server.new.start
|
27
|
+
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
require "rake-pipeline/file_wrapper"
|
2
|
+
require "rake-pipeline/filter"
|
3
|
+
require "rake-pipeline/filters"
|
4
|
+
require "rake-pipeline/dsl"
|
5
|
+
require "rake-pipeline/matcher"
|
6
|
+
require "rake-pipeline/error"
|
7
|
+
|
8
|
+
if defined?(Rails::Railtie)
|
9
|
+
require "rake-pipeline/railtie"
|
10
|
+
elsif defined?(Rails)
|
11
|
+
require "rake-pipeline/rails_plugin"
|
12
|
+
end
|
13
|
+
|
14
|
+
require "thread"
|
15
|
+
|
16
|
+
# Use the Rake namespace
|
17
|
+
module Rake
|
18
|
+
# Override Rake::Task to support recursively re-enabling
|
19
|
+
# a task and its dependencies.
|
20
|
+
class Task
|
21
|
+
|
22
|
+
# @param [Rake::Application] app a Rake Application
|
23
|
+
# @return [void]
|
24
|
+
def recursively_reenable(app)
|
25
|
+
reenable
|
26
|
+
|
27
|
+
prerequisites.each do |dep|
|
28
|
+
app[dep].recursively_reenable(app)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Override Rake::FileTask to make it sortable
|
34
|
+
class FileTask
|
35
|
+
# implement Ruby protocol for sorting
|
36
|
+
#
|
37
|
+
# @return [Fixnum]
|
38
|
+
def <=>(other)
|
39
|
+
[name, prerequisites] <=> [other.name, other.prerequisites]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# A Pipeline is responsible for taking a directory of input
|
44
|
+
# files, applying a number of filters to the inputs, and
|
45
|
+
# outputting them into an output directory.
|
46
|
+
#
|
47
|
+
# The normal way to build and configure a pipeline is by
|
48
|
+
# using {.build}. Inside the block passed to {.build}, all
|
49
|
+
# methods of {DSL} are available.
|
50
|
+
#
|
51
|
+
# @see DSL Rake::Pipeline::DSL for information on the methods
|
52
|
+
# available inside the block.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# !!!ruby
|
56
|
+
# Rake::Pipeline.build do
|
57
|
+
# # process all js, css and html files in app/assets
|
58
|
+
# input "app/assets", "**/*.{js,coffee,css,scss,html}"
|
59
|
+
#
|
60
|
+
# # processed files should be outputted to public
|
61
|
+
# output "public"
|
62
|
+
#
|
63
|
+
# # process all coffee files
|
64
|
+
# match "*.coffee" do
|
65
|
+
# # compile all CoffeeScript files. the output file
|
66
|
+
# # for the compilation should be the input name
|
67
|
+
# # with the .coffee extension replaced with .js
|
68
|
+
# filter(CoffeeCompiler) do |input|
|
69
|
+
# input.sub(/\.coffee$/, '.js')
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # specify filters for js files. this includes the
|
74
|
+
# # output of the previous step, which converted
|
75
|
+
# # coffee files to js files
|
76
|
+
# match "*.js" do
|
77
|
+
# # first, wrap all JS files in a custom filter
|
78
|
+
# filter ClosureFilter
|
79
|
+
# # then, concatenate all JS files into a single file
|
80
|
+
# filter Rake::Pipeline::ConcatFilter, "application.js"
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # specify filters for css and scss files
|
84
|
+
# match "*.{css,scss}" do
|
85
|
+
# # compile CSS and SCSS files using the SCSS
|
86
|
+
# # compiler. if an input file has the extension
|
87
|
+
# # scss, replace it with css
|
88
|
+
# filter(ScssCompiler) do |input|
|
89
|
+
# input.sub(/\.scss$/, 'css')
|
90
|
+
# end
|
91
|
+
# # then, concatenate all CSS files into a single file
|
92
|
+
# filter Rake::Pipeline::ConcatFilter, "application.css"
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# # the remaining files not specified by a matcher (the
|
96
|
+
# # HTML files) are simply copied over.
|
97
|
+
#
|
98
|
+
# # you can also specify filters here that will apply to
|
99
|
+
# # all processed files (application.js and application.css)
|
100
|
+
# # up until this point, as well as the HTML files.
|
101
|
+
# end
|
102
|
+
class Pipeline
|
103
|
+
# @return [String] a glob representing the input files
|
104
|
+
attr_accessor :input_glob
|
105
|
+
|
106
|
+
# @return [String] the directory path for the input files.
|
107
|
+
attr_reader :input_root
|
108
|
+
|
109
|
+
# @return [String] the directory path for the output files.
|
110
|
+
attr_reader :output_root
|
111
|
+
|
112
|
+
# @return [String] the directory path for temporary files.
|
113
|
+
attr_reader :tmpdir
|
114
|
+
|
115
|
+
# @return [Array] an Array of Rake::Task objects. This
|
116
|
+
# property is populated by the #generate_rake_tasks
|
117
|
+
# method.
|
118
|
+
attr_reader :rake_tasks
|
119
|
+
|
120
|
+
# @return [String] a list of files that will be outputted
|
121
|
+
# to the output directory when the pipeline is invoked
|
122
|
+
attr_reader :output_files
|
123
|
+
|
124
|
+
# @return [Array] this pipeline's filters.
|
125
|
+
attr_reader :filters
|
126
|
+
|
127
|
+
attr_writer :input_files
|
128
|
+
|
129
|
+
def initialize
|
130
|
+
@filters = []
|
131
|
+
@tmpdir = "tmp"
|
132
|
+
@mutex = Mutex.new
|
133
|
+
end
|
134
|
+
|
135
|
+
# Build a new pipeline taking a block. The block will
|
136
|
+
# be evaluated by the Rake::Pipeline::DSL class.
|
137
|
+
#
|
138
|
+
# @see Rake::Pipeline::Filter Rake::Pipeline::Filter
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# Rake::Pipeline.build do
|
142
|
+
# input "app/assets"
|
143
|
+
# output "public"
|
144
|
+
#
|
145
|
+
# filter Rake::Pipeline::ConcatFilter, "app.js"
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# @see DSL the Rake::Pipeline::DSL documentation.
|
149
|
+
# All instance methods of DSL are available inside
|
150
|
+
# the build block.
|
151
|
+
#
|
152
|
+
# @return [Rake::Pipeline] the newly configured pipeline
|
153
|
+
def self.build(&block)
|
154
|
+
pipeline = new
|
155
|
+
DSL.evaluate(pipeline, &block) if block
|
156
|
+
pipeline
|
157
|
+
end
|
158
|
+
|
159
|
+
@@tmp_id = 0
|
160
|
+
|
161
|
+
# Copy the current pipeline's attributes over.
|
162
|
+
#
|
163
|
+
# @param [Class] target_class the class to create a new
|
164
|
+
# instance of. Defaults to the class of the current
|
165
|
+
# pipeline. Is overridden in {Matcher}
|
166
|
+
# @param [Proc] block a block to pass to the {DSL DSL}
|
167
|
+
# @return [Pipeline] the new pipeline
|
168
|
+
# @api private
|
169
|
+
def copy(target_class=self.class, &block)
|
170
|
+
pipeline = target_class.build(&block)
|
171
|
+
pipeline.input_root = input_root
|
172
|
+
pipeline.tmpdir = tmpdir
|
173
|
+
pipeline.rake_application = rake_application
|
174
|
+
pipeline
|
175
|
+
end
|
176
|
+
|
177
|
+
# Set the input root of this pipeline and expand its path.
|
178
|
+
#
|
179
|
+
# @param [String] root this pipeline's input root
|
180
|
+
def input_root=(root)
|
181
|
+
@input_root = File.expand_path(root)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Set the output root of this pipeline and expand its path.
|
185
|
+
#
|
186
|
+
# @param [String] root this pipeline's output root
|
187
|
+
def output_root=(root)
|
188
|
+
@output_root = File.expand_path(root)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Set the temporary directory for this pipeline and expand its path.
|
192
|
+
#
|
193
|
+
# @param [String] root this pipeline's temporary directory
|
194
|
+
def tmpdir=(dir)
|
195
|
+
@tmpdir = File.expand_path(dir)
|
196
|
+
end
|
197
|
+
|
198
|
+
# If you specify a glob for #input_glob, this method will
|
199
|
+
# calculate the input files for the directory. If you supply
|
200
|
+
# input_files directly, this method will simply return the
|
201
|
+
# input_files you supplied.
|
202
|
+
#
|
203
|
+
# @return [Array<FileWrapper>] An Array of file wrappers
|
204
|
+
# that represent the inputs for the current pipeline.
|
205
|
+
def input_files
|
206
|
+
return @input_files if @input_files
|
207
|
+
|
208
|
+
assert_input_provided
|
209
|
+
|
210
|
+
expanded_root = File.expand_path(input_root)
|
211
|
+
files = Dir[File.join(expanded_root, input_glob)].select { |f| File.file?(f) }
|
212
|
+
|
213
|
+
files.map do |file|
|
214
|
+
relative_path = file.sub(%r{^#{Regexp.escape(expanded_root)}/}, '')
|
215
|
+
FileWrapper.new(expanded_root, relative_path)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# for Pipelines, this is every file, but it may be overridden
|
220
|
+
# by subclasses
|
221
|
+
alias eligible_input_files input_files
|
222
|
+
|
223
|
+
# @return [Rake::Application] The Rake::Application to install
|
224
|
+
# rake tasks onto. Defaults to Rake.application
|
225
|
+
def rake_application
|
226
|
+
@rake_application || Rake.application
|
227
|
+
end
|
228
|
+
|
229
|
+
# Set the rake_application on the pipeline and apply it to filters.
|
230
|
+
#
|
231
|
+
# @return [void]
|
232
|
+
def rake_application=(rake_application)
|
233
|
+
@rake_application = rake_application
|
234
|
+
@filters.each { |filter| filter.rake_application = rake_application }
|
235
|
+
@rake_tasks = nil
|
236
|
+
end
|
237
|
+
|
238
|
+
# Add one or more filters to the current pipeline.
|
239
|
+
#
|
240
|
+
# @param [Array<Filter>] filters a list of filters
|
241
|
+
# @return [void]
|
242
|
+
def add_filters(*filters)
|
243
|
+
filters.each { |filter| filter.rake_application = rake_application }
|
244
|
+
@filters.concat(filters)
|
245
|
+
end
|
246
|
+
alias add_filter add_filters
|
247
|
+
|
248
|
+
# Invoke the pipeline, processing the inputs into the output. If
|
249
|
+
# the pipeline has already been invoked, reinvoking will not
|
250
|
+
# pick up new input files added to the file system.
|
251
|
+
#
|
252
|
+
# @return [void]
|
253
|
+
def invoke
|
254
|
+
@mutex.synchronize do
|
255
|
+
self.rake_application = Rake::Application.new unless @rake_application
|
256
|
+
|
257
|
+
setup
|
258
|
+
|
259
|
+
@rake_tasks.each { |task| task.recursively_reenable(rake_application) }
|
260
|
+
@rake_tasks.each { |task| task.invoke }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Pick up any new files added to the inputs and process them through
|
265
|
+
# the filters. Then call #invoke.
|
266
|
+
#
|
267
|
+
# @return [void]
|
268
|
+
def invoke_clean
|
269
|
+
@rake_tasks = @rake_application = nil
|
270
|
+
invoke
|
271
|
+
end
|
272
|
+
|
273
|
+
# Set up the filters and generate rake tasks. In general, this method
|
274
|
+
# is called by invoke.
|
275
|
+
#
|
276
|
+
# @return [void]
|
277
|
+
# @api private
|
278
|
+
def setup
|
279
|
+
setup_filters
|
280
|
+
generate_rake_tasks
|
281
|
+
end
|
282
|
+
|
283
|
+
# A list of the output files that invoking this pipeline will
|
284
|
+
# generate.
|
285
|
+
#
|
286
|
+
# @return [Array<FileWrapper>]
|
287
|
+
def output_files
|
288
|
+
@filters.last.output_files unless @filters.empty?
|
289
|
+
end
|
290
|
+
|
291
|
+
protected
|
292
|
+
# Generate a new temporary directory name.
|
293
|
+
#
|
294
|
+
# @return [String] a unique temporary directory name
|
295
|
+
def self.generate_tmpname
|
296
|
+
"rake-pipeline-tmp-#{@@tmp_id += 1}"
|
297
|
+
end
|
298
|
+
|
299
|
+
# Set up the filters. This will loop through all of the filters for
|
300
|
+
# the current pipeline and wire up their input_files and output_files.
|
301
|
+
#
|
302
|
+
# Because matchers implement the filter API, matchers will also be
|
303
|
+
# set up as part of this process.
|
304
|
+
#
|
305
|
+
# @return [void]
|
306
|
+
def setup_filters
|
307
|
+
last = @filters.last
|
308
|
+
|
309
|
+
@filters.inject(eligible_input_files) do |current_inputs, filter|
|
310
|
+
filter.input_files = current_inputs
|
311
|
+
|
312
|
+
# if filters are being reinvoked, they should keep their roots but
|
313
|
+
# get updated with new files.
|
314
|
+
filter.output_root ||= begin
|
315
|
+
output = if filter == last
|
316
|
+
output_root
|
317
|
+
else
|
318
|
+
generate_tmpdir
|
319
|
+
end
|
320
|
+
|
321
|
+
File.expand_path(output)
|
322
|
+
end
|
323
|
+
|
324
|
+
filter.setup_filters if filter.respond_to?(:setup_filters)
|
325
|
+
|
326
|
+
filter.output_files
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Generate a new temporary directory name under the main tmpdir.
|
331
|
+
#
|
332
|
+
# @return [void]
|
333
|
+
def generate_tmpdir
|
334
|
+
File.join(tmpdir, self.class.generate_tmpname)
|
335
|
+
end
|
336
|
+
|
337
|
+
# Generate all of the rake tasks for this pipeline.
|
338
|
+
#
|
339
|
+
# @return [void]
|
340
|
+
def generate_rake_tasks
|
341
|
+
@rake_tasks ||= begin
|
342
|
+
tasks = []
|
343
|
+
|
344
|
+
@filters.each do |filter|
|
345
|
+
# TODO: Don't generate rake tasks if we aren't
|
346
|
+
# creating a new Rake::Application
|
347
|
+
tasks = filter.generate_rake_tasks
|
348
|
+
end
|
349
|
+
|
350
|
+
tasks
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# Assert that an input root and glob were both provided.
|
355
|
+
#
|
356
|
+
# @raise Rake::Pipeline::Error if input root or glob were missing.
|
357
|
+
# @return [void]
|
358
|
+
def assert_input_provided
|
359
|
+
if !input_root || !input_glob
|
360
|
+
raise Rake::Pipeline::Error, "You cannot get input files without " \
|
361
|
+
"first providing input files and an input root"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|