rake-pipeline 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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