sprockets 2.2.3 → 4.0.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +482 -255
  4. data/bin/sprockets +20 -7
  5. data/lib/rake/sprocketstask.rb +28 -15
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  7. data/lib/sprockets/asset.rb +142 -207
  8. data/lib/sprockets/autoload/babel.rb +8 -0
  9. data/lib/sprockets/autoload/closure.rb +8 -0
  10. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  11. data/lib/sprockets/autoload/eco.rb +8 -0
  12. data/lib/sprockets/autoload/ejs.rb +8 -0
  13. data/lib/sprockets/autoload/jsminc.rb +8 -0
  14. data/lib/sprockets/autoload/sass.rb +8 -0
  15. data/lib/sprockets/autoload/sassc.rb +8 -0
  16. data/lib/sprockets/autoload/uglifier.rb +8 -0
  17. data/lib/sprockets/autoload/yui.rb +8 -0
  18. data/lib/sprockets/autoload/zopfli.rb +7 -0
  19. data/lib/sprockets/autoload.rb +16 -0
  20. data/lib/sprockets/babel_processor.rb +66 -0
  21. data/lib/sprockets/base.rb +89 -249
  22. data/lib/sprockets/bower.rb +61 -0
  23. data/lib/sprockets/bundle.rb +105 -0
  24. data/lib/sprockets/cache/file_store.rb +190 -14
  25. data/lib/sprockets/cache/memory_store.rb +75 -0
  26. data/lib/sprockets/cache/null_store.rb +54 -0
  27. data/lib/sprockets/cache.rb +271 -0
  28. data/lib/sprockets/cached_environment.rb +64 -0
  29. data/lib/sprockets/closure_compressor.rb +48 -0
  30. data/lib/sprockets/coffee_script_processor.rb +39 -0
  31. data/lib/sprockets/compressing.rb +134 -0
  32. data/lib/sprockets/configuration.rb +79 -0
  33. data/lib/sprockets/context.rb +204 -135
  34. data/lib/sprockets/dependencies.rb +74 -0
  35. data/lib/sprockets/digest_utils.rb +200 -0
  36. data/lib/sprockets/directive_processor.rb +224 -216
  37. data/lib/sprockets/eco_processor.rb +33 -0
  38. data/lib/sprockets/ejs_processor.rb +32 -0
  39. data/lib/sprockets/encoding_utils.rb +262 -0
  40. data/lib/sprockets/environment.rb +23 -68
  41. data/lib/sprockets/erb_processor.rb +37 -0
  42. data/lib/sprockets/errors.rb +6 -13
  43. data/lib/sprockets/exporters/base.rb +72 -0
  44. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  45. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  46. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  47. data/lib/sprockets/exporting.rb +73 -0
  48. data/lib/sprockets/file_reader.rb +16 -0
  49. data/lib/sprockets/http_utils.rb +135 -0
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +36 -19
  52. data/lib/sprockets/loader.rb +343 -0
  53. data/lib/sprockets/manifest.rb +231 -96
  54. data/lib/sprockets/manifest_utils.rb +48 -0
  55. data/lib/sprockets/mime.rb +80 -32
  56. data/lib/sprockets/npm.rb +52 -0
  57. data/lib/sprockets/path_dependency_utils.rb +77 -0
  58. data/lib/sprockets/path_digest_utils.rb +48 -0
  59. data/lib/sprockets/path_utils.rb +367 -0
  60. data/lib/sprockets/paths.rb +82 -0
  61. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  62. data/lib/sprockets/processing.rb +140 -192
  63. data/lib/sprockets/processor_utils.rb +169 -0
  64. data/lib/sprockets/resolve.rb +295 -0
  65. data/lib/sprockets/sass_cache_store.rb +30 -0
  66. data/lib/sprockets/sass_compressor.rb +63 -0
  67. data/lib/sprockets/sass_functions.rb +3 -0
  68. data/lib/sprockets/sass_importer.rb +3 -0
  69. data/lib/sprockets/sass_processor.rb +313 -0
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +138 -90
  73. data/lib/sprockets/source_map_processor.rb +66 -0
  74. data/lib/sprockets/source_map_utils.rb +483 -0
  75. data/lib/sprockets/transformers.rb +173 -0
  76. data/lib/sprockets/uglifier_compressor.rb +66 -0
  77. data/lib/sprockets/unloaded_asset.rb +139 -0
  78. data/lib/sprockets/uri_tar.rb +99 -0
  79. data/lib/sprockets/uri_utils.rb +191 -0
  80. data/lib/sprockets/utils/gzip.rb +99 -0
  81. data/lib/sprockets/utils.rb +186 -53
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +56 -0
  84. data/lib/sprockets.rb +217 -52
  85. metadata +250 -59
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/asset_attributes.rb +0 -126
  88. data/lib/sprockets/bundled_asset.rb +0 -79
  89. data/lib/sprockets/caching.rb +0 -96
  90. data/lib/sprockets/charset_normalizer.rb +0 -41
  91. data/lib/sprockets/eco_template.rb +0 -38
  92. data/lib/sprockets/ejs_template.rb +0 -37
  93. data/lib/sprockets/engines.rb +0 -74
  94. data/lib/sprockets/index.rb +0 -99
  95. data/lib/sprockets/processed_asset.rb +0 -152
  96. data/lib/sprockets/processor.rb +0 -32
  97. data/lib/sprockets/safety_colons.rb +0 -28
  98. data/lib/sprockets/static_asset.rb +0 -57
  99. data/lib/sprockets/trail.rb +0 -90
@@ -1,80 +1,43 @@
1
- require 'sprockets/engines'
1
+ # frozen_string_literal: true
2
+ require 'sprockets/file_reader'
2
3
  require 'sprockets/mime'
3
- require 'sprockets/processor'
4
+ require 'sprockets/processor_utils'
5
+ require 'sprockets/uri_utils'
4
6
  require 'sprockets/utils'
5
7
 
6
8
  module Sprockets
7
9
  # `Processing` is an internal mixin whose public methods are exposed on
8
- # the `Environment` and `Index` classes.
10
+ # the `Environment` and `CachedEnvironment` classes.
9
11
  module Processing
10
- include Engines, Mime
12
+ include ProcessorUtils, URIUtils, Utils
11
13
 
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
14
+ def pipelines
15
+ config[:pipelines]
18
16
  end
19
17
 
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
27
- end
18
+ # Registers a pipeline that will be called by `call_processor` method.
19
+ def register_pipeline(name, proc = nil, &block)
20
+ proc ||= block
28
21
 
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
22
+ self.config = hash_reassoc(config, :pipeline_exts) do |pipeline_exts|
23
+ pipeline_exts.merge(".#{name}".freeze => name.to_sym)
24
+ end
36
25
 
37
- # Deprecated alias for `preprocessors`.
38
- def processors(*args)
39
- preprocessors(*args)
26
+ self.config = hash_reassoc(config, :pipelines) do |pipelines|
27
+ pipelines.merge(name.to_sym => proc)
28
+ end
40
29
  end
41
30
 
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
31
  # Preprocessors are ran before Postprocessors and Engine
47
32
  # 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
33
+ def preprocessors
34
+ config[:preprocessors]
57
35
  end
36
+ alias_method :processors, :preprocessors
58
37
 
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
38
  # 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)
39
+ def postprocessors
40
+ config[:postprocessors]
78
41
  end
79
42
 
80
43
  # Registers a new Preprocessor `klass` for `mime_type`.
@@ -83,197 +46,182 @@ module Sprockets
83
46
  #
84
47
  # A block can be passed for to create a shorthand processor.
85
48
  #
86
- # register_preprocessor :my_processor do |context, data|
87
- # data.gsub(...)
49
+ # register_preprocessor 'text/css' do |input|
50
+ # input[:data].gsub(...)
88
51
  # end
89
52
  #
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)
53
+ def register_preprocessor(*args, &block)
54
+ register_config_processor(:preprocessors, *args, &block)
55
+ compute_transformers!(self.config[:registered_transformers])
102
56
  end
57
+ alias_method :register_processor, :register_preprocessor
103
58
 
104
59
  # Registers a new Postprocessor `klass` for `mime_type`.
105
60
  #
106
- # register_postprocessor 'text/css', Sprockets::CharsetNormalizer
61
+ # register_postprocessor 'application/javascript', Sprockets::DirectiveProcessor
107
62
  #
108
63
  # A block can be passed for to create a shorthand processor.
109
64
  #
110
- # register_postprocessor :my_processor do |context, data|
111
- # data.gsub(...)
65
+ # register_postprocessor 'application/javascript' do |input|
66
+ # input[:data].gsub(...)
112
67
  # end
113
68
  #
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)
69
+ def register_postprocessor(*args, &block)
70
+ register_config_processor(:postprocessors, *args, &block)
71
+ compute_transformers!(self.config[:registered_transformers])
131
72
  end
132
73
 
133
74
  # Remove Preprocessor `klass` for `mime_type`.
134
75
  #
135
76
  # unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
136
77
  #
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)
78
+ def unregister_preprocessor(*args)
79
+ unregister_config_processor(:preprocessors, *args)
80
+ compute_transformers!(self.config[:registered_transformers])
148
81
  end
82
+ alias_method :unregister_processor, :unregister_preprocessor
149
83
 
150
84
  # Remove Postprocessor `klass` for `mime_type`.
151
85
  #
152
86
  # unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
153
87
  #
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)
88
+ def unregister_postprocessor(*args)
89
+ unregister_config_processor(:postprocessors, *args)
90
+ compute_transformers!(self.config[:registered_transformers])
165
91
  end
166
92
 
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
93
  # Bundle Processors are ran on concatenated assets rather than
172
94
  # 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
95
+ def bundle_processors
96
+ config[:bundle_processors]
182
97
  end
183
98
 
184
99
  # Registers a new Bundle Processor `klass` for `mime_type`.
185
100
  #
186
- # register_bundle_processor 'text/css', Sprockets::CharsetNormalizer
101
+ # register_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
187
102
  #
188
103
  # A block can be passed for to create a shorthand processor.
189
104
  #
190
- # register_bundle_processor :my_processor do |context, data|
191
- # data.gsub(...)
105
+ # register_bundle_processor 'application/javascript' do |input|
106
+ # input[:data].gsub(...)
192
107
  # end
193
108
  #
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)
109
+ def register_bundle_processor(*args, &block)
110
+ register_config_processor(:bundle_processors, *args, &block)
206
111
  end
207
112
 
208
113
  # Remove Bundle Processor `klass` for `mime_type`.
209
114
  #
210
- # unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
115
+ # unregister_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
211
116
  #
212
- def unregister_bundle_processor(mime_type, klass)
213
- expire_index!
117
+ def unregister_bundle_processor(*args)
118
+ unregister_config_processor(:bundle_processors, *args)
119
+ end
214
120
 
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
- }
121
+ # Public: Register bundle metadata reducer function.
122
+ #
123
+ # Examples
124
+ #
125
+ # Sprockets.register_bundle_metadata_reducer 'application/javascript', :jshint_errors, [], :+
126
+ #
127
+ # Sprockets.register_bundle_metadata_reducer 'text/css', :selector_count, 0 { |total, count|
128
+ # total + count
129
+ # }
130
+ #
131
+ # mime_type - String MIME Type. Use '*/*' applies to all types.
132
+ # key - Symbol metadata key
133
+ # initial - Initial memo to pass to the reduce funciton (default: nil)
134
+ # block - Proc accepting the memo accumulator and current value
135
+ #
136
+ # Returns nothing.
137
+ def register_bundle_metadata_reducer(mime_type, key, *args, &block)
138
+ case args.size
139
+ when 0
140
+ reducer = block
141
+ when 1
142
+ if block_given?
143
+ initial = args[0]
144
+ reducer = block
145
+ else
146
+ initial = nil
147
+ reducer = args[0].to_proc
148
+ end
149
+ when 2
150
+ initial = args[0]
151
+ reducer = args[1].to_proc
152
+ else
153
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0..2)"
220
154
  end
221
155
 
222
- @bundle_processors[mime_type].delete(klass)
156
+ self.config = hash_reassoc(config, :bundle_reducers, mime_type) do |reducers|
157
+ reducers.merge(key => [initial, reducer])
158
+ end
223
159
  end
224
160
 
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
161
+ protected
162
+ def resolve_processors_cache_key_uri(uri)
163
+ params = parse_uri_query_params(uri[11..-1])
164
+ processors = processors_for(params[:type], params[:file_type], params[:pipeline])
165
+ processors_cache_keys(processors)
166
+ end
232
167
 
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!
168
+ def build_processors_uri(type, file_type, pipeline)
169
+ query = encode_uri_query_params(
170
+ type: type,
171
+ file_type: file_type,
172
+ pipeline: pipeline
173
+ )
174
+ "processors:#{query}"
175
+ end
238
176
 
239
- unregister_bundle_processor 'text/css', :css_compressor
240
- return unless compressor
177
+ def processors_for(type, file_type, pipeline)
178
+ pipeline ||= :default
179
+ if fn = config[:pipelines][pipeline.to_sym]
180
+ fn.call(self, type, file_type)
181
+ else
182
+ raise Error, "no pipeline: #{pipeline}"
183
+ end
184
+ end
241
185
 
242
- register_bundle_processor 'text/css', :css_compressor do |context, data|
243
- compressor.compress(data)
186
+ def default_processors_for(type, file_type)
187
+ bundled_processors = config[:bundle_processors][type]
188
+ if bundled_processors.any?
189
+ bundled_processors
190
+ else
191
+ self_processors_for(type, file_type)
192
+ end
244
193
  end
245
- end
246
194
 
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
195
+ def self_processors_for(type, file_type)
196
+ processors = []
254
197
 
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!
198
+ processors.concat config[:postprocessors][type]
199
+ if type != file_type && processor = config[:transformers][file_type][type]
200
+ processors << processor
201
+ end
202
+ processors.concat config[:preprocessors][file_type]
260
203
 
261
- unregister_bundle_processor 'application/javascript', :js_compressor
262
- return unless compressor
204
+ if processors.any? || mime_type_charset_detecter(type)
205
+ processors << FileReader
206
+ end
263
207
 
264
- register_bundle_processor 'application/javascript', :js_compressor do |context, data|
265
- compressor.compress(data)
208
+ processors
266
209
  end
267
- end
268
210
 
269
211
  private
270
- def add_engine_to_trail(ext, klass)
271
- @trail.append_extension(ext.to_s)
212
+ def register_config_processor(type, mime_type, processor = nil, &block)
213
+ processor ||= block
214
+
215
+ self.config = hash_reassoc(config, type, mime_type) do |processors|
216
+ processors.unshift(processor)
217
+ processors
218
+ end
219
+ end
272
220
 
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)
276
- end
221
+ def unregister_config_processor(type, mime_type, processor)
222
+ self.config = hash_reassoc(config, type, mime_type) do |processors|
223
+ processors.delete_if { |p| p == processor || p.class == processor }
224
+ processors
277
225
  end
278
226
  end
279
227
  end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+
4
+ module Sprockets
5
+ # Functional utilities for dealing with Processor functions.
6
+ #
7
+ # A Processor is a general function that may modify or transform an asset as
8
+ # part of the pipeline. CoffeeScript to JavaScript conversion, Minification
9
+ # or Concatenation are all implemented as seperate Processor steps.
10
+ #
11
+ # Processors maybe any object that responds to call. So procs or a class that
12
+ # defines a self.call method.
13
+ #
14
+ # For ergonomics, processors may return a number of shorthand values.
15
+ # Unfortunately, this means that processors can not compose via ordinary
16
+ # function composition. The composition helpers here can help.
17
+ module ProcessorUtils
18
+ extend self
19
+
20
+ class CompositeProcessor < Struct.new(:processor_strategy, :param, :processors) # :nodoc:
21
+ SINGULAR = lambda { |param, input| ProcessorUtils.call_processor param, input }
22
+ PLURAL = lambda { |param, input| ProcessorUtils.call_processors param, input }
23
+
24
+ def self.create(processors)
25
+ if processors.length == 1
26
+ new SINGULAR, processors.first, processors
27
+ else
28
+ new PLURAL, processors, processors
29
+ end
30
+ end
31
+
32
+ def call(input)
33
+ processor_strategy.call param, input
34
+ end
35
+
36
+ def cache_key
37
+ ProcessorUtils.processors_cache_keys(processors)
38
+ end
39
+ end
40
+
41
+ # Public: Compose processors in right to left order.
42
+ #
43
+ # processors - Array of processors callables
44
+ #
45
+ # Returns a composed Proc.
46
+ def compose_processors(*processors)
47
+ CompositeProcessor.create processors
48
+ end
49
+
50
+ # Public: Invoke list of processors in right to left order.
51
+ #
52
+ # The right to left order processing mirrors standard function composition.
53
+ # Think about:
54
+ #
55
+ # bundle.call(uglify.call(coffee.call(input)))
56
+ #
57
+ # processors - Array of processor callables
58
+ # input - Hash of input data to pass to each processor
59
+ #
60
+ # Returns a Hash with :data and other processor metadata key/values.
61
+ def call_processors(processors, input)
62
+ data = input[:data] || ""
63
+ metadata = (input[:metadata] || {}).dup
64
+
65
+ processors.reverse_each do |processor|
66
+ result = call_processor(processor, input.merge(data: data, metadata: metadata))
67
+ data = result.delete(:data)
68
+ metadata.merge!(result)
69
+ end
70
+
71
+ metadata.merge(data: data)
72
+ end
73
+
74
+ # Public: Invoke processor.
75
+ #
76
+ # processor - Processor callables
77
+ # input - Hash of input data to pass to processor
78
+ #
79
+ # Returns a Hash with :data and other processor metadata key/values.
80
+ def call_processor(processor, input)
81
+ metadata = (input[:metadata] || {}).dup
82
+ metadata[:data] = input[:data]
83
+
84
+ case result = processor.call({data: "", metadata: {}}.merge(input))
85
+ when NilClass
86
+ metadata
87
+ when Hash
88
+ metadata.merge(result)
89
+ when String
90
+ metadata.merge(data: result)
91
+ else
92
+ raise TypeError, "invalid processor return type: #{result.class}"
93
+ end
94
+ end
95
+
96
+ # Internal: Get processor defined cached key.
97
+ #
98
+ # processor - Processor function
99
+ #
100
+ # Returns JSON serializable key or nil.
101
+ def processor_cache_key(processor)
102
+ processor.cache_key if processor.respond_to?(:cache_key)
103
+ end
104
+
105
+ # Internal: Get combined cache keys for set of processors.
106
+ #
107
+ # processors - Array of processor functions
108
+ #
109
+ # Returns Array of JSON serializable keys.
110
+ def processors_cache_keys(processors)
111
+ processors.map { |processor| processor_cache_key(processor) }
112
+ end
113
+
114
+ # Internal: Set of all "simple" value types allowed to be returned in
115
+ # processor metadata.
116
+ VALID_METADATA_VALUE_TYPES = Set.new([
117
+ String,
118
+ Symbol,
119
+ TrueClass,
120
+ FalseClass,
121
+ NilClass
122
+ ] + (0.class == Integer ? [Integer] : [Bignum, Fixnum])).freeze
123
+
124
+ # Internal: Set of all nested compound metadata types that can nest values.
125
+ VALID_METADATA_COMPOUND_TYPES = Set.new([
126
+ Array,
127
+ Hash,
128
+ Set
129
+ ]).freeze
130
+
131
+ # Internal: Hash of all "simple" value types allowed to be returned in
132
+ # processor metadata.
133
+ VALID_METADATA_VALUE_TYPES_HASH = VALID_METADATA_VALUE_TYPES.each_with_object({}) do |type, hash|
134
+ hash[type] = true
135
+ end.freeze
136
+
137
+ # Internal: Hash of all nested compound metadata types that can nest values.
138
+ VALID_METADATA_COMPOUND_TYPES_HASH = VALID_METADATA_COMPOUND_TYPES.each_with_object({}) do |type, hash|
139
+ hash[type] = true
140
+ end.freeze
141
+
142
+ # Internal: Set of all allowed metadata types.
143
+ VALID_METADATA_TYPES = (VALID_METADATA_VALUE_TYPES + VALID_METADATA_COMPOUND_TYPES).freeze
144
+
145
+ # Internal: Validate returned result of calling a processor pipeline and
146
+ # raise a friendly user error message.
147
+ #
148
+ # result - Metadata Hash returned from call_processors
149
+ #
150
+ # Returns result or raises a TypeError.
151
+ def validate_processor_result!(result)
152
+ if !result.instance_of?(Hash)
153
+ raise TypeError, "processor metadata result was expected to be a Hash, but was #{result.class}"
154
+ end
155
+
156
+ if !result[:data].instance_of?(String)
157
+ raise TypeError, "processor :data was expected to be a String, but as #{result[:data].class}"
158
+ end
159
+
160
+ result.each do |key, value|
161
+ if !key.instance_of?(Symbol)
162
+ raise TypeError, "processor metadata[#{key.inspect}] expected to be a Symbol"
163
+ end
164
+ end
165
+
166
+ result
167
+ end
168
+ end
169
+ end