rake-pipeline 0.5.0 → 0.7.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 (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