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
@@ -2,14 +2,29 @@ require "rake-pipeline/middleware"
2
2
 
3
3
  module Rake
4
4
  class Pipeline
5
+ # Use Rake::Pipeline inside of Rails 3.x. To use, simply add
6
+ # Rake::Pipeline to your +Gemfile+:
7
+ #
8
+ # !!!ruby
9
+ # gem 'rake-pipeline'
10
+ #
11
+ # Then, activate it in development mode. In config/development.rb:
12
+ #
13
+ # !!!ruby
14
+ # config.rake_pipeline_enabled = true
15
+ #
5
16
  class Railtie < ::Rails::Railtie
6
17
  config.rake_pipeline_enabled = false
7
18
  config.rake_pipeline_assetfile = 'Assetfile'
8
19
 
20
+ rake_tasks do
21
+ load "rake-pipeline/precompile.rake"
22
+ end
23
+
9
24
  initializer "rake-pipeline.assetfile" do |app|
10
25
  if config.rake_pipeline_enabled
11
26
  assetfile = File.join(Rails.root, config.rake_pipeline_assetfile)
12
- config.app_middleware.use(Rake::Pipeline::Middleware, assetfile)
27
+ config.app_middleware.insert ActionDispatch::Static, Rake::Pipeline::Middleware, assetfile
13
28
  end
14
29
  end
15
30
  end
@@ -0,0 +1,15 @@
1
+ require "rake-pipeline/middleware"
2
+ require "rack/server"
3
+
4
+ module Rake
5
+ class Pipeline
6
+ class Server < Rack::Server
7
+ def app
8
+ not_found = proc { [404, { "Content-Type" => "text/plain" }, ["not found"]] }
9
+ config = "Assetfile"
10
+
11
+ Middleware.new(not_found, config)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  module Rake
2
2
  class Pipeline
3
- # @version 0.5.0
4
- VERSION = "0.5.0"
3
+ # @version 0.7.0
4
+ VERSION = "0.7.0"
5
5
  end
6
6
  end
@@ -16,6 +16,8 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Rake::Pipeline::VERSION
17
17
 
18
18
  gem.add_dependency "rake", "~> 0.9.0"
19
+ gem.add_dependency "thor"
20
+ gem.add_dependency "json"
19
21
 
20
22
  gem.add_development_dependency "rspec"
21
23
  gem.add_development_dependency "rack-test"
@@ -0,0 +1,71 @@
1
+ describe "Rake::Pipeline::CLI" do
2
+ attr_reader :project, :pipeline
3
+
4
+ before do
5
+ @project = Rake::Pipeline::Project.new
6
+ @project.stub(:clean)
7
+ @project.stub(:invoke)
8
+ @project.stub(:output_files).and_return([])
9
+ Rake::Pipeline::Project.stub(:new).and_return(@project)
10
+ end
11
+
12
+ def rakep(*args)
13
+ Rake::Pipeline::CLI.start(args)
14
+ end
15
+
16
+ describe "build" do
17
+ context "with no arguments" do
18
+ it "invokes a project" do
19
+ project.should_receive :invoke
20
+ rakep "build"
21
+ end
22
+
23
+ it "cleans up the tmpdir" do
24
+ project.should_receive :cleanup_tmpdir
25
+ rakep "build"
26
+ end
27
+ end
28
+
29
+ context "with a --pretend argument" do
30
+ it "doesn't invoke a project" do
31
+ project.should_not_receive :invoke
32
+ rakep "build", "--pretend"
33
+ end
34
+ end
35
+
36
+ context "with a --clean argument" do
37
+ it "cleans a project" do
38
+ project.should_receive :clean
39
+ rakep "build", "--clean"
40
+ end
41
+
42
+ it "invokes a project" do
43
+ project.should_receive :invoke
44
+ rakep "build", "--clean"
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "clean" do
50
+ context "with no arguments" do
51
+ it "cleans a project" do
52
+ project.should_receive :clean
53
+ rakep "clean"
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "server" do
59
+ let(:server) { double "server" }
60
+
61
+ before do
62
+ require 'rake-pipeline/server'
63
+ Rake::Pipeline::Server.stub(:new).and_return(server)
64
+ end
65
+
66
+ it "starts a Rake::Pipeline::Server" do
67
+ server.should_receive :start
68
+ rakep "server"
69
+ end
70
+ end
71
+ end
@@ -1,31 +1,5 @@
1
1
  describe "ConcatFilter" do
2
- class MemoryFileWrapper < Struct.new(:root, :path, :encoding, :body)
3
- @@files = {}
4
-
5
- def self.files
6
- @@files
7
- end
8
-
9
- def with_encoding(new_encoding)
10
- self.class.new(root, path, new_encoding, body)
11
- end
12
-
13
- def fullpath
14
- File.join(root, path)
15
- end
16
-
17
- def create
18
- @@files[fullpath] = self
19
- self.body = ""
20
- yield
21
- end
22
-
23
- alias read body
24
-
25
- def write(contents)
26
- self.body << contents
27
- end
28
- end
2
+ MemoryFileWrapper = Rake::Pipeline::SpecHelpers::MemoryFileWrapper
29
3
 
30
4
  let(:input_files) {
31
5
  [
@@ -1,11 +1,11 @@
1
- describe "Rake::Pipeline::DSL" do
1
+ describe "Rake::Pipeline::PipelineDSL" do
2
2
  ConcatFilter = Rake::Pipeline::SpecHelpers::Filters::ConcatFilter
3
3
 
4
4
  let(:pipeline) { Rake::Pipeline.new }
5
- let(:dsl) { Rake::Pipeline::DSL.new(pipeline) }
5
+ let(:dsl) { Rake::Pipeline::DSL::PipelineDSL.new(pipeline) }
6
6
 
7
- before do
8
- pipeline.input_root = "."
7
+ def filter
8
+ pipeline.filters.last
9
9
  end
10
10
 
11
11
  it "accepts a pipeline in its constructor" do
@@ -13,19 +13,19 @@ describe "Rake::Pipeline::DSL" do
13
13
  end
14
14
 
15
15
  describe "#input" do
16
- it "configures the pipeline's input_root" do
16
+ it "adds an input to the pipeline" do
17
17
  dsl.input "/app"
18
- pipeline.input_root.should == "/app"
18
+ pipeline.inputs["/app"].should == '**/*'
19
19
  end
20
20
 
21
- it "configures the pipeline's input_glob" do
21
+ it "configures the input's glob" do
22
22
  dsl.input "/app", "*.js"
23
- pipeline.input_glob.should == "*.js"
23
+ pipeline.inputs['/app'].should == "*.js"
24
24
  end
25
25
 
26
- it "defaults the pipeline's input_glob to **/*" do
26
+ it "defaults input's glob to **/*" do
27
27
  dsl.input "/app"
28
- pipeline.input_glob.should == "**/*"
28
+ pipeline.inputs['/app'].should == "**/*"
29
29
  end
30
30
  end
31
31
 
@@ -35,13 +35,13 @@ describe "Rake::Pipeline::DSL" do
35
35
  pipeline.filters.should be_empty
36
36
  dsl.filter ConcatFilter
37
37
  pipeline.filters.should_not be_empty
38
- pipeline.filters.last.should be_kind_of(ConcatFilter)
38
+ filter.should be_kind_of(ConcatFilter)
39
39
  end
40
40
 
41
41
  it "takes a block to configure the filter's output file names" do
42
42
  generator = proc { |input| "main.js" }
43
43
  dsl.filter(ConcatFilter, &generator)
44
- pipeline.filters.last.output_name_generator.should == generator
44
+ filter.output_name_generator.should == generator
45
45
  end
46
46
 
47
47
  it "passes any extra arguments to the filter's constructor" do
@@ -53,7 +53,7 @@ describe "Rake::Pipeline::DSL" do
53
53
  end
54
54
 
55
55
  dsl.filter filter_class, "foo", "bar"
56
- pipeline.filters.last.args.should == %w(foo bar)
56
+ filter.args.should == %w(foo bar)
57
57
  end
58
58
  end
59
59
 
@@ -66,7 +66,7 @@ describe "Rake::Pipeline::DSL" do
66
66
 
67
67
  it "adds the new matcher to the pipeline's filters" do
68
68
  matcher = dsl.match("*.glob") {}
69
- pipeline.filters.last.should == matcher
69
+ filter.should == matcher
70
70
  end
71
71
  end
72
72
 
@@ -77,10 +77,24 @@ describe "Rake::Pipeline::DSL" do
77
77
  end
78
78
  end
79
79
 
80
- describe "#tmpdir" do
81
- it "configures the pipeline's tmpdir" do
82
- dsl.tmpdir "/temporary"
83
- pipeline.tmpdir.should == "/temporary"
80
+ describe "#concat" do
81
+ it "creates a ConcatFilter" do
82
+ dsl.concat "octopus"
83
+ filter.should be_kind_of(Rake::Pipeline::ConcatFilter)
84
+ end
85
+
86
+ context "passed an Array first argument" do
87
+ it "creates an OrderingConcatFilter" do
88
+ dsl.concat ["octopus"]
89
+ filter.should be_kind_of(Rake::Pipeline::OrderingConcatFilter)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "#copy" do
95
+ it "creates a ConcatFilter" do
96
+ dsl.copy
97
+ filter.should be_kind_of(Rake::Pipeline::ConcatFilter)
84
98
  end
85
99
  end
86
100
  end
@@ -0,0 +1,41 @@
1
+ describe "Rake::Pipeline::ProjectDSL" do
2
+ ConcatFilter = Rake::Pipeline::SpecHelpers::Filters::ConcatFilter
3
+
4
+ let(:project) { Rake::Pipeline::Project.new }
5
+ let(:dsl) { Rake::Pipeline::DSL::ProjectDSL.new(project) }
6
+
7
+ it "accepts a project in its constructor" do
8
+ dsl.project.should == project
9
+ end
10
+
11
+ describe "#output" do
12
+ it "configures the project's default_output_root" do
13
+ dsl.output "/path/to/output"
14
+ project.default_output_root.should == "/path/to/output"
15
+ end
16
+ end
17
+
18
+ describe "#tmpdir" do
19
+ it "sets the project's tmpdir" do
20
+ dsl.tmpdir "/path/to/tmpdir"
21
+ project.tmpdir.should == "/path/to/tmpdir"
22
+ end
23
+ end
24
+
25
+ describe "#input" do
26
+ it "uses Project#build_pipeline to add a new pipeline to the project" do
27
+ project.should_receive(:build_pipeline)
28
+ dsl.input("app") {}
29
+ end
30
+ end
31
+
32
+ describe "#map" do
33
+ it "saves the block in a hash on the project" do
34
+ project.maps.keys.size.should == 0
35
+ run_me = lambda { }
36
+ dsl.map("foo", &run_me)
37
+ dsl.project.maps["foo"].should == run_me
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,111 @@
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
+ klass.define_task(deps, &task_proc)
13
+ end
14
+
15
+ let(:task) { define_task('output') }
16
+
17
+ before do
18
+ # Make sure date conversions happen in UTC, not local time
19
+ ENV['TZ'] = 'UTC'
20
+ end
21
+
22
+ after do
23
+ # Clean out all defined tasks after each test runs
24
+ Rake.application = Rake::Application.new
25
+ end
26
+
27
+ describe "#dynamic" do
28
+ it "saves a block that can be called later with #invoke_dynamic_block" do
29
+ block = proc {}
30
+ task.dynamic(&block)
31
+ block.should_receive(:call).with(task)
32
+ task.invoke_dynamic_block
33
+ end
34
+
35
+ it "returns the task" do
36
+ (task.dynamic {}).should eq(task)
37
+ end
38
+ end
39
+
40
+ describe "#invoke" do
41
+ let(:static) { define_task('static', Rake::FileTask) }
42
+ let!(:dynamic) { define_task('dynamic', Rake::FileTask) }
43
+ let!(:dynamic_task) { define_task('output' => static).dynamic { ['dynamic'] } }
44
+
45
+ it "invokes the task's static and dynamic prerequisites" do
46
+ dynamic_task.invoke
47
+ invoked_tasks.should include(static)
48
+ invoked_tasks.should include(dynamic)
49
+ end
50
+
51
+ it "adds dynamic dependencies to its manifest entry" do
52
+ dynamic_task.invoke
53
+ dynamic_task.manifest_entry.deps.should == {
54
+ 'dynamic' => File.mtime('dynamic')
55
+ }
56
+ end
57
+
58
+ it "adds the current task's mtime to its manifest entry" do
59
+ dynamic_task.invoke
60
+ dynamic_task.manifest_entry.mtime.should == File.mtime('output')
61
+ end
62
+ end
63
+
64
+ describe "#needed?" do
65
+ it "is true if the task has no previous manifest entry" do
66
+ task.last_manifest_entry.should be_nil
67
+ task.should be_needed
68
+ end
69
+ end
70
+
71
+ describe "#dynamic_prerequisites" do
72
+ def make_file(name, mtime=nil)
73
+ touch(name)
74
+ if mtime
75
+ File.utime(mtime, mtime, name)
76
+ end
77
+ end
78
+
79
+ it "returns an empty array if the task has no dynamic block" do
80
+ task.dynamic_prerequisites.should == []
81
+ end
82
+
83
+ it "returns the result of invoking the dynamic block" do
84
+ task.dynamic { %w[blinky] }
85
+ task.dynamic_prerequisites.should == %w[blinky]
86
+ end
87
+
88
+ it "filters the task itself from the list" do
89
+ task.dynamic { %w[output blinky] }
90
+ task.dynamic_prerequisites.should == %w[blinky]
91
+ end
92
+
93
+ it "loads dependency information from the manifest first" do
94
+ time = Time.utc(2000)
95
+ %w[blinky output].each { |f| make_file f, time }
96
+
97
+ manifest_entry = Rake::Pipeline::ManifestEntry.from_hash({
98
+ "deps" => {
99
+ "blinky" => "2000-01-01 00:00:00 +0000"
100
+ },
101
+ "mtime" => "2000-01-01 00:00:00 +0000"
102
+ })
103
+
104
+ task.dynamic { %w[] }
105
+ task.stub(:last_manifest_entry) { manifest_entry }
106
+ task.should_not_receive(:invoke_dynamic_block)
107
+ task.dynamic_prerequisites.should == %w[blinky]
108
+ end
109
+ end
110
+
111
+ end
@@ -57,11 +57,10 @@ if "".respond_to?(:encode)
57
57
  create_files
58
58
 
59
59
  @pipeline = Rake::Pipeline.build do
60
- tmpdir "temporary"
61
- input tmp, "app/javascripts/*.js"
62
- filter Filters::ConcatFilter, "javascripts/application.js"
63
- filter Filters::StripAssertsFilter
64
60
  output "public"
61
+ input "#{tmp}/app/javascripts/", "*.js"
62
+ concat "javascripts/application.js"
63
+ filter Filters::StripAssertsFilter
65
64
  end
66
65
  end
67
66
 
@@ -86,11 +85,10 @@ if "".respond_to?(:encode)
86
85
  let(:encoding) { "EUC-JP" }
87
86
 
88
87
  it "does not raise an exception" do
89
- pipeline = Rake::Pipeline.build do
90
- tmpdir "temporary"
91
- input tmp, "app/javascripts/*.js"
92
- filter Filters::ConcatFilter, "javascripts/application.js"
88
+ pipeline = Rake::Pipeline.build do
93
89
  output "public"
90
+ input "#{tmp}/app/javascripts/", "*.js"
91
+ concat "javascripts/application.js"
94
92
  end
95
93
 
96
94
  pipeline.invoke