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
@@ -0,0 +1,56 @@
1
+ require "thor"
2
+
3
+ module Rake
4
+ class Pipeline
5
+ class CLI < Thor
6
+ class_option :assetfile, :default => "Assetfile", :aliases => "-c"
7
+ default_task :build
8
+
9
+ desc "build", "Build the project."
10
+ method_option :pretend, :type => :boolean, :aliases => "-p"
11
+ method_option :clean, :type => :boolean, :aliases => "-C"
12
+ def build
13
+ if options[:pretend]
14
+ project.output_files.each do |file|
15
+ say_status :create, relative_path(file)
16
+ end
17
+ else
18
+ options[:clean] ? project.clean : project.cleanup_tmpdir
19
+ project.invoke
20
+ end
21
+ end
22
+
23
+ desc "clean", "Remove the pipeline's temporary and output files."
24
+ method_option :pretend, :type => :boolean, :aliases => "-p"
25
+ def clean
26
+ if options[:pretend]
27
+ project.files_to_clean.each do |file|
28
+ say_status :remove, relative_path(file)
29
+ end
30
+ else
31
+ project.clean
32
+ end
33
+ end
34
+
35
+ desc "server", "Run the Rake::Pipeline preview server."
36
+ def server
37
+ require "rake-pipeline/server"
38
+ Rake::Pipeline::Server.new.start
39
+ end
40
+
41
+ private
42
+ def project
43
+ @project ||= Rake::Pipeline::Project.new(options[:assetfile])
44
+ end
45
+
46
+ # @param [FileWrapper|String] path
47
+ # @return [String] The path to the file with the current
48
+ # directory stripped out.
49
+ def relative_path(path)
50
+ pathstr = path.respond_to?(:fullpath) ? path.fullpath : path
51
+ pathstr.sub(%r|#{Dir.pwd}/|, '')
52
+ end
53
+ end
54
+ end
55
+ end
56
+
@@ -1,146 +1,9 @@
1
1
  module Rake
2
2
  class Pipeline
3
- # This class exists purely to provide a convenient DSL for
4
- # configuring a pipeline.
5
- #
6
- # All instance methods of {DSL} are available in the context
7
- # the block passed to +Rake::Pipeline.+{Pipeline.build}.
8
- #
9
- # When configuring a pipeline, you *must* provide both a
10
- # root, and a series of files using {#input}.
11
- class DSL
12
- # @return [Pipeline] the pipeline the DSL should configure
13
- attr_reader :pipeline
14
-
15
- # Configure a pipeline with a passed in block.
16
- #
17
- # @param [Pipeline] pipeline the pipeline that the DSL
18
- # should configure.
19
- # @param [Proc] block the block describing the
20
- # configuration. This block will be evaluated in
21
- # the context of a new instance of {DSL}
22
- # @return [void]
23
- def self.evaluate(pipeline, &block)
24
- new(pipeline).instance_eval(&block)
25
- copy_filter = Rake::Pipeline::ConcatFilter.new
26
- copy_filter.output_name_generator = proc { |input| input }
27
- pipeline.add_filter(copy_filter)
28
- end
29
-
30
- # Create a new {DSL} to configure a pipeline.
31
- #
32
- # @param [Pipeline] pipeline the pipeline that the DSL
33
- # should configure.
34
- # @return [void]
35
- def initialize(pipeline)
36
- @pipeline = pipeline
37
- end
38
-
39
- # Define the input location and files for the pipeline.
40
- #
41
- # @example
42
- # !!!ruby
43
- # Rake::Pipeline.build do
44
- # input "app/assets", "**/*.js"
45
- # # ...
46
- # end
47
- #
48
- # @param [String] root the root path where the pipeline
49
- # should find its input files.
50
- # @param [String] glob a file pattern that represents
51
- # the list of all files that the pipeline should
52
- # process. The default is +"**/*"+.
53
- # @return [void]
54
- def input(root, glob="**/*")
55
- pipeline.input_root = root
56
- pipeline.input_glob = glob
57
- end
58
-
59
- # Add a filter to the pipeline.
60
- #
61
- # In addition to a filter class, {#filter} takes a
62
- # block that describes how the filter should map
63
- # input files to output files.
64
- #
65
- # By default, the block maps an input file into
66
- # an output file with the same name.
67
- #
68
- # Any additional arguments passed to {#filter} will
69
- # be passed on to the filter class's constructor.
70
- #
71
- # @see Filter#outputs Filter#output (for an example
72
- # of how a list of input files gets mapped to
73
- # its outputs)
74
- #
75
- # @param [Class] filter_class the class of the filter.
76
- # @param [Array] ctor_args a list of arguments to pass
77
- # to the filter's constructor.
78
- # @param [Proc] block an output file name generator.
79
- # @return [void]
80
- def filter(filter_class, *ctor_args, &block)
81
- filter = filter_class.new(*ctor_args, &block)
82
- pipeline.add_filter(filter)
83
- end
84
-
85
- # Apply a number of filters, but only to files matching
86
- # a particular pattern.
87
- #
88
- # Inside the block passed to {#match match}, you may
89
- # specify any number of filters that should be applied
90
- # to files matching the pattern.
91
- #
92
- # @param [String] pattern a glob pattern to match
93
- # @param [Proc] block a block that supplies filters
94
- # @return [Matcher]
95
- #
96
- # @example
97
- # !!!ruby
98
- # Pipeline.build do
99
- # input "app/assets"
100
- # output "public"
101
- #
102
- # # compile coffee files into JS files
103
- # match "*.coffee" do
104
- # filter CompileCoffee do |input|
105
- # input.sub(/coffee$/, "js")
106
- # end
107
- # end
108
- #
109
- # # because the previous step converted coffeee
110
- # # into JS, the coffee files will be included here
111
- # match "*.js" do
112
- # filter MinifyFilter
113
- # filter Rake::Pipeline::ConcatFilter, "application.js"
114
- # end
115
- # end
116
- def match(pattern, &block)
117
- matcher = pipeline.copy(Matcher, &block)
118
- matcher.glob = pattern
119
- pipeline.add_filter matcher
120
- matcher
121
- end
122
-
123
- # Specify the output directory for the pipeline.
124
- #
125
- # @param [String] root the output directory.
126
- # @return [void]
127
- def output(root)
128
- pipeline.output_root = root
129
- end
130
-
131
- # Specify the location of the temporary directory.
132
- # Filters will store intermediate build artifacts
133
- # here.
134
- #
135
- # This defaults "tmp" in the current working directory.
136
- #
137
- # @param [String] root the temporary directory
138
- # @return [void]
139
- def tmpdir(root)
140
- pipeline.tmpdir = root
141
- end
3
+ module DSL
142
4
  end
143
5
  end
144
6
  end
145
7
 
146
-
8
+ require 'rake-pipeline/dsl/pipeline_dsl'
9
+ require 'rake-pipeline/dsl/project_dsl'
@@ -0,0 +1,168 @@
1
+ module Rake
2
+ class Pipeline
3
+ module DSL
4
+ # This class is used by {ProjectDSL} to provide a convenient DSL for
5
+ # configuring a pipeline.
6
+ #
7
+ # All instance methods of {PipelineDSL} are available in the context
8
+ # the block passed to +Rake::Pipeline.+{Pipeline.build}.
9
+ class PipelineDSL
10
+ # @return [Pipeline] the pipeline the DSL should configure
11
+ attr_reader :pipeline
12
+
13
+ # Configure a pipeline with a passed in block.
14
+ #
15
+ # @param [Pipeline] pipeline the pipeline that the PipelineDSL
16
+ # should configure.
17
+ # @param [Proc] block the block describing the
18
+ # configuration. This block will be evaluated in
19
+ # the context of a new instance of {PipelineDSL}
20
+ # @return [void]
21
+ def self.evaluate(pipeline, options, &block)
22
+ dsl = new(pipeline)
23
+
24
+ # If any before filters, apply them to the pipeline.
25
+ # They will be run in reverse of insertion order.
26
+ if before_filters = options[:before_filters]
27
+ before_filters.each do |klass, args, block|
28
+ dsl.filter klass, *args, &block
29
+ end
30
+ end
31
+
32
+ # Evaluate the block in the context of the DSL.
33
+ dsl.instance_eval(&block)
34
+
35
+ # If any after filters, apply them to the pipeline.
36
+ # They will be run in insertion order.
37
+ if after_filters = options[:after_filters]
38
+ after_filters.each do |klass, args, block|
39
+ dsl.filter klass, *args, &block
40
+ end
41
+ end
42
+
43
+ # the FinalizingFilter should always come after all
44
+ # user specified after filters
45
+ pipeline.finalize
46
+ end
47
+
48
+ # Create a new {PipelineDSL} to configure a pipeline.
49
+ #
50
+ # @param [Pipeline] pipeline the pipeline that the PipelineDSL
51
+ # should configure.
52
+ # @return [void]
53
+ def initialize(pipeline)
54
+ @pipeline = pipeline
55
+ end
56
+
57
+ # Add an input location and files to a pipeline.
58
+ #
59
+ # @example
60
+ # !!!ruby
61
+ # Rake::Pipeline::Project.build do
62
+ # input "app" do
63
+ # input "assets", "**/*.js"
64
+ # # ...
65
+ # end
66
+ # end
67
+ #
68
+ # @param [String] root the root path where the pipeline
69
+ # should find its input files.
70
+ # @param [String] glob a file pattern that represents
71
+ # the list of files that the pipeline should
72
+ # process within +root+. The default is +"**/*"+.
73
+ # @return [void]
74
+ def input(root, glob="**/*")
75
+ pipeline.add_input root, glob
76
+ end
77
+
78
+ # Add a filter to the pipeline.
79
+ #
80
+ # In addition to a filter class, {#filter} takes a
81
+ # block that describes how the filter should map
82
+ # input files to output files.
83
+ #
84
+ # By default, the block maps an input file into
85
+ # an output file with the same name.
86
+ #
87
+ # Any additional arguments passed to {#filter} will
88
+ # be passed on to the filter class's constructor.
89
+ #
90
+ # @see Filter#outputs Filter#output (for an example
91
+ # of how a list of input files gets mapped to
92
+ # its outputs)
93
+ #
94
+ # @param [Class] filter_class the class of the filter.
95
+ # @param [Array] ctor_args a list of arguments to pass
96
+ # to the filter's constructor.
97
+ # @param [Proc] block an output file name generator.
98
+ # @return [void]
99
+ def filter(filter_class, *ctor_args, &block)
100
+ filter = filter_class.new(*ctor_args, &block)
101
+ pipeline.add_filter(filter)
102
+ end
103
+
104
+ # Apply a number of filters, but only to files matching
105
+ # a particular pattern.
106
+ #
107
+ # Inside the block passed to {#match match}, you may
108
+ # specify any number of filters that should be applied
109
+ # to files matching the pattern.
110
+ #
111
+ # @param [String] pattern a glob pattern to match
112
+ # @param [Proc] block a block that supplies filters
113
+ # @return [Matcher]
114
+ #
115
+ # @example
116
+ # !!!ruby
117
+ # Rake::Pipeline::Project.build do
118
+ # output "public"
119
+ #
120
+ # input "app/assets" do
121
+ # # compile coffee files into JS files
122
+ # match "*.coffee" do
123
+ # coffee_script
124
+ # end
125
+ #
126
+ # # because the previous step converted coffeee
127
+ # # into JS, the coffee files will be included here
128
+ # match "*.js" do
129
+ # uglify
130
+ # concat "application.js"
131
+ # end
132
+ # end
133
+ # end
134
+ def match(pattern, &block)
135
+ matcher = pipeline.copy(Matcher, &block)
136
+ matcher.glob = pattern
137
+ pipeline.add_filter matcher
138
+ matcher
139
+ end
140
+
141
+ # Specify the output directory for the pipeline.
142
+ #
143
+ # @param [String] root the output directory.
144
+ # @return [void]
145
+ def output(root)
146
+ pipeline.output_root = root
147
+ end
148
+
149
+ # A helper method for adding a concat filter to
150
+ # the pipeline.
151
+ # If the first argument is an Array, it adds a new
152
+ # {OrderingConcatFilter}, otherwise it adds a new
153
+ # {ConcatFilter}.
154
+ #
155
+ # @see OrderingConcatFilter#initialize
156
+ # @see ConcatFilter#initialize
157
+ def concat(*args, &block)
158
+ if args.first.kind_of?(Array)
159
+ filter(Rake::Pipeline::OrderingConcatFilter, *args, &block)
160
+ else
161
+ filter(Rake::Pipeline::ConcatFilter, *args, &block)
162
+ end
163
+ end
164
+ alias_method :copy, :concat
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,108 @@
1
+ module Rake
2
+ class Pipeline
3
+ module DSL
4
+ # This class exists purely to provide a convenient DSL for
5
+ # configuring a project.
6
+ #
7
+ # All instance methods of {ProjectDSL} are available in the context
8
+ # the block passed to +Rake::Pipeline::Project.+{Project.build}.
9
+ #
10
+ # When configuring a project, you *must* provide an output root
11
+ # and a series of files using at least one {#input} block.
12
+ class ProjectDSL
13
+ # @return [Project] the project the DSL should configure
14
+ attr_reader :project
15
+
16
+ # Configure a project with a passed in block.
17
+ #
18
+ # @param [Project] project the project that the ProjectDSL
19
+ # should configure.
20
+ # @param [Proc] block the block describing the
21
+ # configuration. This block will be evaluated in
22
+ # the context of a new instance of {ProjectDSL}
23
+ # @return [void]
24
+ def self.evaluate(project, &block)
25
+ new(project).instance_eval(&block)
26
+ end
27
+
28
+ # Create a new {ProjectDSL} to configure a project.
29
+ #
30
+ # @param [Project] project
31
+ # the project that the ProjectDSL should configure.
32
+ # @return [void]
33
+ def initialize(project)
34
+ @project = project
35
+ @before_filters = []
36
+ @after_filters = []
37
+ @project.before_filters = @before_filters
38
+ @project.after_filters = @after_filters
39
+ end
40
+
41
+ # Add a filter to every input block. The parameters
42
+ # to +before_filter+ are the same as the parameters
43
+ # to {PipelineDSL#filter}.
44
+ #
45
+ # Filters will be executed before the specified
46
+ # filters in reverse of insertion order.
47
+ #
48
+ # @see {PipelineDSL#filter}
49
+ def before_filter(klass, *args, &block)
50
+ @before_filters.unshift [klass, args, block]
51
+ end
52
+
53
+ # Add a filter to every input block. The parameters
54
+ # to +after_filter+ are the same as the parameters
55
+ # to {PipelineDSL#filter}.
56
+ #
57
+ # Filters will be executed after the specified
58
+ # filters in insertion order.
59
+ #
60
+ # @see {PipelineDSL#filter}
61
+ def after_filter(klass, *args, &block)
62
+ @after_filters.push [klass, args, block]
63
+ end
64
+
65
+ # Specify the default output directory for the project.
66
+ #
67
+ # Pipelines created in this project will place their
68
+ # outputs here unless the value is overriden in their
69
+ # {#input} block.
70
+ #
71
+ # @param [String] root the output directory.
72
+ # @return [void]
73
+ def output(root)
74
+ project.default_output_root = root
75
+ end
76
+
77
+ # Specify the location of the root temporary directory.
78
+ #
79
+ # Pipelines will store intermediate build artifacts
80
+ # in a subdirectory of this directory.
81
+ #
82
+ # This defaults to "tmp" in the current working directory.
83
+ #
84
+ # @param [String] root the temporary directory
85
+ # @return [void]
86
+ def tmpdir(root)
87
+ project.tmpdir = root
88
+ end
89
+
90
+ # Add a new pipeline with the given inputs to the project.
91
+ #
92
+ # @see Project.build_pipeline
93
+ def input(*inputs, &block)
94
+ # Allow pipelines without a specified block. This is possible
95
+ # if before and after filters are all that are needed for a
96
+ # given input.
97
+ block = proc {} unless block_given?
98
+ project.build_pipeline(*inputs, &block)
99
+ end
100
+ alias inputs input
101
+
102
+ def map(path, &block)
103
+ project.maps[path] = block
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end