sprockets 2.2.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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