rake-pipeline-fork 0.8.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 (75) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +12 -0
  5. data/.yardopts +2 -0
  6. data/GETTING_STARTED.md +268 -0
  7. data/Gemfile +14 -0
  8. data/LICENSE +20 -0
  9. data/README.markdown +11 -0
  10. data/README.yard +178 -0
  11. data/Rakefile +21 -0
  12. data/bin/rakep +4 -0
  13. data/examples/copying_files.md +12 -0
  14. data/examples/minifying_files.md +37 -0
  15. data/examples/modifying_pipelines.md +67 -0
  16. data/examples/multiple_pipelines.md +77 -0
  17. data/lib/generators/rake/pipeline/install/install_generator.rb +70 -0
  18. data/lib/rake-pipeline.rb +462 -0
  19. data/lib/rake-pipeline/cli.rb +56 -0
  20. data/lib/rake-pipeline/dsl.rb +9 -0
  21. data/lib/rake-pipeline/dsl/pipeline_dsl.rb +246 -0
  22. data/lib/rake-pipeline/dsl/project_dsl.rb +108 -0
  23. data/lib/rake-pipeline/dynamic_file_task.rb +194 -0
  24. data/lib/rake-pipeline/error.rb +17 -0
  25. data/lib/rake-pipeline/file_wrapper.rb +182 -0
  26. data/lib/rake-pipeline/filter.rb +249 -0
  27. data/lib/rake-pipeline/filters.rb +4 -0
  28. data/lib/rake-pipeline/filters/concat_filter.rb +63 -0
  29. data/lib/rake-pipeline/filters/gsub_filter.rb +56 -0
  30. data/lib/rake-pipeline/filters/ordering_concat_filter.rb +38 -0
  31. data/lib/rake-pipeline/filters/pipeline_finalizing_filter.rb +21 -0
  32. data/lib/rake-pipeline/graph.rb +178 -0
  33. data/lib/rake-pipeline/manifest.rb +86 -0
  34. data/lib/rake-pipeline/manifest_entry.rb +34 -0
  35. data/lib/rake-pipeline/matcher.rb +141 -0
  36. data/lib/rake-pipeline/middleware.rb +72 -0
  37. data/lib/rake-pipeline/precompile.rake +8 -0
  38. data/lib/rake-pipeline/project.rb +335 -0
  39. data/lib/rake-pipeline/rails_plugin.rb +10 -0
  40. data/lib/rake-pipeline/railtie.rb +34 -0
  41. data/lib/rake-pipeline/reject_matcher.rb +29 -0
  42. data/lib/rake-pipeline/server.rb +15 -0
  43. data/lib/rake-pipeline/sorted_pipeline.rb +19 -0
  44. data/lib/rake-pipeline/version.rb +6 -0
  45. data/rails/init.rb +2 -0
  46. data/rake-pipeline.gemspec +24 -0
  47. data/spec/cli_spec.rb +71 -0
  48. data/spec/concat_filter_spec.rb +37 -0
  49. data/spec/dsl/pipeline_dsl_spec.rb +165 -0
  50. data/spec/dsl/project_dsl_spec.rb +41 -0
  51. data/spec/dynamic_file_task_spec.rb +119 -0
  52. data/spec/encoding_spec.rb +106 -0
  53. data/spec/file_wrapper_spec.rb +132 -0
  54. data/spec/filter_spec.rb +332 -0
  55. data/spec/graph_spec.rb +56 -0
  56. data/spec/gsub_filter_spec.rb +87 -0
  57. data/spec/manifest_entry_spec.rb +46 -0
  58. data/spec/manifest_spec.rb +67 -0
  59. data/spec/matcher_spec.rb +141 -0
  60. data/spec/middleware_spec.rb +199 -0
  61. data/spec/ordering_concat_filter_spec.rb +42 -0
  62. data/spec/pipeline_spec.rb +232 -0
  63. data/spec/project_spec.rb +295 -0
  64. data/spec/rake_acceptance_spec.rb +738 -0
  65. data/spec/rake_tasks_spec.rb +21 -0
  66. data/spec/reject_matcher_spec.rb +31 -0
  67. data/spec/sorted_pipeline_spec.rb +27 -0
  68. data/spec/spec_helper.rb +38 -0
  69. data/spec/support/spec_helpers/file_utils.rb +35 -0
  70. data/spec/support/spec_helpers/filters.rb +37 -0
  71. data/spec/support/spec_helpers/input_helpers.rb +23 -0
  72. data/spec/support/spec_helpers/memory_file_wrapper.rb +31 -0
  73. data/spec/support/spec_helpers/memory_manifest.rb +19 -0
  74. data/tools/perfs +101 -0
  75. metadata +215 -0
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rake::Pipeline::DynamicFileTask do
4
+ let(:invoked_tasks) { [] }
5
+
6
+ def define_task(deps, klass=Rake::Pipeline::DynamicFileTask, &task_proc)
7
+ task_proc ||= proc do |task|
8
+ touch(task.name)
9
+ invoked_tasks << task
10
+ end
11
+
12
+ task = klass.define_task(deps, &task_proc)
13
+
14
+ if klass == Rake::Pipeline::DynamicFileTask
15
+ task.manifest = Rake::Pipeline::Manifest.new
16
+ end
17
+
18
+ task
19
+ end
20
+
21
+ let(:task) { define_task('output') }
22
+
23
+ after do
24
+ # Clean out all defined tasks after each test runs
25
+ Rake.application = Rake::Application.new
26
+ end
27
+
28
+ describe "#dynamic" do
29
+ it "saves a block that can be called later with #invoke_dynamic_block" do
30
+ block = proc {}
31
+ task.dynamic(&block)
32
+ block.should_receive(:call).with(task)
33
+ task.invoke_dynamic_block
34
+ end
35
+
36
+ it "returns the task" do
37
+ (task.dynamic {}).should eq(task)
38
+ end
39
+ end
40
+
41
+ describe "#invoke" do
42
+ let(:static) { define_task('static', Rake::FileTask) }
43
+ let!(:dynamic) { define_task('dynamic', Rake::FileTask) }
44
+ let!(:dynamic_task) { define_task('output' => static).dynamic { ['dynamic'] } }
45
+
46
+ it "invokes the task's static and dynamic prerequisites" do
47
+ dynamic_task.invoke
48
+ invoked_tasks.should include(static)
49
+ invoked_tasks.should include(dynamic)
50
+ end
51
+
52
+ it "adds dynamic dependencies to its manifest entry" do
53
+ dynamic_task.invoke
54
+ dynamic_task.manifest_entry.deps.should == {
55
+ 'dynamic' => File.mtime('dynamic').to_i
56
+ }
57
+ end
58
+
59
+ it "adds the current task's mtime to its manifest entry" do
60
+ dynamic_task.invoke
61
+ dynamic_task.manifest_entry.mtime.should == File.mtime('output').to_i
62
+ end
63
+
64
+ it "raises an error when there is no manifest" do
65
+ dynamic_task.manifest = nil
66
+ lambda {
67
+ dynamic_task.invoke
68
+ }.should raise_error(Rake::Pipeline::DynamicFileTask::ManifestRequired)
69
+ end
70
+ end
71
+
72
+ describe "#needed?" do
73
+ it "is true if the task has manifest entry" do
74
+ task.manifest_entry.should be_nil
75
+ task.should be_needed
76
+ end
77
+ end
78
+
79
+ describe "#dynamic_prerequisites" do
80
+ def make_file(name, mtime=nil)
81
+ touch(name)
82
+ if mtime
83
+ File.utime(mtime, mtime, name)
84
+ end
85
+ end
86
+
87
+ it "returns an empty array if the task has no dynamic block" do
88
+ task.dynamic_prerequisites.should == []
89
+ end
90
+
91
+ it "returns the result of invoking the dynamic block" do
92
+ task.dynamic { %w[blinky] }
93
+ task.dynamic_prerequisites.should == %w[blinky]
94
+ end
95
+
96
+ it "filters the task itself from the list" do
97
+ task.dynamic { %w[output blinky] }
98
+ task.dynamic_prerequisites.should == %w[blinky]
99
+ end
100
+
101
+ it "loads dependency information from the manifest first" do
102
+ time = Time.utc(2000)
103
+ %w[blinky output].each { |f| make_file f, time }
104
+
105
+ manifest_entry = Rake::Pipeline::ManifestEntry.from_hash({
106
+ "deps" => {
107
+ "blinky" => time.to_i
108
+ },
109
+ "mtime" => time.to_i
110
+ })
111
+
112
+ task.dynamic { %w[] }
113
+ task.stub(:last_manifest_entry) { manifest_entry }
114
+ task.should_not_receive(:invoke_dynamic_block)
115
+ task.dynamic_prerequisites.should == %w[blinky]
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,106 @@
1
+ # encoding: UTF-8
2
+
3
+ if "".respond_to?(:encode)
4
+ class String
5
+ def strip_heredoc
6
+ indent = scan(/^[ \t]*(?=\S)/).min
7
+ indent = indent ? indent.size : 0
8
+ gsub(/^[ \t]{#{indent}}/, '')
9
+ end
10
+ end
11
+
12
+ inputs = {
13
+ "app/javascripts/jquery.js" => <<-HERE.strip_heredoc,
14
+ var jQuery = { japanese: "こんにちは" };
15
+ HERE
16
+
17
+ "app/javascripts/sproutcore.js" => <<-HERE.strip_heredoc,
18
+ var SC = {};
19
+ assert(SC);
20
+ SC.hi = function() { console.log("こんにちは"); };
21
+ HERE
22
+ }
23
+
24
+ expected_output = <<-HERE.strip_heredoc
25
+ var jQuery = { japanese: "こんにちは" };
26
+ var SC = {};
27
+
28
+ SC.hi = function() { console.log("こんにちは"); };
29
+ HERE
30
+
31
+ describe "the pipeline's encoding handling" do
32
+ Filters = Rake::Pipeline::SpecHelpers::Filters
33
+
34
+ let(:project) { Rake::Pipeline::Project.new }
35
+
36
+ let(:inputs) { inputs }
37
+
38
+ def output_should_exist(expected, encoding="UTF-8")
39
+ output = File.join(tmp, "public/javascripts/application.js")
40
+
41
+ File.exists?(output).should be_true
42
+ output = File.read(output, :encoding => encoding)
43
+ output.should == expected
44
+ output.should be_valid_encoding
45
+ end
46
+
47
+ def create_files
48
+ inputs.each do |name, contents|
49
+ filename = File.join(tmp, name)
50
+ mkdir_p File.dirname(filename)
51
+
52
+ File.open(filename, "w:#{encoding}") do |file|
53
+ file.write contents.encode(encoding)
54
+ end
55
+ end
56
+ end
57
+
58
+ before do
59
+ create_files
60
+
61
+ @pipeline = Rake::Pipeline.build :project => project do
62
+ output "public"
63
+ input "#{tmp}/app/javascripts/", "*.js"
64
+ concat "javascripts/application.js"
65
+ filter Filters::StripAssertsFilter
66
+ end
67
+ end
68
+
69
+ describe "when the input is UTF-8" do
70
+ let(:encoding) { "UTF-8" }
71
+
72
+ it "creates the correct file" do
73
+ @pipeline.invoke
74
+ output_should_exist(expected_output)
75
+ end
76
+ end
77
+
78
+ describe "when the input is not UTF-8" do
79
+ let(:encoding) { "EUC-JP" }
80
+
81
+ it "raises an exception" do
82
+ lambda { @pipeline.invoke }.should raise_error(Rake::Pipeline::EncodingError, /not valid UTF-8/)
83
+ end
84
+ end
85
+
86
+ describe "when dealing with only BINARY-type filters" do
87
+ let(:encoding) { "EUC-JP" }
88
+
89
+ it "does not raise an exception" do
90
+ pipeline = Rake::Pipeline.build :project => project do
91
+ output "public"
92
+ input "#{tmp}/app/javascripts/", "*.js"
93
+ concat "javascripts/application.js"
94
+ end
95
+
96
+ pipeline.invoke
97
+
98
+ expected = inputs.map do |filename, contents|
99
+ contents.encode("EUC-JP")
100
+ end.join
101
+
102
+ output_should_exist(expected.force_encoding("BINARY"), "BINARY")
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,132 @@
1
+ describe "Rake::Pipeline::FileWrapper" do
2
+ let(:file) { Rake::Pipeline::FileWrapper.new }
3
+ let(:root) { File.expand_path("app/assets") }
4
+ let(:jquery) { File.join(root, "javascripts/jquery.js") }
5
+
6
+ before do
7
+ file.root = root
8
+ file.path = "javascripts/jquery.js"
9
+ end
10
+
11
+ it "is equal to another FileWrapper if the root and path are the same" do
12
+ other = Rake::Pipeline::FileWrapper.new
13
+ other.root = root
14
+ other.path = "javascripts/jquery.js"
15
+
16
+ file.should == other
17
+ file.hash.should == other.hash
18
+ end
19
+
20
+ it "is not equal to another FileWrapper if the root is the same but the path is different" do
21
+ other = Rake::Pipeline::FileWrapper.new
22
+ other.root = root
23
+ other.path = "javascripts/jquery2.js"
24
+
25
+ file.should_not == other
26
+ end
27
+
28
+ it "is not equal to another FileWrapper if the root is different but the path is the same" do
29
+ other = Rake::Pipeline::FileWrapper.new
30
+ other.root = "/"
31
+ other.path = "javascripts/jquery.js"
32
+
33
+ file.should_not == other
34
+ end
35
+
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
55
+ end
56
+
57
+ describe "#in_directory?" do
58
+ it "is when the full path starts with the directory" do
59
+ file.should be_in_directory(file.root)
60
+ end
61
+
62
+ it "is not when full path does not start with the directory" do
63
+ file.should_not be_in_directory("/foo/bar/baz")
64
+ end
65
+ end
66
+
67
+ it "it knows it doesn't exist when the file doesn't exist" do
68
+ file.exists?.should == false
69
+ end
70
+
71
+ it "knows it exists when the file exists" do
72
+ touch_p jquery
73
+ file.exists?.should == true
74
+ end
75
+
76
+ it "raises an exception if trying to #read a file that doesn't exist" do
77
+ lambda { file.read }.should raise_error(Errno::ENOENT)
78
+ end
79
+
80
+ it "returns the file's body when invoking #read on a file that does exist" do
81
+ touch_p jquery
82
+ body = "This. Is. jQuery!"
83
+ File.open(jquery, "w") { |file| file.write body }
84
+
85
+ file.read.should == body
86
+ end
87
+
88
+ it "creates a file with #create" do
89
+ new_file = file.create
90
+ new_file.should be_kind_of File
91
+
92
+ File.exists?(jquery).should == true
93
+
94
+ new_file.close
95
+ end
96
+
97
+ it "complains if trying to close an unopened file" do
98
+ lambda { file.close }.should raise_error(IOError)
99
+ end
100
+
101
+ it "closes a file created using #create with #close" do
102
+ new_file = file.create
103
+ new_file.closed?.should == false
104
+
105
+ file.close
106
+ new_file.closed?.should == true
107
+
108
+ lambda { file.close }.should raise_error(IOError)
109
+ end
110
+
111
+ it "complains if trying to write to a file that was not created" do
112
+ lambda { file.write "This. Is. jQuery" }.should raise_error(Rake::Pipeline::UnopenedFile)
113
+ end
114
+
115
+ it "supports a block form of create that closes the file at the end" do
116
+ file.create do |f|
117
+ f.write "HI"
118
+ end
119
+
120
+ File.exists?(jquery).should == true
121
+ File.read(jquery).should == "HI"
122
+ file.closed?.should == true
123
+ end
124
+
125
+ it "writes to the file system if the file was created" do
126
+ new_file = file.create
127
+ file.write "This. Is. jQuery"
128
+ new_file.close
129
+
130
+ file.read.should == "This. Is. jQuery"
131
+ end
132
+ end
@@ -0,0 +1,332 @@
1
+ describe "Rake::Pipeline::Filter" do
2
+ MemoryManifest ||= Rake::Pipeline::SpecHelpers::MemoryManifest
3
+
4
+ after do
5
+ # Clean out all defined tasks after each test runs
6
+ Rake.application = Rake::Application.new
7
+ end
8
+
9
+ def input_file(path, file_root=input_root)
10
+ Rake::Pipeline::FileWrapper.new(file_root, path)
11
+ end
12
+
13
+ def output_file(path, file_root=output_root)
14
+ Rake::Pipeline::FileWrapper.new(file_root, path)
15
+ end
16
+
17
+ let(:project) { Rake::Pipeline::Project.new }
18
+ let(:pipeline) { project.build_pipeline "app" }
19
+
20
+ let(:filter) { Rake::Pipeline::Filter.new }
21
+ let(:input_root) { File.join(tmp, "app/assets") }
22
+ let(:output_root) { File.join(tmp, "filter1/app/assets") }
23
+ let(:input_files) do
24
+ %w(jquery.js jquery-ui.js ember.js).map { |f| input_file(f) }
25
+ end
26
+
27
+ it "accepts a series of FileWrapper objects for the input" do
28
+ filter.input_files = input_files
29
+ filter.input_files.should == input_files
30
+ end
31
+
32
+ it "accepts a root directory for the outputs" do
33
+ path = File.expand_path(tmp, "filter1/app/assets")
34
+ filter.output_root = path
35
+ filter.output_root.should == path
36
+ end
37
+
38
+ it "accepts a Rake::Application to install tasks into" do
39
+ app = Rake::Application.new
40
+ filter.rake_application = app
41
+ filter.rake_application.should == app
42
+ end
43
+
44
+ it "makes Rake.application the default rake_application" do
45
+ filter.rake_application.should == Rake.application
46
+ end
47
+
48
+ it "accepts a proc to convert the input name into an output name" do
49
+ conversion = proc { |input| input }
50
+ filter.output_name_generator = conversion
51
+ filter.output_name_generator.should == conversion
52
+ end
53
+
54
+ it "accepts a block constructor argument to convert the input name into an output name" do
55
+ conversion = proc { |input| "application.js" }
56
+ new_filter = Rake::Pipeline::Filter.new(&conversion)
57
+ new_filter.output_name_generator.should == conversion
58
+ end
59
+
60
+ it "knows about its containing pipeline" do
61
+ filter = Rake::Pipeline::Filter.new
62
+ pipeline.add_filter(filter)
63
+ filter.pipeline.should == pipeline
64
+ end
65
+
66
+ describe "using the output_name proc to converting the input names into a hash" do
67
+ before do
68
+ filter.output_root = output_root
69
+ filter.input_files = input_files
70
+ end
71
+
72
+ it "with a simple output_name proc that outputs to a single file" do
73
+ output_name_generator = proc { |input| "application.js" }
74
+ filter.output_name_generator = output_name_generator
75
+
76
+ filter.outputs.should == {
77
+ output_file("application.js") => input_files
78
+ }
79
+
80
+ filter.output_files.should == [output_file("application.js")]
81
+ end
82
+
83
+ it "with more than one output per input" do
84
+ output_name_generator = proc { |input| [ input, "application.js" ] }
85
+ filter.output_name_generator = output_name_generator
86
+ outputs = filter.outputs
87
+
88
+ outputs = input_files.inject({}) do |hash, input|
89
+ hash.merge output_file(input.path) => [ input ]
90
+ end
91
+
92
+ filter.outputs.should == { output_file("application.js") => input_files }.merge(outputs)
93
+ filter.output_files.sort.should ==
94
+ ([ output_file("application.js") ] + input_files.map { |f| output_file(f.path) }).sort
95
+ end
96
+
97
+ it "with a 1:1 output_name proc" do
98
+ output_name_generator = proc { |input| input }
99
+ filter.output_name_generator = output_name_generator
100
+ outputs = filter.outputs
101
+
102
+ outputs.keys.sort.should == input_files.map { |f| output_file(f.path) }.sort
103
+ outputs.values.flatten.sort.should == input_files.sort
104
+
105
+ filter.output_files.should == input_files.map { |file| output_file(file.path) }
106
+ end
107
+
108
+ it "with a more complicated proc" do
109
+ output_name_generator = proc { |input| input.split(/[-.]/, 2).first + ".js" }
110
+ filter.output_name_generator = output_name_generator
111
+ outputs = filter.outputs
112
+
113
+ outputs.keys.sort.should == [output_file("ember.js"), output_file("jquery.js")]
114
+ outputs.values.sort.should == [[input_file("ember.js")], [input_file("jquery.js"), input_file("jquery-ui.js")]]
115
+
116
+ filter.output_files.should == [output_file("jquery.js"), output_file("ember.js")]
117
+ end
118
+ end
119
+
120
+ describe "generates rake tasks" do
121
+ let(:filter_class) {
122
+ Class.new(Rake::Pipeline::Filter) do
123
+ attr_accessor :generate_output_block
124
+
125
+ def generate_output(inputs, output)
126
+ generate_output_block.call(inputs, output)
127
+ end
128
+ end
129
+ }
130
+ let(:filter) { filter_class.new }
131
+ let(:input_root) { File.join(tmp, "app/assets") }
132
+ let(:output_root) { File.join(tmp, "filter1/app/assets") }
133
+ let(:input_files) do
134
+ %w(jquery.js jquery-ui.js ember.js).map do |file|
135
+ input_file("javascripts/#{file}")
136
+ end
137
+ end
138
+
139
+ before do
140
+ Rake.application = Rake::Application.new
141
+ filter.output_root = output_root
142
+ filter.input_files = input_files
143
+ filter.pipeline = pipeline
144
+ end
145
+
146
+ def output_task(path, app=Rake.application)
147
+ app.define_task(Rake::Pipeline::DynamicFileTask, File.join(output_root, path))
148
+ end
149
+
150
+ def input_task(path)
151
+ Rake::Pipeline::DynamicFileTask.define_task(File.join(input_root, path))
152
+ end
153
+
154
+ it "does not generate Rake tasks onto Rake.application if an alternate application is supplied" do
155
+ app = Rake::Application.new
156
+ filter.rake_application = app
157
+ filter.output_name_generator = proc { |input| input }
158
+ filter.generate_rake_tasks
159
+ tasks = filter.rake_tasks
160
+
161
+ input_files.each do |file|
162
+ task = output_task(file.path, app)
163
+ tasks.include?(task).should == true
164
+ Rake.application.tasks.include?(task).should == false
165
+ app.tasks.include?(task).should == true
166
+ end
167
+ end
168
+
169
+ it "with a simple output_name proc that outputs to a single file" do
170
+ filter_runs = 0
171
+
172
+ filter.output_name_generator = proc { |input| "javascripts/application.js" }
173
+ filter.generate_output_block = proc do |inputs, output|
174
+ inputs.should == input_files
175
+ output.should == output_file("javascripts/application.js")
176
+
177
+ filter_runs += 1
178
+ end
179
+
180
+ filter.generate_rake_tasks
181
+ tasks = filter.rake_tasks
182
+ tasks.should == [output_task("javascripts/application.js")]
183
+
184
+ tasks.each(&:invoke)
185
+
186
+ filter_runs.should == 1
187
+ end
188
+
189
+ it "with an output_name proc that takes two arguments" do
190
+ filter.output_name_generator = proc { |path, input|
191
+ input.path.upcase
192
+ }
193
+
194
+ filter.outputs.keys.map(&:path).should include('JAVASCRIPTS/JQUERY.JS')
195
+ filter.output_files.map(&:path).should include('JAVASCRIPTS/JQUERY.JS')
196
+ end
197
+
198
+ it "with a 1:1 output_name proc" do
199
+ filter_runs = 0
200
+
201
+ filter.output_name_generator = proc { |input| input }
202
+ filter.generate_output_block = proc do |inputs, output|
203
+ inputs.should == [input_file(output.path)]
204
+
205
+ filter_runs += 1
206
+ end
207
+
208
+ filter.generate_rake_tasks
209
+ tasks = filter.rake_tasks
210
+ tasks.sort.should == input_files.map { |file| output_task(file.path) }.sort
211
+ tasks.each do |task|
212
+ name = task.name.sub(/^#{Regexp.escape(output_root)}/, '')
213
+ prereq = File.join(input_root, name)
214
+ task.prerequisites.should == [prereq]
215
+ end
216
+
217
+ tasks.each(&:invoke)
218
+
219
+ filter_runs.should == 3
220
+ end
221
+
222
+ it "with a more complicated proc" do
223
+ filter_runs = 0
224
+
225
+ filter.output_name_generator = proc { |input| input.match(%r{javascripts/[^-.]*})[0] + ".js" }
226
+ filter.generate_output_block = proc do |inputs, output|
227
+ if output.path == "javascripts/jquery.js"
228
+ inputs.should == [input_file("javascripts/jquery.js"), input_file("javascripts/jquery-ui.js")]
229
+ output.should == output_file("javascripts/jquery.js")
230
+ elsif output.path == "javascripts/ember.js"
231
+ inputs.should == [input_file("javascripts/ember.js")]
232
+ output.should == output_file("javascripts/ember.js")
233
+ else
234
+ flunk
235
+ end
236
+
237
+ filter_runs += 1
238
+ end
239
+
240
+ filter.generate_rake_tasks
241
+ tasks = filter.rake_tasks
242
+ tasks.sort.should == [output_task("javascripts/jquery.js"), output_task("javascripts/ember.js")].sort
243
+
244
+ tasks.sort[0].prerequisites.should == [
245
+ File.join(input_root, "javascripts/ember.js"),
246
+ ]
247
+
248
+ tasks.sort[1].prerequisites.should == [
249
+ File.join(input_root, "javascripts/jquery.js"),
250
+ File.join(input_root, "javascripts/jquery-ui.js"),
251
+ ]
252
+
253
+ tasks.each(&:invoke)
254
+
255
+ filter_runs.should == 2
256
+ end
257
+ end
258
+
259
+ describe "a filter with additional_dependencies" do
260
+ let(:include_filter) {
261
+ Class.new(Rake::Pipeline::ConcatFilter) do
262
+ def additional_dependencies(input)
263
+ includes(input)
264
+ end
265
+
266
+ def includes(input)
267
+ input.read.scan(/^#include \"(.*)\"$/).map(&:first).map do |inc|
268
+ File.join(input.root, inc)
269
+ end
270
+ end
271
+
272
+ def generate_output(inputs, output)
273
+ out = ""
274
+ inputs.each do |input|
275
+ includes(input).each { |inc| out += File.read(inc) }
276
+ out += input.read
277
+ output.write out
278
+ end
279
+ end
280
+ end
281
+ }
282
+
283
+ def write_input_file(filename, contents='', root=input_root)
284
+ mkdir_p root
285
+ File.open(File.join(root, filename), 'w') { |f| f.puts contents }
286
+ input_file(filename)
287
+ end
288
+
289
+ def invoke_filter(filter)
290
+ mkdir_p output_root
291
+ filter.pipeline = pipeline
292
+ filter.last_manifest = MemoryManifest.new
293
+ filter.output_root = output_root
294
+ filter.input_files = input_files
295
+ tasks = filter.generate_rake_tasks
296
+ tasks.each(&:invoke)
297
+ tasks
298
+ end
299
+
300
+ let!(:main) { write_input_file('main', '#include "header"') }
301
+ let!(:header) { write_input_file('header', 'header') }
302
+ let!(:main_output) { output_file('main') }
303
+ let(:input_files) { [main] }
304
+ let(:filter) { include_filter.new }
305
+
306
+ it "creates its output files" do
307
+ invoke_filter(filter)
308
+ main_output.exists?.should be_true
309
+ main_output.read.should == %[header\n#include "header"\n]
310
+ end
311
+
312
+ it "rebuilds main file when it changes" do
313
+ invoke_filter(filter)
314
+ sleep 1
315
+ File.open(main.fullpath, 'w') { |f| f.puts 'PEANUTS' }
316
+ filter.rake_tasks.each { |t| t.recursively_reenable(Rake.application) }
317
+ invoke_filter(filter)
318
+ main_output.read.should == %[PEANUTS\n]
319
+ end
320
+
321
+ it "rebuilds the main file when a dynamic dependency changes" do
322
+ invoke_filter(filter)
323
+ sleep 1
324
+ $LATCH = true
325
+ File.open(header.fullpath, 'w') { |f| f.puts 'PEANUTS' }
326
+ filter.rake_tasks.each { |t| t.recursively_reenable(Rake.application) }
327
+ invoke_filter(filter)
328
+ main_output.read.should == %[PEANUTS\n#include "header"\n]
329
+ end
330
+ end
331
+ end
332
+