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
@@ -5,16 +5,6 @@ require "rack/test"
5
5
  describe "Rake::Pipeline Middleware" do
6
6
  include Rack::Test::Methods
7
7
 
8
- ConcatFilter = Rake::Pipeline::SpecHelpers::Filters::ConcatFilter
9
-
10
- class StripAssertsFilter < Rake::Pipeline::Filter
11
- def generate_output(inputs, output)
12
- inputs.each do |input|
13
- output.write input.read.gsub(%r{^\s*assert\(.*\)\s*;?\s*$}m, '')
14
- end
15
- end
16
- end
17
-
18
8
  inputs = {
19
9
  "app/javascripts/jquery.js" => "var jQuery = {};\n",
20
10
 
@@ -25,8 +15,8 @@ describe "Rake::Pipeline Middleware" do
25
15
  HERE
26
16
 
27
17
  "app/index.html" => "<html>HI</html>",
28
- "app/javascripts/index.html" => "<html>JAVASCRIPT</html>",
29
- "app/empty_dir" => nil
18
+ "app/javascripts/index.html" => "<html>JAVASCRIPT</html>",
19
+ "app/empty_dir" => nil
30
20
  }
31
21
 
32
22
  expected_output = <<-HERE.gsub(/^ {4}/, '')
@@ -36,114 +26,172 @@ describe "Rake::Pipeline Middleware" do
36
26
  SC.hi = function() { console.log("hi"); };
37
27
  HERE
38
28
 
39
- app = middleware = pipeline = nil
40
-
41
- before do
42
- app = lambda { |env| [404, {}, ['not found']] }
29
+ assetfile_source = <<-HERE.gsub(/^ {4}/, '')
30
+ require "#{tmp}/../support/spec_helpers/filters"
31
+ output "public"
43
32
 
44
- pipeline = Rake::Pipeline.build do
45
- input tmp, "app/**/*"
33
+ map "/dynamic-request.js" do
34
+ [200, { "Content-Type" => "text/plain" }, ["I am dynamic!"]]
35
+ end
46
36
 
37
+ input "#{tmp}", "app/**/*" do
47
38
  match "*.js" do
48
- filter(ConcatFilter) { "javascripts/application.js" }
49
- filter(StripAssertsFilter) { |input| input }
39
+ concat "javascripts/application.js"
40
+ filter(Rake::Pipeline::SpecHelpers::Filters::StripAssertsFilter) { |input| input }
50
41
  end
51
42
 
52
43
  # copy the rest
53
- filter(ConcatFilter) { |input| input.sub(/^app\//, '') }
54
-
55
- output "public"
44
+ concat { |input| input.sub(%r|^app/|, '') }
56
45
  end
46
+ HERE
57
47
 
58
- middleware = Rake::Pipeline::Middleware.new(app, pipeline)
48
+ modified_assetfile_source = <<-HERE.gsub(/^ {4}/, '')
49
+ require "#{tmp}/../support/spec_helpers/filters"
50
+ output "public"
59
51
 
60
- inputs.each do |name, string|
61
- path = File.join(tmp, name)
62
- if string
63
- mkdir_p File.dirname(path)
64
- File.open(path, "w") { |file| file.write(string) }
65
- else
66
- mkdir_p path
52
+ input "#{tmp}", "app/**/*" do
53
+ match "*.js" do
54
+ concat { "javascripts/app.js" }
55
+ filter(Rake::Pipeline::SpecHelpers::Filters::StripAssertsFilter) { |input| input }
67
56
  end
57
+
58
+ # copy the rest
59
+ concat { |input| input.sub(%r|^app/|, '') }
68
60
  end
61
+ HERE
69
62
 
70
- get "/javascripts/application.js"
71
- end
63
+ app = middleware = nil
72
64
 
73
65
  let(:app) { middleware }
74
66
 
75
- it "returns files relative to the output directory" do
76
- last_response.should be_ok
67
+ before do
68
+ assetfile_path = File.join(tmp, "Assetfile")
69
+ File.open(assetfile_path, "w") { |file| file.write(assetfile_source) }
77
70
 
78
- last_response.body.should == expected_output
79
- last_response.headers["Content-Type"].should == "application/javascript"
71
+ app = lambda { |env| [404, {}, ['not found']] }
72
+ middleware = Rake::Pipeline::Middleware.new(app, assetfile_path)
80
73
  end
81
74
 
82
- it "updates the output when files change" do
83
- age_existing_files
75
+ describe "dynamic requests" do
76
+ it "returns the value from the given block for paths that have been mapped" do
77
+ get "/dynamic-request.js"
84
78
 
85
- File.open(File.join(tmp, "app/javascripts/jquery.js"), "w") do |file|
86
- file.write "var jQuery = {};\njQuery.trim = function() {};\n"
79
+ last_response.should be_ok
80
+ last_response.headers["Content-Type"].should == "text/plain"
81
+ last_response.body.should == "I am dynamic!"
87
82
  end
83
+ end
84
+
85
+ describe "static requests" do
86
+ before do
87
+ inputs.each do |name, string|
88
+ path = File.join(tmp, name)
89
+ if string
90
+ mkdir_p File.dirname(path)
91
+ File.open(path, "w") { |file| file.write(string) }
92
+ else
93
+ mkdir_p path
94
+ end
95
+ end
96
+
97
+ get "/javascripts/application.js"
98
+ end
99
+
100
+ it "returns files relative to the output directory" do
101
+ last_response.should be_ok
102
+
103
+ last_response.body.should == expected_output
104
+ last_response.headers["Content-Type"].should == "application/javascript"
105
+ end
106
+
107
+ it "updates the output when files change" do
108
+ age_existing_files
88
109
 
89
- expected = <<-HERE.gsub(/^ {6}/, '')
110
+ File.open(File.join(tmp, "app/javascripts/jquery.js"), "w") do |file|
111
+ file.write "var jQuery = {};\njQuery.trim = function() {};\n"
112
+ end
113
+
114
+ expected = <<-HERE.gsub(/^ {6}/, '')
90
115
  var jQuery = {};
91
116
  jQuery.trim = function() {};
92
117
  var SC = {};
93
118
 
94
119
  SC.hi = function() { console.log("hi"); };
95
- HERE
120
+ HERE
96
121
 
97
- get "/javascripts/application.js"
122
+ get "/javascripts/application.js"
98
123
 
99
- last_response.body.should == expected
100
- last_response.headers["Content-Type"].should == "application/javascript"
101
- end
124
+ last_response.body.should == expected
125
+ last_response.headers["Content-Type"].should == "application/javascript"
126
+ end
102
127
 
103
- it "updates the output when new files are added" do
104
- age_existing_files
128
+ it "updates the output when new files are added" do
129
+ age_existing_files
105
130
 
106
- File.open(File.join(tmp, "app/javascripts/history.js"), "w") do |file|
107
- file.write "var History = {};\n"
108
- end
131
+ File.open(File.join(tmp, "app/javascripts/history.js"), "w") do |file|
132
+ file.write "var History = {};\n"
133
+ end
109
134
 
110
- expected = <<-HERE.gsub(/^ {6}/, '')
135
+ expected = <<-HERE.gsub(/^ {6}/, '')
111
136
  var History = {};
112
137
  var jQuery = {};
113
138
  var SC = {};
114
139
 
115
140
  SC.hi = function() { console.log("hi"); };
116
- HERE
141
+ HERE
117
142
 
118
- get "/javascripts/application.js"
143
+ get "/javascripts/application.js"
119
144
 
120
- last_response.body.should == expected
121
- last_response.headers["Content-Type"].should == "application/javascript"
122
- end
145
+ last_response.body.should == expected
146
+ last_response.headers["Content-Type"].should == "application/javascript"
147
+ end
123
148
 
124
- it "returns index.html for directories" do
125
- get "/"
149
+ it "returns index.html for directories" do
150
+ get "/"
126
151
 
127
- last_response.body.should == "<html>HI</html>"
128
- last_response.headers["Content-Type"].should == "text/html"
152
+ last_response.body.should == "<html>HI</html>"
153
+ last_response.headers["Content-Type"].should == "text/html"
129
154
 
130
- get "/javascripts"
155
+ get "/javascripts"
131
156
 
132
- last_response.body.should == "<html>JAVASCRIPT</html>"
133
- last_response.headers["Content-Type"].should == "text/html"
134
- end
157
+ last_response.body.should == "<html>JAVASCRIPT</html>"
158
+ last_response.headers["Content-Type"].should == "text/html"
159
+ end
135
160
 
136
- it "ignores directories without index.html" do
137
- get "/empty_dir"
161
+ it "ignores directories without index.html" do
162
+ get "/empty_dir"
138
163
 
139
- last_response.body.should == "not found"
140
- last_response.status.should == 404
141
- end
164
+ last_response.body.should == "not found"
165
+ last_response.status.should == 404
166
+ end
167
+
168
+ it "falls back to the app" do
169
+ get "/zomg.notfound"
170
+
171
+ last_response.body.should == "not found"
172
+ last_response.status.should == 404
173
+ end
142
174
 
143
- it "falls back to the app" do
144
- get "/zomg.notfound"
175
+ it "recreates the pipeline when the Assetfile changes" do
176
+ get "/javascripts/app.js"
177
+ last_response.body.should == "not found"
178
+ last_response.status.should == 404
145
179
 
146
- last_response.body.should == "not found"
147
- last_response.status.should == 404
180
+ File.open(File.join(tmp, "Assetfile"), "w") do |file|
181
+ file.write(modified_assetfile_source)
182
+ end
183
+
184
+ expected = <<-HERE.gsub(/^ {6}/, '')
185
+ var jQuery = {};
186
+ var SC = {};
187
+
188
+ SC.hi = function() { console.log("hi"); };
189
+ HERE
190
+
191
+ get "/javascripts/app.js"
192
+
193
+ last_response.body.should == expected
194
+ last_response.headers["Content-Type"].should == "application/javascript"
195
+ end
148
196
  end
149
197
  end
@@ -0,0 +1,39 @@
1
+ describe "OrderingConcatFilter" do
2
+ MemoryFileWrapper = Rake::Pipeline::SpecHelpers::MemoryFileWrapper
3
+
4
+ let(:input_files) {
5
+ [
6
+ MemoryFileWrapper.new("/path/to/input", "first.txt", "UTF-8", "FIRST"),
7
+ MemoryFileWrapper.new("/path/to/input", "second.txt", "UTF-8", "SECOND"),
8
+ MemoryFileWrapper.new("/path/to/input", "last.txt", "UTF-8", "LAST")
9
+ ]
10
+ }
11
+
12
+ let(:output_files) {
13
+ [
14
+ MemoryFileWrapper.new("/path/to/output", "all.txt", "BINARY")
15
+ ]
16
+ }
17
+
18
+ let(:output_file) {
19
+ MemoryFileWrapper.files["/path/to/output/all.txt"]
20
+ }
21
+
22
+ def make_filter(ordering)
23
+ filter = Rake::Pipeline::OrderingConcatFilter.new(ordering, "all.txt")
24
+ filter.file_wrapper_class = MemoryFileWrapper
25
+ filter.input_files = input_files
26
+ filter.output_root = "/path/to/output"
27
+ filter.rake_application = Rake::Application.new
28
+ filter.generate_rake_tasks.each(&:invoke)
29
+ filter
30
+ end
31
+
32
+ it "generates output" do
33
+ filter = make_filter(["first.txt", "second.txt"])
34
+
35
+ filter.output_files.should == output_files
36
+ output_file.body.should == "FIRSTSECONDLAST"
37
+ output_file.encoding.should == "BINARY"
38
+ end
39
+ end
@@ -5,17 +5,11 @@ describe "Rake::Pipeline" do
5
5
  let(:pipeline) { Rake::Pipeline.new }
6
6
 
7
7
  it "accepts a input root" do
8
- pipeline.input_root = "app/assets"
9
- pipeline.input_root.should == File.expand_path("app/assets")
8
+ pipeline.add_input "app/assets"
9
+ pipeline.inputs["app/assets"].should == '**/*'
10
10
  end
11
11
 
12
12
  it "raises an exception on #relative_input_files if input_files are not provided" do
13
- pipeline.input_root = "app/assets"
14
- lambda { pipeline.input_files }.should raise_error(Rake::Pipeline::Error)
15
- end
16
-
17
- it "raises an exception on #relative_input_files if input_root is not provided" do
18
- pipeline.input_glob = "app/assets/javascripts/**/*.js"
19
13
  lambda { pipeline.input_files }.should raise_error(Rake::Pipeline::Error)
20
14
  end
21
15
 
@@ -39,15 +33,73 @@ describe "Rake::Pipeline" do
39
33
  pipeline.rake_application.should == Rake.application
40
34
  end
41
35
 
42
- it "can have filters added to it" do
43
- filter = ConcatFilter.new
44
- pipeline.add_filter filter
36
+ describe ".build" do
37
+ it "evaluates a block against a new Pipeline" do
38
+ pipeline = Rake::Pipeline.build do
39
+ output "octopus"
40
+ end
41
+ pipeline.should be_kind_of(Rake::Pipeline)
42
+ pipeline.output_root.should == File.expand_path("octopus")
43
+ end
45
44
  end
46
45
 
47
- shared_examples_for "when working with input" do
48
- def input_file(path, root=File.join(tmp, "app/assets"))
49
- Rake::Pipeline::FileWrapper.new root, path
46
+ describe "#build" do
47
+ it "evaluates a block against an existing Pipeline" do
48
+ pipeline = Rake::Pipeline.new
49
+ pipeline.output_root = "octopus"
50
+
51
+ pipeline.build do
52
+ output "squid"
53
+ end
54
+ pipeline.output_root.should == File.expand_path("squid")
55
+ end
56
+ end
57
+
58
+ describe "the constructor" do
59
+ it "accepts an :inputs option" do
60
+ pipeline = Rake::Pipeline.new(:inputs => {"app/assets" => "**/*"})
61
+ pipeline.inputs.should == {"app/assets" => "**/*"}
62
+ end
63
+
64
+ it "accepts a :tmpdir option" do
65
+ pipeline = Rake::Pipeline.new(:tmpdir => "tmp")
66
+ pipeline.tmpdir.should == "tmp"
67
+ end
68
+
69
+ it "accepts an :output_root option" do
70
+ pipeline = Rake::Pipeline.new(:output_root => "public")
71
+ pipeline.output_root.should == File.expand_path("public")
72
+ end
73
+
74
+ it "accepts a :rake_application option" do
75
+ app = Rake::Application.new
76
+ pipeline = Rake::Pipeline.new(:rake_application => app)
77
+ pipeline.rake_application.should == app
78
+ end
79
+ end
80
+
81
+ describe "adding filters" do
82
+ let(:filter) { ConcatFilter.new }
83
+
84
+ it "can have filters added to it" do
85
+ pipeline.add_filter filter
86
+ end
87
+
88
+ it "sets an added filter's rake_application" do
89
+ app = Rake::Application.new
90
+ pipeline.rake_application = app
91
+ pipeline.add_filter filter
92
+ filter.rake_application.should == app
93
+ end
94
+
95
+ it "sets an added filter's pipeline" do
96
+ pipeline.add_filter filter
97
+ filter.pipeline.should == pipeline
50
98
  end
99
+ end
100
+
101
+ shared_examples_for "when working with input" do
102
+ include Rake::Pipeline::SpecHelpers::InputHelpers
51
103
 
52
104
  let(:files) do
53
105
  %w(javascripts/jquery.js javascripts/sproutcore.js).map do |filename|
@@ -55,18 +107,14 @@ describe "Rake::Pipeline" do
55
107
  end
56
108
  end
57
109
 
110
+ def setup_roots
111
+ pipeline.add_input "app/assets"
112
+ end
113
+
58
114
  before do
59
115
  Rake.application = Rake::Application.new
60
-
61
- files.each do |file|
62
- mkdir_p File.dirname(file.fullpath)
63
-
64
- File.open(file.fullpath, "w") do |file|
65
- file.write "// This is #{file.path}\n"
66
- end
67
- end
68
-
69
- pipeline.input_root = "app/assets"
116
+ create_files(files)
117
+ setup_roots
70
118
  setup_input(pipeline)
71
119
  pipeline.output_root = "public"
72
120
  end
@@ -111,8 +159,11 @@ describe "Rake::Pipeline" do
111
159
 
112
160
  deps = task.prerequisites
113
161
  deps.size.should == 2
114
- deps[0].should == File.join(pipeline.input_root, "javascripts/jquery.js")
115
- deps[1].should == File.join(pipeline.input_root, "javascripts/sproutcore.js")
162
+
163
+ root = File.expand_path(pipeline.inputs.keys.first)
164
+
165
+ deps[0].should == File.join(root, "javascripts/jquery.js")
166
+ deps[1].should == File.join(root, "javascripts/sproutcore.js")
116
167
 
117
168
  Rake.application.tasks.size.should == 3
118
169
  end
@@ -128,19 +179,29 @@ describe "Rake::Pipeline" do
128
179
  end
129
180
  end
130
181
 
131
- describe "using a glob for input files" do
182
+ describe "when using multiple input roots" do
132
183
  it_behaves_like "when working with input"
133
184
 
185
+ def setup_roots
186
+ pipeline.add_input File.join(tmp, 'tmp1', "app/assets"), '**/*.js'
187
+ pipeline.add_input File.join(tmp, 'tmp2', "app/assets"), '**/*.css'
188
+ end
189
+
134
190
  def setup_input(pipeline)
135
- pipeline.input_glob = "javascripts/**/*.js"
136
191
  end
137
- end
138
192
 
139
- describe "using a glob containing directories for input files" do
140
- it_behaves_like "when working with input"
193
+ let(:files) do
194
+ f = []
141
195
 
142
- def setup_input(pipeline)
143
- pipeline.input_glob = "**/*"
196
+ %w(javascripts/jquery.js javascripts/sproutcore.js).map do |filename|
197
+ f << input_file(filename, File.join(tmp, 'tmp1', "app/assets"))
198
+ end
199
+
200
+ %w(stylesheets/jquery.css stylesheets/sproutcore.css).map do |filename|
201
+ f << input_file(filename, File.join(tmp, 'tmp2', "app/assets"))
202
+ end
203
+
204
+ f
144
205
  end
145
206
  end
146
207
 
@@ -149,7 +210,7 @@ describe "Rake::Pipeline" do
149
210
 
150
211
  def setup_input(pipeline)
151
212
  Dir.chdir("app/assets") do
152
- files = Dir["javascripts/**/*.js"]
213
+ files = Dir["javascripts/**/*.js"].sort
153
214
  wrappers = files.map do |file|
154
215
  Rake::Pipeline::FileWrapper.new(File.join(tmp, "app/assets"), file)
155
216
  end