rake-pipeline 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data/.travis.yml +12 -0
  2. data/Gemfile +1 -0
  3. data/README.markdown +1 -1
  4. data/README.yard +61 -32
  5. data/Rakefile +9 -0
  6. data/bin/rakep +1 -24
  7. data/lib/generators/rake/pipeline/install/install_generator.rb +70 -0
  8. data/lib/rake-pipeline.rb +117 -53
  9. data/lib/rake-pipeline/cli.rb +56 -0
  10. data/lib/rake-pipeline/dsl.rb +3 -140
  11. data/lib/rake-pipeline/dsl/pipeline_dsl.rb +168 -0
  12. data/lib/rake-pipeline/dsl/project_dsl.rb +108 -0
  13. data/lib/rake-pipeline/dynamic_file_task.rb +188 -0
  14. data/lib/rake-pipeline/file_wrapper.rb +1 -1
  15. data/lib/rake-pipeline/filter.rb +45 -15
  16. data/lib/rake-pipeline/filters.rb +3 -1
  17. data/lib/rake-pipeline/filters/{concat.rb → concat_filter.rb} +0 -0
  18. data/lib/rake-pipeline/filters/ordering_concat_filter.rb +38 -0
  19. data/lib/rake-pipeline/filters/pipeline_finalizing_filter.rb +19 -0
  20. data/lib/rake-pipeline/graph.rb +178 -0
  21. data/lib/rake-pipeline/manifest.rb +63 -0
  22. data/lib/rake-pipeline/manifest_entry.rb +34 -0
  23. data/lib/rake-pipeline/matcher.rb +65 -30
  24. data/lib/rake-pipeline/middleware.rb +15 -12
  25. data/lib/rake-pipeline/precompile.rake +8 -0
  26. data/lib/rake-pipeline/project.rb +280 -0
  27. data/lib/rake-pipeline/railtie.rb +16 -1
  28. data/lib/rake-pipeline/server.rb +15 -0
  29. data/lib/rake-pipeline/version.rb +2 -2
  30. data/rake-pipeline.gemspec +2 -0
  31. data/spec/cli_spec.rb +71 -0
  32. data/spec/concat_filter_spec.rb +1 -27
  33. data/spec/{dsl_spec.rb → dsl/pipeline_dsl_spec.rb} +32 -18
  34. data/spec/dsl/project_dsl_spec.rb +41 -0
  35. data/spec/dynamic_file_task_spec.rb +111 -0
  36. data/spec/encoding_spec.rb +6 -8
  37. data/spec/file_wrapper_spec.rb +19 -2
  38. data/spec/filter_spec.rb +120 -22
  39. data/spec/graph_spec.rb +56 -0
  40. data/spec/manifest_entry_spec.rb +51 -0
  41. data/spec/manifest_spec.rb +67 -0
  42. data/spec/matcher_spec.rb +35 -2
  43. data/spec/middleware_spec.rb +123 -75
  44. data/spec/ordering_concat_filter_spec.rb +39 -0
  45. data/spec/pipeline_spec.rb +95 -34
  46. data/spec/project_spec.rb +293 -0
  47. data/spec/rake_acceptance_spec.rb +307 -67
  48. data/spec/rake_tasks_spec.rb +21 -0
  49. data/spec/spec_helper.rb +11 -48
  50. data/spec/support/spec_helpers/file_utils.rb +35 -0
  51. data/spec/support/spec_helpers/filters.rb +16 -0
  52. data/spec/support/spec_helpers/input_helpers.rb +23 -0
  53. data/spec/support/spec_helpers/memory_file_wrapper.rb +31 -0
  54. data/tools/perfs +107 -0
  55. metadata +100 -12
@@ -33,8 +33,25 @@ describe "Rake::Pipeline::FileWrapper" do
33
33
  file.should_not == other
34
34
  end
35
35
 
36
- it "has a fullpath" do
37
- file.fullpath.should == jquery
36
+ describe "#fullpath" do
37
+ it "returns the complete path to the file" do
38
+ file.fullpath.should == jquery
39
+ end
40
+
41
+ it "raises an error if its root is not a root path" do
42
+ file.root = 'root'
43
+ lambda { file.fullpath }.should raise_error
44
+ end
45
+
46
+ it "doesn't raise an error with a unix root path" do
47
+ file.root = '/root'
48
+ lambda { file.fullpath }.should_not raise_error
49
+ end
50
+
51
+ it "doesn't raise an error with a windows root path" do
52
+ file.root = 'c:\root'
53
+ lambda { file.fullpath }.should_not raise_error
54
+ end
38
55
  end
39
56
 
40
57
  it "it knows it doesn't exist when the file doesn't exist" do
@@ -1,4 +1,9 @@
1
1
  describe "Rake::Pipeline::Filter" do
2
+ after do
3
+ # Clean out all defined tasks after each test runs
4
+ Rake.application = Rake::Application.new
5
+ end
6
+
2
7
  def input_file(path, file_root=input_root)
3
8
  Rake::Pipeline::FileWrapper.new(file_root, path)
4
9
  end
@@ -11,7 +16,7 @@ describe "Rake::Pipeline::Filter" do
11
16
  let(:input_root) { File.join(tmp, "app/assets") }
12
17
  let(:output_root) { File.join(tmp, "filter1/app/assets") }
13
18
  let(:input_files) do
14
- %w(jquery.js jquery-ui.js sproutcore.js).map { |f| input_file(f) }
19
+ %w(jquery.js jquery-ui.js ember.js).map { |f| input_file(f) }
15
20
  end
16
21
 
17
22
  it "accepts a series of FileWrapper objects for the input" do
@@ -47,6 +52,13 @@ describe "Rake::Pipeline::Filter" do
47
52
  new_filter.output_name_generator.should == conversion
48
53
  end
49
54
 
55
+ it "knows about its containing pipeline" do
56
+ pipeline = Rake::Pipeline.new
57
+ filter = Rake::Pipeline::Filter.new
58
+ pipeline.add_filter(filter)
59
+ filter.pipeline.should == pipeline
60
+ end
61
+
50
62
  describe "using the output_name proc to converting the input names into a hash" do
51
63
  before do
52
64
  filter.output_root = output_root
@@ -64,6 +76,20 @@ describe "Rake::Pipeline::Filter" do
64
76
  filter.output_files.should == [output_file("application.js")]
65
77
  end
66
78
 
79
+ it "with more than one output per input" do
80
+ output_name_generator = proc { |input| [ input, "application.js" ] }
81
+ filter.output_name_generator = output_name_generator
82
+ outputs = filter.outputs
83
+
84
+ outputs = input_files.inject({}) do |hash, input|
85
+ hash.merge output_file(input.path) => [ input ]
86
+ end
87
+
88
+ filter.outputs.should == { output_file("application.js") => input_files }.merge(outputs)
89
+ filter.output_files.sort.should ==
90
+ ([ output_file("application.js") ] + input_files.map { |f| output_file(f.path) }).sort
91
+ end
92
+
67
93
  it "with a 1:1 output_name proc" do
68
94
  output_name_generator = proc { |input| input }
69
95
  filter.output_name_generator = output_name_generator
@@ -80,27 +106,28 @@ describe "Rake::Pipeline::Filter" do
80
106
  filter.output_name_generator = output_name_generator
81
107
  outputs = filter.outputs
82
108
 
83
- outputs.keys.should == [output_file("jquery.js"), output_file("sproutcore.js")]
84
- outputs.values.should == [[input_file("jquery.js"), input_file("jquery-ui.js")], [input_file("sproutcore.js")]]
109
+ outputs.keys.sort.should == [output_file("ember.js"), output_file("jquery.js")]
110
+ outputs.values.sort.should == [[input_file("ember.js")], [input_file("jquery.js"), input_file("jquery-ui.js")]]
85
111
 
86
- filter.output_files.should == [output_file("jquery.js"), output_file("sproutcore.js")]
112
+ filter.output_files.should == [output_file("jquery.js"), output_file("ember.js")]
87
113
  end
88
114
  end
89
115
 
90
116
  describe "generates rake tasks" do
91
- class TestFilter < Rake::Pipeline::Filter
92
- attr_accessor :generate_output_block
117
+ let(:filter_class) {
118
+ Class.new(Rake::Pipeline::Filter) do
119
+ attr_accessor :generate_output_block
93
120
 
94
- def generate_output(inputs, output)
95
- generate_output_block.call(inputs, output)
121
+ def generate_output(inputs, output)
122
+ generate_output_block.call(inputs, output)
123
+ end
96
124
  end
97
- end
98
-
99
- let(:filter) { TestFilter.new }
125
+ }
126
+ let(:filter) { filter_class.new }
100
127
  let(:input_root) { File.join(tmp, "app/assets") }
101
128
  let(:output_root) { File.join(tmp, "filter1/app/assets") }
102
129
  let(:input_files) do
103
- %w(jquery.js jquery-ui.js sproutcore.js).map do |file|
130
+ %w(jquery.js jquery-ui.js ember.js).map do |file|
104
131
  input_file("javascripts/#{file}")
105
132
  end
106
133
  end
@@ -112,11 +139,11 @@ describe "Rake::Pipeline::Filter" do
112
139
  end
113
140
 
114
141
  def output_task(path, app=Rake.application)
115
- app.define_task(Rake::FileTask, File.join(output_root, path))
142
+ app.define_task(Rake::Pipeline::DynamicFileTask, File.join(output_root, path))
116
143
  end
117
144
 
118
145
  def input_task(path)
119
- Rake::FileTask.define_task(File.join(input_root, path))
146
+ Rake::Pipeline::DynamicFileTask.define_task(File.join(input_root, path))
120
147
  end
121
148
 
122
149
  it "does not generate Rake tasks onto Rake.application if an alternate application is supplied" do
@@ -148,13 +175,21 @@ describe "Rake::Pipeline::Filter" do
148
175
  filter.generate_rake_tasks
149
176
  tasks = filter.rake_tasks
150
177
  tasks.should == [output_task("javascripts/application.js")]
151
- tasks[0].prerequisites.should == input_files.map { |i| i.fullpath }
152
178
 
153
179
  tasks.each(&:invoke)
154
180
 
155
181
  filter_runs.should == 1
156
182
  end
157
183
 
184
+ it "with an output_name proc that takes two arguments" do
185
+ filter.output_name_generator = proc { |path, input|
186
+ input.path.upcase
187
+ }
188
+
189
+ filter.outputs.keys.map(&:path).should include('JAVASCRIPTS/JQUERY.JS')
190
+ filter.output_files.map(&:path).should include('JAVASCRIPTS/JQUERY.JS')
191
+ end
192
+
158
193
  it "with a 1:1 output_name proc" do
159
194
  filter_runs = 0
160
195
 
@@ -187,9 +222,9 @@ describe "Rake::Pipeline::Filter" do
187
222
  if output.path == "javascripts/jquery.js"
188
223
  inputs.should == [input_file("javascripts/jquery.js"), input_file("javascripts/jquery-ui.js")]
189
224
  output.should == output_file("javascripts/jquery.js")
190
- elsif output.path == "javascripts/sproutcore.js"
191
- inputs.should == [input_file("javascripts/sproutcore.js")]
192
- output.should == output_file("javascripts/sproutcore.js")
225
+ elsif output.path == "javascripts/ember.js"
226
+ inputs.should == [input_file("javascripts/ember.js")]
227
+ output.should == output_file("javascripts/ember.js")
193
228
  else
194
229
  flunk
195
230
  end
@@ -199,18 +234,81 @@ describe "Rake::Pipeline::Filter" do
199
234
 
200
235
  filter.generate_rake_tasks
201
236
  tasks = filter.rake_tasks
202
- tasks.sort.should == [output_task("javascripts/jquery.js"), output_task("javascripts/sproutcore.js")].sort
237
+ tasks.sort.should == [output_task("javascripts/jquery.js"), output_task("javascripts/ember.js")].sort
203
238
 
204
239
  tasks.sort[0].prerequisites.should == [
205
- File.join(input_root, "javascripts/jquery.js"),
206
- File.join(input_root, "javascripts/jquery-ui.js")
240
+ File.join(input_root, "javascripts/ember.js"),
207
241
  ]
208
242
 
209
- tasks.sort[1].prerequisites.should == [File.join(input_root, "javascripts/sproutcore.js")]
243
+ tasks.sort[1].prerequisites.should == [
244
+ File.join(input_root, "javascripts/jquery.js"),
245
+ File.join(input_root, "javascripts/jquery-ui.js"),
246
+ ]
210
247
 
211
248
  tasks.each(&:invoke)
212
249
 
213
250
  filter_runs.should == 2
214
251
  end
215
252
  end
253
+
254
+ describe "a filter with additional_dependencies" do
255
+ let(:include_filter) {
256
+ Class.new(Rake::Pipeline::ConcatFilter) do
257
+ def additional_dependencies(input)
258
+ includes(input)
259
+ end
260
+
261
+ def includes(input)
262
+ input.read.scan(/^#include \"(.*)\"$/).map(&:first).map do |inc|
263
+ File.join(input.root, inc)
264
+ end
265
+ end
266
+
267
+ def generate_output(inputs, output)
268
+ out = ""
269
+ inputs.each do |input|
270
+ includes(input).each { |inc| out += File.read(inc) }
271
+ out += input.read
272
+ output.write out
273
+ end
274
+ end
275
+ end
276
+ }
277
+
278
+ def write_input_file(filename, contents='', root=input_root)
279
+ mkdir_p root
280
+ File.open(File.join(root, filename), 'w') { |f| f.puts contents }
281
+ input_file(filename)
282
+ end
283
+
284
+ def invoke_filter(filter)
285
+ mkdir_p output_root
286
+ filter.output_root = output_root
287
+ filter.input_files = input_files
288
+ tasks = filter.generate_rake_tasks
289
+ tasks.each(&:invoke)
290
+ tasks
291
+ end
292
+
293
+ let!(:main) { write_input_file('main', '#include "header"') }
294
+ let!(:header) { write_input_file('header', 'header') }
295
+ let!(:main_output) { output_file('main') }
296
+ let(:input_files) { [main] }
297
+ let(:filter) { include_filter.new }
298
+
299
+ it "creates its output files" do
300
+ invoke_filter(filter)
301
+ main_output.exists?.should be_true
302
+ main_output.read.should == %[header\n#include "header"\n]
303
+ end
304
+
305
+ it "rebuilds the main file when a dynamic dependency changes" do
306
+ invoke_filter(filter)
307
+ File.open(header.fullpath, 'w') { |f| f.puts 'PEANUTS' }
308
+ filter.rake_tasks.each { |t| t.recursively_reenable(Rake.application) }
309
+ invoke_filter(filter)
310
+ main_output.read.should == %[PEANUTS\n#include "header"\n]
311
+ end
312
+ end
216
313
  end
314
+
@@ -0,0 +1,56 @@
1
+ describe "Rake::Pipeline::Graph" do
2
+ Graph = Rake::Pipeline::Graph
3
+ Node = Rake::Pipeline::Graph::Node
4
+
5
+ before do
6
+ @graph = Graph.new
7
+ end
8
+
9
+ it "has nodes" do
10
+ @graph.nodes.should == []
11
+ end
12
+
13
+ it "can add nodes" do
14
+ @graph.add("foo")
15
+ @graph["foo"].should == Node.new("foo")
16
+ end
17
+
18
+ it "can link nodes" do
19
+ @graph.add("foo")
20
+ @graph.add("bar")
21
+ @graph.nodes.should == [Node.new("foo"), Node.new("bar")]
22
+ @graph.link("foo", "bar")
23
+ @graph["foo"].children.map(&:name).should == ["bar"]
24
+ @graph["bar"].parents.map(&:name).should == ["foo"]
25
+ end
26
+
27
+ it "can unlink nodes" do
28
+ @graph.add("foo")
29
+ @graph.add("bar")
30
+ @graph.link("foo", "bar")
31
+ @graph.unlink("foo", "bar")
32
+ @graph["foo"].children.should == Set[]
33
+ @graph["bar"].parents.should == Set[]
34
+ end
35
+
36
+ it "can remove nodes" do
37
+ @graph.add("foo")
38
+ @graph.add("bar")
39
+ @graph.nodes.should == [Node.new("foo"), Node.new("bar")]
40
+ @graph.link("foo", "bar")
41
+ @graph.remove("foo")
42
+ @graph.nodes.should == [Node.new("bar")]
43
+ @graph["bar"].children.should == Set[]
44
+ @graph["bar"].parents.should == Set[]
45
+ end
46
+
47
+ it "can add metadata to nodes" do
48
+ @graph.add("foo", :meta => 1)
49
+ @graph.add("bar")
50
+ @graph["bar"].metadata[:meta] = 2
51
+
52
+ @graph["foo"].metadata.should == { :meta => 1 }
53
+ @graph["bar"].metadata.should == { :meta => 2 }
54
+ end
55
+ end
56
+
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rake::Pipeline::ManifestEntry do
4
+ before do
5
+ # ensure all our date/time conversions happen in UTC.
6
+ ENV['TZ'] = 'UTC'
7
+ end
8
+
9
+ let(:hash) do
10
+ {
11
+ "deps" => {
12
+ "file.h" => "2001-01-01 00:00:00 +0000",
13
+ "other.h" => "2001-01-01 00:00:00 +0000"
14
+ },
15
+ "mtime" => "2000-01-01 00:00:00 +0000"
16
+ }
17
+ end
18
+
19
+ subject {
20
+ Rake::Pipeline::ManifestEntry.from_hash(hash)
21
+ }
22
+
23
+ describe "#from_hash" do
24
+ it "creates a new ManifestEntry from a hash" do
25
+ subject.should be_kind_of described_class
26
+ end
27
+
28
+ it "parses mtime value into a Time" do
29
+ subject.mtime.should == Time.utc(2000)
30
+ end
31
+
32
+ it "parses each time from the deps value into a Time" do
33
+ subject.deps.each do |file, mtime|
34
+ mtime.should == Time.utc(2001)
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#as_json" do
40
+ it "returns a hash representation of the entry for converting to json" do
41
+ subject.as_json.should == {
42
+ :deps => {
43
+ "file.h" => Time.utc(2001),
44
+ "other.h" => Time.utc(2001),
45
+ },
46
+ :mtime => Time.utc(2000)
47
+ }
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rake::Pipeline::Manifest do
4
+ before do
5
+ # ensure all our date/time conversions happen in UTC.
6
+ ENV['TZ'] = 'UTC'
7
+ end
8
+
9
+ let(:manifest_file) { "#{tmp}/manifest.json" }
10
+
11
+ let(:json) { <<-JSON }
12
+ {
13
+ "file.h": {
14
+ "deps": {
15
+ "other.h": "2000-01-01 00:00:00 +0000"
16
+ },
17
+ "mtime":"2000-01-01 00:00:00 +0000"
18
+ }
19
+ }
20
+ JSON
21
+
22
+ let(:entries_hash) do
23
+ {
24
+ "file.h" => Rake::Pipeline::ManifestEntry.from_hash({
25
+ "deps" => {
26
+ "other.h" => "2000-01-01 00:00:00 +0000"
27
+ },
28
+ "mtime" => "2000-01-01 00:00:00 +0000"
29
+ })
30
+ }
31
+ end
32
+
33
+ subject do
34
+ Rake::Pipeline::Manifest.new(manifest_file)
35
+ end
36
+
37
+ describe "#write_manifest" do
38
+ before do
39
+ subject.entries = entries_hash
40
+ rm_rf manifest_file
41
+ end
42
+
43
+ it "writes a manifest json file to disk" do
44
+ subject.write_manifest
45
+
46
+ File.exist?(manifest_file).should be_true
47
+ JSON.parse(File.read(manifest_file)).should == JSON.parse(json)
48
+ end
49
+
50
+ it "writes nothing if it's empty" do
51
+ subject.entries = {}
52
+ subject.write_manifest
53
+ File.exist?(manifest_file).should be_false
54
+ end
55
+ end
56
+
57
+ describe "#read_manifest" do
58
+ before do
59
+ File.open(manifest_file, 'w') { |f| f.puts json }
60
+ end
61
+
62
+ it "reads a manifest json file from disk" do
63
+ subject.read_manifest
64
+ subject.entries.should == entries_hash
65
+ end
66
+ end
67
+ end
@@ -25,6 +25,12 @@ describe "a matcher" do
25
25
  @matcher.glob.should == "*.js"
26
26
  end
27
27
 
28
+ it "knows about its containing pipeline" do
29
+ pipeline = Rake::Pipeline.new
30
+ pipeline.add_filter @matcher
31
+ @matcher.pipeline.should == pipeline
32
+ end
33
+
28
34
  it "only processes files matching the matcher" do
29
35
  @matcher.glob = "*.js"
30
36
  @matcher.output_root = "tmp1"
@@ -61,19 +67,22 @@ describe "a matcher" do
61
67
 
62
68
  it "understands */* style globs" do
63
69
  @matcher.input_files << file_wrapper("javascripts/backbone.js")
70
+ @matcher.input_files << file_wrapper("something/javascripts/backbone.js")
64
71
 
65
72
  should_match_glob "*/*.js", [
66
73
  file_wrapper("javascripts/backbone.js", :encoding => "BINARY", :root => File.join(tmp, "tmp1")),
67
74
  file_wrapper("jquery.js"),
68
75
  file_wrapper("sproutcore.js"),
69
- file_wrapper("sproutcore.css")
76
+ file_wrapper("sproutcore.css"),
77
+ file_wrapper("something/javascripts/backbone.js")
70
78
  ]
71
79
 
72
80
  should_match_glob "proutcore*.js", [
73
81
  file_wrapper("jquery.js"),
74
82
  file_wrapper("sproutcore.js"),
75
83
  file_wrapper("sproutcore.css"),
76
- file_wrapper("javascripts/backbone.js")
84
+ file_wrapper("javascripts/backbone.js"),
85
+ file_wrapper("something/javascripts/backbone.js")
77
86
  ]
78
87
  end
79
88
 
@@ -102,4 +111,28 @@ describe "a matcher" do
102
111
  file_wrapper("javascripts/backbone.js")
103
112
  ]
104
113
  end
114
+
115
+ it "accepts Regexp as glob" do
116
+ regexp = /application\.erb/
117
+ @matcher.glob = regexp
118
+
119
+ @matcher.glob.should == regexp
120
+ end
121
+
122
+ it "underestands regexp globs" do
123
+ regexp = /^*(?<!\.coffee)\.js$/i
124
+
125
+ @matcher.input_files << file_wrapper("application.coffee.js")
126
+ @matcher.input_files << file_wrapper("application.engine.js")
127
+
128
+ output_root = File.join(tmp, "tmp1")
129
+
130
+ should_match_glob regexp, [
131
+ file_wrapper("jquery.js", :encoding => "BINARY", :root => output_root),
132
+ file_wrapper("sproutcore.js", :encoding => "BINARY", :root => output_root),
133
+ file_wrapper("application.engine.js", :encoding => "BINARY", :root => output_root),
134
+ file_wrapper("sproutcore.css"),
135
+ file_wrapper("application.coffee.js")
136
+ ]
137
+ end
105
138
  end