sprockets 2.3.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sprockets might be problematic. Click here for more details.

Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +2 -2
  3. data/README.md +332 -115
  4. data/bin/sprockets +8 -0
  5. data/lib/rake/sprocketstask.rb +25 -13
  6. data/lib/sprockets/asset.rb +143 -205
  7. data/lib/sprockets/autoload/closure.rb +7 -0
  8. data/lib/sprockets/autoload/coffee_script.rb +7 -0
  9. data/lib/sprockets/autoload/eco.rb +7 -0
  10. data/lib/sprockets/autoload/ejs.rb +7 -0
  11. data/lib/sprockets/autoload/sass.rb +7 -0
  12. data/lib/sprockets/autoload/uglifier.rb +7 -0
  13. data/lib/sprockets/autoload/yui.rb +7 -0
  14. data/lib/sprockets/autoload.rb +11 -0
  15. data/lib/sprockets/base.rb +49 -257
  16. data/lib/sprockets/bower.rb +58 -0
  17. data/lib/sprockets/bundle.rb +65 -0
  18. data/lib/sprockets/cache/file_store.rb +165 -14
  19. data/lib/sprockets/cache/memory_store.rb +66 -0
  20. data/lib/sprockets/cache/null_store.rb +46 -0
  21. data/lib/sprockets/cache.rb +234 -0
  22. data/lib/sprockets/cached_environment.rb +69 -0
  23. data/lib/sprockets/closure_compressor.rb +53 -0
  24. data/lib/sprockets/coffee_script_processor.rb +25 -0
  25. data/lib/sprockets/coffee_script_template.rb +6 -0
  26. data/lib/sprockets/compressing.rb +74 -0
  27. data/lib/sprockets/configuration.rb +83 -0
  28. data/lib/sprockets/context.rb +125 -131
  29. data/lib/sprockets/dependencies.rb +73 -0
  30. data/lib/sprockets/digest_utils.rb +156 -0
  31. data/lib/sprockets/directive_processor.rb +209 -211
  32. data/lib/sprockets/eco_processor.rb +32 -0
  33. data/lib/sprockets/eco_template.rb +3 -35
  34. data/lib/sprockets/ejs_processor.rb +31 -0
  35. data/lib/sprockets/ejs_template.rb +3 -34
  36. data/lib/sprockets/encoding_utils.rb +258 -0
  37. data/lib/sprockets/engines.rb +45 -38
  38. data/lib/sprockets/environment.rb +17 -67
  39. data/lib/sprockets/erb_processor.rb +30 -0
  40. data/lib/sprockets/erb_template.rb +6 -0
  41. data/lib/sprockets/errors.rb +6 -13
  42. data/lib/sprockets/file_reader.rb +15 -0
  43. data/lib/sprockets/http_utils.rb +115 -0
  44. data/lib/sprockets/jst_processor.rb +35 -19
  45. data/lib/sprockets/legacy.rb +314 -0
  46. data/lib/sprockets/legacy_proc_processor.rb +35 -0
  47. data/lib/sprockets/legacy_tilt_processor.rb +29 -0
  48. data/lib/sprockets/loader.rb +176 -0
  49. data/lib/sprockets/manifest.rb +179 -98
  50. data/lib/sprockets/manifest_utils.rb +45 -0
  51. data/lib/sprockets/mime.rb +114 -32
  52. data/lib/sprockets/path_dependency_utils.rb +85 -0
  53. data/lib/sprockets/path_digest_utils.rb +47 -0
  54. data/lib/sprockets/path_utils.rb +282 -0
  55. data/lib/sprockets/paths.rb +81 -0
  56. data/lib/sprockets/processing.rb +157 -189
  57. data/lib/sprockets/processor_utils.rb +103 -0
  58. data/lib/sprockets/resolve.rb +208 -0
  59. data/lib/sprockets/sass_cache_store.rb +19 -15
  60. data/lib/sprockets/sass_compressor.rb +59 -0
  61. data/lib/sprockets/sass_functions.rb +2 -0
  62. data/lib/sprockets/sass_importer.rb +2 -29
  63. data/lib/sprockets/sass_processor.rb +285 -0
  64. data/lib/sprockets/sass_template.rb +4 -44
  65. data/lib/sprockets/server.rb +109 -84
  66. data/lib/sprockets/transformers.rb +145 -0
  67. data/lib/sprockets/uglifier_compressor.rb +63 -0
  68. data/lib/sprockets/uri_utils.rb +190 -0
  69. data/lib/sprockets/utils.rb +193 -44
  70. data/lib/sprockets/version.rb +1 -1
  71. data/lib/sprockets/yui_compressor.rb +65 -0
  72. data/lib/sprockets.rb +144 -53
  73. metadata +248 -238
  74. data/lib/sprockets/asset_attributes.rb +0 -126
  75. data/lib/sprockets/bundled_asset.rb +0 -79
  76. data/lib/sprockets/caching.rb +0 -96
  77. data/lib/sprockets/charset_normalizer.rb +0 -41
  78. data/lib/sprockets/index.rb +0 -99
  79. data/lib/sprockets/processed_asset.rb +0 -152
  80. data/lib/sprockets/processor.rb +0 -32
  81. data/lib/sprockets/safety_colons.rb +0 -28
  82. data/lib/sprockets/scss_template.rb +0 -13
  83. data/lib/sprockets/static_asset.rb +0 -57
  84. data/lib/sprockets/trail.rb +0 -90
@@ -1,80 +1,40 @@
1
1
  require 'sprockets/engines'
2
+ require 'sprockets/file_reader'
3
+ require 'sprockets/legacy_proc_processor'
4
+ require 'sprockets/legacy_tilt_processor'
2
5
  require 'sprockets/mime'
3
- require 'sprockets/processor'
6
+ require 'sprockets/processor_utils'
7
+ require 'sprockets/uri_utils'
4
8
  require 'sprockets/utils'
5
9
 
6
10
  module Sprockets
7
11
  # `Processing` is an internal mixin whose public methods are exposed on
8
- # the `Environment` and `Index` classes.
12
+ # the `Environment` and `CachedEnvironment` classes.
9
13
  module Processing
10
- include Engines, Mime
11
-
12
- # Register a new mime type.
13
- def register_mime_type(mime_type, ext)
14
- # Overrides the global behavior to expire the index
15
- expire_index!
16
- @trail.append_extension(ext)
17
- super
18
- end
14
+ include ProcessorUtils, URIUtils, Utils
19
15
 
20
- # Returns an `Array` of format extension `String`s.
21
- #
22
- # format_extensions
23
- # # => ['.js', '.css']
24
- #
25
- def format_extensions
26
- @trail.extensions - @engines.keys
16
+ def pipelines
17
+ config[:pipelines]
27
18
  end
28
19
 
29
- # Registers a new Engine `klass` for `ext`.
30
- def register_engine(ext, klass)
31
- # Overrides the global behavior to expire the index
32
- expire_index!
33
- add_engine_to_trail(ext, klass)
34
- super
35
- end
20
+ def register_pipeline(name, proc = nil, &block)
21
+ proc ||= block
36
22
 
37
- # Deprecated alias for `preprocessors`.
38
- def processors(*args)
39
- preprocessors(*args)
23
+ self.config = hash_reassoc(config, :pipelines) do |pipelines|
24
+ pipelines.merge(name.to_sym => proc)
25
+ end
40
26
  end
41
27
 
42
- # Returns an `Array` of `Processor` classes. If a `mime_type`
43
- # argument is supplied, the processors registered under that
44
- # extension will be returned.
45
- #
46
28
  # Preprocessors are ran before Postprocessors and Engine
47
29
  # processors.
48
- #
49
- # All `Processor`s must follow the `Tilt::Template` interface. It is
50
- # recommended to subclass `Tilt::Template`.
51
- def preprocessors(mime_type = nil)
52
- if mime_type
53
- @preprocessors[mime_type].dup
54
- else
55
- deep_copy_hash(@preprocessors)
56
- end
30
+ def preprocessors
31
+ config[:preprocessors]
57
32
  end
33
+ alias_method :processors, :preprocessors
58
34
 
59
- # Returns an `Array` of `Processor` classes. If a `mime_type`
60
- # argument is supplied, the processors registered under that
61
- # extension will be returned.
62
- #
63
35
  # Postprocessors are ran after Preprocessors and Engine processors.
64
- #
65
- # All `Processor`s must follow the `Tilt::Template` interface. It is
66
- # recommended to subclass `Tilt::Template`.
67
- def postprocessors(mime_type = nil)
68
- if mime_type
69
- @postprocessors[mime_type].dup
70
- else
71
- deep_copy_hash(@postprocessors)
72
- end
73
- end
74
-
75
- # Deprecated alias for `register_preprocessor`.
76
- def register_processor(*args, &block)
77
- register_preprocessor(*args, &block)
36
+ def postprocessors
37
+ config[:postprocessors]
78
38
  end
79
39
 
80
40
  # Registers a new Preprocessor `klass` for `mime_type`.
@@ -83,197 +43,205 @@ module Sprockets
83
43
  #
84
44
  # A block can be passed for to create a shorthand processor.
85
45
  #
86
- # register_preprocessor :my_processor do |context, data|
46
+ # register_preprocessor 'text/css', :my_processor do |context, data|
87
47
  # data.gsub(...)
88
48
  # end
89
49
  #
90
- def register_preprocessor(mime_type, klass, &block)
91
- expire_index!
92
-
93
- if block_given?
94
- name = klass.to_s
95
- klass = Class.new(Processor) do
96
- @name = name
97
- @processor = block
98
- end
99
- end
100
-
101
- @preprocessors[mime_type].push(klass)
50
+ def register_preprocessor(*args, &block)
51
+ register_config_processor(:preprocessors, *args, &block)
102
52
  end
53
+ alias_method :register_processor, :register_preprocessor
103
54
 
104
55
  # Registers a new Postprocessor `klass` for `mime_type`.
105
56
  #
106
- # register_postprocessor 'text/css', Sprockets::CharsetNormalizer
57
+ # register_postprocessor 'application/javascript', Sprockets::DirectiveProcessor
107
58
  #
108
59
  # A block can be passed for to create a shorthand processor.
109
60
  #
110
- # register_postprocessor :my_processor do |context, data|
61
+ # register_postprocessor 'application/javascript', :my_processor do |context, data|
111
62
  # data.gsub(...)
112
63
  # end
113
64
  #
114
- def register_postprocessor(mime_type, klass, &block)
115
- expire_index!
116
-
117
- if block_given?
118
- name = klass.to_s
119
- klass = Class.new(Processor) do
120
- @name = name
121
- @processor = block
122
- end
123
- end
124
-
125
- @postprocessors[mime_type].push(klass)
126
- end
127
-
128
- # Deprecated alias for `unregister_preprocessor`.
129
- def unregister_processor(*args)
130
- unregister_preprocessor(*args)
65
+ def register_postprocessor(*args, &block)
66
+ register_config_processor(:postprocessors, *args, &block)
131
67
  end
132
68
 
133
69
  # Remove Preprocessor `klass` for `mime_type`.
134
70
  #
135
71
  # unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
136
72
  #
137
- def unregister_preprocessor(mime_type, klass)
138
- expire_index!
139
-
140
- if klass.is_a?(String) || klass.is_a?(Symbol)
141
- klass = @preprocessors[mime_type].detect { |cls|
142
- cls.respond_to?(:name) &&
143
- cls.name == "Sprockets::Processor (#{klass})"
144
- }
145
- end
146
-
147
- @preprocessors[mime_type].delete(klass)
73
+ def unregister_preprocessor(*args)
74
+ unregister_config_processor(:preprocessors, *args)
148
75
  end
76
+ alias_method :unregister_processor, :unregister_preprocessor
149
77
 
150
78
  # Remove Postprocessor `klass` for `mime_type`.
151
79
  #
152
80
  # unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
153
81
  #
154
- def unregister_postprocessor(mime_type, klass)
155
- expire_index!
156
-
157
- if klass.is_a?(String) || klass.is_a?(Symbol)
158
- klass = @postprocessors[mime_type].detect { |cls|
159
- cls.respond_to?(:name) &&
160
- cls.name == "Sprockets::Processor (#{klass})"
161
- }
162
- end
163
-
164
- @postprocessors[mime_type].delete(klass)
82
+ def unregister_postprocessor(*args)
83
+ unregister_config_processor(:postprocessors, *args)
165
84
  end
166
85
 
167
- # Returns an `Array` of `Processor` classes. If a `mime_type`
168
- # argument is supplied, the processors registered under that
169
- # extension will be returned.
170
- #
171
86
  # Bundle Processors are ran on concatenated assets rather than
172
87
  # individual files.
173
- #
174
- # All `Processor`s must follow the `Tilt::Template` interface. It is
175
- # recommended to subclass `Tilt::Template`.
176
- def bundle_processors(mime_type = nil)
177
- if mime_type
178
- @bundle_processors[mime_type].dup
179
- else
180
- deep_copy_hash(@bundle_processors)
181
- end
88
+ def bundle_processors
89
+ config[:bundle_processors]
182
90
  end
183
91
 
184
92
  # Registers a new Bundle Processor `klass` for `mime_type`.
185
93
  #
186
- # register_bundle_processor 'text/css', Sprockets::CharsetNormalizer
94
+ # register_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
187
95
  #
188
96
  # A block can be passed for to create a shorthand processor.
189
97
  #
190
- # register_bundle_processor :my_processor do |context, data|
98
+ # register_bundle_processor 'application/javascript', :my_processor do |context, data|
191
99
  # data.gsub(...)
192
100
  # end
193
101
  #
194
- def register_bundle_processor(mime_type, klass, &block)
195
- expire_index!
196
-
197
- if block_given?
198
- name = klass.to_s
199
- klass = Class.new(Processor) do
200
- @name = name
201
- @processor = block
202
- end
203
- end
204
-
205
- @bundle_processors[mime_type].push(klass)
102
+ def register_bundle_processor(*args, &block)
103
+ register_config_processor(:bundle_processors, *args, &block)
206
104
  end
207
105
 
208
106
  # Remove Bundle Processor `klass` for `mime_type`.
209
107
  #
210
- # unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
108
+ # unregister_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
211
109
  #
212
- def unregister_bundle_processor(mime_type, klass)
213
- expire_index!
214
-
215
- if klass.is_a?(String) || klass.is_a?(Symbol)
216
- klass = @bundle_processors[mime_type].detect { |cls|
217
- cls.respond_to?(:name) &&
218
- cls.name == "Sprockets::Processor (#{klass})"
219
- }
110
+ def unregister_bundle_processor(*args)
111
+ unregister_config_processor(:bundle_processors, *args)
112
+ end
113
+
114
+ # Public: Register bundle metadata reducer function.
115
+ #
116
+ # Examples
117
+ #
118
+ # Sprockets.register_bundle_metadata_reducer 'application/javascript', :jshint_errors, [], :+
119
+ #
120
+ # Sprockets.register_bundle_metadata_reducer 'text/css', :selector_count, 0 { |total, count|
121
+ # total + count
122
+ # }
123
+ #
124
+ # mime_type - String MIME Type. Use '*/*' applies to all types.
125
+ # key - Symbol metadata key
126
+ # initial - Initial memo to pass to the reduce funciton (default: nil)
127
+ # block - Proc accepting the memo accumulator and current value
128
+ #
129
+ # Returns nothing.
130
+ def register_bundle_metadata_reducer(mime_type, key, *args, &block)
131
+ case args.size
132
+ when 0
133
+ reducer = block
134
+ when 1
135
+ if block_given?
136
+ initial = args[0]
137
+ reducer = block
138
+ else
139
+ initial = nil
140
+ reducer = args[0].to_proc
141
+ end
142
+ when 2
143
+ initial = args[0]
144
+ reducer = args[1].to_proc
145
+ else
146
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0..2)"
220
147
  end
221
148
 
222
- @bundle_processors[mime_type].delete(klass)
149
+ self.config = hash_reassoc(config, :bundle_reducers, mime_type) do |reducers|
150
+ reducers.merge(key => [initial, reducer])
151
+ end
223
152
  end
224
153
 
225
- # Return CSS compressor or nil if none is set
226
- def css_compressor
227
- bundle_processors('text/css').detect { |klass|
228
- klass.respond_to?(:name) &&
229
- klass.name == 'Sprockets::Processor (css_compressor)'
230
- }
231
- end
154
+ protected
155
+ def resolve_processors_cache_key_uri(uri)
156
+ params = parse_uri_query_params(uri[11..-1])
157
+ params[:engine_extnames] = params[:engines] ? params[:engines].split(',') : []
158
+ processors = processors_for(params[:type], params[:file_type], params[:engine_extnames], params[:pipeline])
159
+ processors_cache_keys(processors)
160
+ end
232
161
 
233
- # Assign a compressor to run on `text/css` assets.
234
- #
235
- # The compressor object must respond to `compress` or `compile`.
236
- def css_compressor=(compressor)
237
- expire_index!
162
+ def build_processors_uri(type, file_type, engine_extnames, pipeline)
163
+ engines = engine_extnames.join(',') if engine_extnames.any?
164
+ query = encode_uri_query_params(
165
+ type: type,
166
+ file_type: file_type,
167
+ engines: engines,
168
+ pipeline: pipeline
169
+ )
170
+ "processors:#{query}"
171
+ end
238
172
 
239
- unregister_bundle_processor 'text/css', :css_compressor
240
- return unless compressor
173
+ def processors_for(type, file_type, engine_extnames, pipeline)
174
+ pipeline ||= :default
175
+ config[:pipelines][pipeline.to_sym].call(self, type, file_type, engine_extnames)
176
+ end
241
177
 
242
- register_bundle_processor 'text/css', :css_compressor do |context, data|
243
- compressor.compress(data)
178
+ def default_processors_for(type, file_type, engine_extnames)
179
+ bundled_processors = config[:bundle_processors][type]
180
+ if bundled_processors.any?
181
+ bundled_processors
182
+ else
183
+ self_processors_for(type, file_type, engine_extnames)
184
+ end
244
185
  end
245
- end
246
186
 
247
- # Return JS compressor or nil if none is set
248
- def js_compressor
249
- bundle_processors('application/javascript').detect { |klass|
250
- klass.respond_to?(:name) &&
251
- klass.name == 'Sprockets::Processor (js_compressor)'
252
- }
253
- end
187
+ def self_processors_for(type, file_type, engine_extnames)
188
+ processors = []
254
189
 
255
- # Assign a compressor to run on `application/javascript` assets.
256
- #
257
- # The compressor object must respond to `compress` or `compile`.
258
- def js_compressor=(compressor)
259
- expire_index!
190
+ processors.concat config[:postprocessors][type]
191
+
192
+ if type != file_type && processor = config[:transformers][file_type][type]
193
+ processors << processor
194
+ end
260
195
 
261
- unregister_bundle_processor 'application/javascript', :js_compressor
262
- return unless compressor
196
+ processors.concat engine_extnames.map { |ext| engines[ext] }
197
+ processors.concat config[:preprocessors][file_type]
198
+
199
+ if processors.any? || mime_type_charset_detecter(type)
200
+ processors << FileReader
201
+ end
263
202
 
264
- register_bundle_processor 'application/javascript', :js_compressor do |context, data|
265
- compressor.compress(data)
203
+ processors
266
204
  end
267
- end
268
205
 
269
206
  private
270
- def add_engine_to_trail(ext, klass)
271
- @trail.append_extension(ext.to_s)
207
+ def register_config_processor(type, mime_type, klass, proc = nil, &block)
208
+ proc ||= block
209
+ processor = wrap_processor(klass, proc)
210
+
211
+ self.config = hash_reassoc(config, type, mime_type) do |processors|
212
+ processors.unshift(processor)
213
+ processors
214
+ end
215
+
216
+ compute_transformers!
217
+ end
218
+
219
+ def unregister_config_processor(type, mime_type, klass)
220
+ if klass.is_a?(String) || klass.is_a?(Symbol)
221
+ klass = config[type][mime_type].detect do |cls|
222
+ cls.respond_to?(:name) && cls.name == "Sprockets::LegacyProcProcessor (#{klass})"
223
+ end
224
+ end
225
+
226
+ self.config = hash_reassoc(config, type, mime_type) do |processors|
227
+ processors.delete(klass)
228
+ processors
229
+ end
230
+
231
+ compute_transformers!
232
+ end
272
233
 
273
- if klass.respond_to?(:default_mime_type) && klass.default_mime_type
274
- if format_ext = extension_for_mime_type(klass.default_mime_type)
275
- @trail.alias_extension(ext.to_s, format_ext)
234
+ def wrap_processor(klass, proc)
235
+ if !proc
236
+ if klass.respond_to?(:call)
237
+ klass
238
+ else
239
+ LegacyTiltProcessor.new(klass)
276
240
  end
241
+ elsif proc.respond_to?(:arity) && proc.arity == 2
242
+ LegacyProcProcessor.new(klass.to_s, proc)
243
+ else
244
+ proc
277
245
  end
278
246
  end
279
247
  end
@@ -0,0 +1,103 @@
1
+ module Sprockets
2
+ # Functional utilities for dealing with Processor functions.
3
+ #
4
+ # A Processor is a general function that my modify or transform an asset as
5
+ # part of the pipeline. CoffeeScript to JavaScript conversion, Minification
6
+ # or Concatenation are all implemented as seperate Processor steps.
7
+ #
8
+ # Processors maybe any object that responds to call. So procs or a class that
9
+ # defines a self.call method.
10
+ #
11
+ # For ergonomics, processors may return a number of shorthand values.
12
+ # Unfortunately, this means that processors can not compose via ordinary
13
+ # function composition. The composition helpers here can help.
14
+ module ProcessorUtils
15
+ extend self
16
+
17
+ # Public: Compose processors in right to left order.
18
+ #
19
+ # processors - Array of processors callables
20
+ #
21
+ # Returns a composed Proc.
22
+ def compose_processors(*processors)
23
+ context = self
24
+
25
+ if processors.length == 1
26
+ obj = method(:call_processor).to_proc.curry[processors.first]
27
+ else
28
+ obj = method(:call_processors).to_proc.curry[processors]
29
+ end
30
+
31
+ metaclass = (class << obj; self; end)
32
+ metaclass.send(:define_method, :cache_key) do
33
+ context.processors_cache_keys(processors)
34
+ end
35
+
36
+ obj
37
+ end
38
+
39
+ # Public: Invoke list of processors in right to left order.
40
+ #
41
+ # The right to left order processing mirrors standard function composition.
42
+ # Think about:
43
+ #
44
+ # bundle.call(uglify.call(coffee.call(input)))
45
+ #
46
+ # processors - Array of processor callables
47
+ # input - Hash of input data to pass to each processor
48
+ #
49
+ # Returns a Hash with :data and other processor metadata key/values.
50
+ def call_processors(processors, input)
51
+ data = input[:data] || ""
52
+ metadata = (input[:metadata] || {}).dup
53
+
54
+ processors.reverse_each do |processor|
55
+ result = call_processor(processor, input.merge(data: data, metadata: metadata))
56
+ data = result.delete(:data)
57
+ metadata.merge!(result)
58
+ end
59
+
60
+ metadata.merge(data: data)
61
+ end
62
+
63
+ # Public: Invoke processor.
64
+ #
65
+ # processor - Processor callables
66
+ # input - Hash of input data to pass to processor
67
+ #
68
+ # Returns a Hash with :data and other processor metadata key/values.
69
+ def call_processor(processor, input)
70
+ metadata = (input[:metadata] || {}).dup
71
+ metadata[:data] = input[:data]
72
+
73
+ case result = processor.call({data: "", metadata: {}}.merge(input))
74
+ when NilClass
75
+ metadata
76
+ when Hash
77
+ metadata.merge(result)
78
+ when String
79
+ metadata.merge(data: result)
80
+ else
81
+ raise TypeError, "invalid processor return type: #{result.class}"
82
+ end
83
+ end
84
+
85
+ # Internal: Get processor defined cached key.
86
+ #
87
+ # processor - Processor function
88
+ #
89
+ # Returns JSON serializable key or nil.
90
+ def processor_cache_key(processor)
91
+ processor.cache_key if processor.respond_to?(:cache_key)
92
+ end
93
+
94
+ # Internal: Get combined cache keys for set of processors.
95
+ #
96
+ # processors - Array of processor functions
97
+ #
98
+ # Returns Array of JSON serializable keys.
99
+ def processors_cache_keys(processors)
100
+ processors.map { |processor| processor_cache_key(processor) }
101
+ end
102
+ end
103
+ end