sprockets 2.12.5 → 3.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +296 -0
  3. data/LICENSE +2 -2
  4. data/README.md +235 -262
  5. data/bin/sprockets +1 -0
  6. data/lib/rake/sprocketstask.rb +5 -4
  7. data/lib/sprockets/asset.rb +143 -212
  8. data/lib/sprockets/autoload/closure.rb +7 -0
  9. data/lib/sprockets/autoload/coffee_script.rb +7 -0
  10. data/lib/sprockets/autoload/eco.rb +7 -0
  11. data/lib/sprockets/autoload/ejs.rb +7 -0
  12. data/lib/sprockets/autoload/sass.rb +7 -0
  13. data/lib/sprockets/autoload/uglifier.rb +7 -0
  14. data/lib/sprockets/autoload/yui.rb +7 -0
  15. data/lib/sprockets/autoload.rb +11 -0
  16. data/lib/sprockets/base.rb +56 -393
  17. data/lib/sprockets/bower.rb +58 -0
  18. data/lib/sprockets/bundle.rb +69 -0
  19. data/lib/sprockets/cache/file_store.rb +168 -14
  20. data/lib/sprockets/cache/memory_store.rb +66 -0
  21. data/lib/sprockets/cache/null_store.rb +46 -0
  22. data/lib/sprockets/cache.rb +236 -0
  23. data/lib/sprockets/cached_environment.rb +69 -0
  24. data/lib/sprockets/closure_compressor.rb +35 -10
  25. data/lib/sprockets/coffee_script_processor.rb +25 -0
  26. data/lib/sprockets/coffee_script_template.rb +17 -0
  27. data/lib/sprockets/compressing.rb +44 -23
  28. data/lib/sprockets/configuration.rb +83 -0
  29. data/lib/sprockets/context.rb +86 -144
  30. data/lib/sprockets/dependencies.rb +73 -0
  31. data/lib/sprockets/deprecation.rb +90 -0
  32. data/lib/sprockets/digest_utils.rb +180 -0
  33. data/lib/sprockets/directive_processor.rb +207 -211
  34. data/lib/sprockets/eco_processor.rb +32 -0
  35. data/lib/sprockets/eco_template.rb +9 -30
  36. data/lib/sprockets/ejs_processor.rb +31 -0
  37. data/lib/sprockets/ejs_template.rb +9 -29
  38. data/lib/sprockets/encoding_utils.rb +261 -0
  39. data/lib/sprockets/engines.rb +53 -35
  40. data/lib/sprockets/environment.rb +17 -64
  41. data/lib/sprockets/erb_processor.rb +30 -0
  42. data/lib/sprockets/erb_template.rb +11 -0
  43. data/lib/sprockets/errors.rb +4 -13
  44. data/lib/sprockets/file_reader.rb +15 -0
  45. data/lib/sprockets/http_utils.rb +117 -0
  46. data/lib/sprockets/jst_processor.rb +35 -15
  47. data/lib/sprockets/legacy.rb +330 -0
  48. data/lib/sprockets/legacy_proc_processor.rb +35 -0
  49. data/lib/sprockets/legacy_tilt_processor.rb +29 -0
  50. data/lib/sprockets/loader.rb +325 -0
  51. data/lib/sprockets/manifest.rb +202 -127
  52. data/lib/sprockets/manifest_utils.rb +45 -0
  53. data/lib/sprockets/mime.rb +112 -31
  54. data/lib/sprockets/path_dependency_utils.rb +85 -0
  55. data/lib/sprockets/path_digest_utils.rb +47 -0
  56. data/lib/sprockets/path_utils.rb +287 -0
  57. data/lib/sprockets/paths.rb +42 -19
  58. data/lib/sprockets/processing.rb +178 -126
  59. data/lib/sprockets/processor_utils.rb +180 -0
  60. data/lib/sprockets/resolve.rb +211 -0
  61. data/lib/sprockets/sass_cache_store.rb +22 -17
  62. data/lib/sprockets/sass_compressor.rb +39 -15
  63. data/lib/sprockets/sass_functions.rb +2 -70
  64. data/lib/sprockets/sass_importer.rb +2 -30
  65. data/lib/sprockets/sass_processor.rb +292 -0
  66. data/lib/sprockets/sass_template.rb +12 -59
  67. data/lib/sprockets/server.rb +129 -84
  68. data/lib/sprockets/transformers.rb +145 -0
  69. data/lib/sprockets/uglifier_compressor.rb +39 -12
  70. data/lib/sprockets/unloaded_asset.rb +137 -0
  71. data/lib/sprockets/uri_tar.rb +98 -0
  72. data/lib/sprockets/uri_utils.rb +188 -0
  73. data/lib/sprockets/utils/gzip.rb +67 -0
  74. data/lib/sprockets/utils.rb +210 -44
  75. data/lib/sprockets/version.rb +1 -1
  76. data/lib/sprockets/yui_compressor.rb +39 -11
  77. data/lib/sprockets.rb +142 -81
  78. metadata +96 -90
  79. data/lib/sprockets/asset_attributes.rb +0 -137
  80. data/lib/sprockets/bundled_asset.rb +0 -78
  81. data/lib/sprockets/caching.rb +0 -96
  82. data/lib/sprockets/charset_normalizer.rb +0 -41
  83. data/lib/sprockets/index.rb +0 -100
  84. data/lib/sprockets/processed_asset.rb +0 -152
  85. data/lib/sprockets/processor.rb +0 -32
  86. data/lib/sprockets/safety_colons.rb +0 -28
  87. data/lib/sprockets/scss_template.rb +0 -13
  88. data/lib/sprockets/static_asset.rb +0 -60
@@ -1,62 +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
- # Returns an `Array` of format extension `String`s.
11
- #
12
- # format_extensions
13
- # # => ['.js', '.css']
14
- #
15
- def format_extensions
16
- @trail.extensions - @engines.keys
14
+ include ProcessorUtils, URIUtils, Utils
15
+
16
+ def pipelines
17
+ config[:pipelines]
17
18
  end
18
19
 
19
- # Deprecated alias for `preprocessors`.
20
- def processors(*args)
21
- preprocessors(*args)
20
+ def register_pipeline(name, proc = nil, &block)
21
+ proc ||= block
22
+
23
+ self.config = hash_reassoc(config, :pipelines) do |pipelines|
24
+ pipelines.merge(name.to_sym => proc)
25
+ end
22
26
  end
23
27
 
24
- # Returns an `Array` of `Processor` classes. If a `mime_type`
25
- # argument is supplied, the processors registered under that
26
- # extension will be returned.
27
- #
28
28
  # Preprocessors are ran before Postprocessors and Engine
29
29
  # processors.
30
- #
31
- # All `Processor`s must follow the `Tilt::Template` interface. It is
32
- # recommended to subclass `Tilt::Template`.
33
- def preprocessors(mime_type = nil)
34
- if mime_type
35
- @preprocessors[mime_type].dup
36
- else
37
- deep_copy_hash(@preprocessors)
38
- end
30
+ def preprocessors
31
+ config[:preprocessors]
39
32
  end
33
+ alias_method :processors, :preprocessors
40
34
 
41
- # Returns an `Array` of `Processor` classes. If a `mime_type`
42
- # argument is supplied, the processors registered under that
43
- # extension will be returned.
44
- #
45
35
  # Postprocessors are ran after Preprocessors and Engine processors.
46
- #
47
- # All `Processor`s must follow the `Tilt::Template` interface. It is
48
- # recommended to subclass `Tilt::Template`.
49
- def postprocessors(mime_type = nil)
50
- if mime_type
51
- @postprocessors[mime_type].dup
52
- else
53
- deep_copy_hash(@postprocessors)
54
- end
55
- end
56
-
57
- # Deprecated alias for `register_preprocessor`.
58
- def register_processor(*args, &block)
59
- register_preprocessor(*args, &block)
36
+ def postprocessors
37
+ config[:postprocessors]
60
38
  end
61
39
 
62
40
  # Registers a new Preprocessor `klass` for `mime_type`.
@@ -69,137 +47,211 @@ module Sprockets
69
47
  # data.gsub(...)
70
48
  # end
71
49
  #
72
- def register_preprocessor(mime_type, klass, &block)
73
- if block_given?
74
- name = klass.to_s
75
- klass = Class.new(Processor) do
76
- @name = name
77
- @processor = block
78
- end
79
- end
80
-
81
- @preprocessors[mime_type].push(klass)
50
+ def register_preprocessor(*args, &block)
51
+ register_config_processor(:preprocessors, *args, &block)
82
52
  end
53
+ alias_method :register_processor, :register_preprocessor
83
54
 
84
55
  # Registers a new Postprocessor `klass` for `mime_type`.
85
56
  #
86
- # register_postprocessor 'text/css', Sprockets::CharsetNormalizer
57
+ # register_postprocessor 'application/javascript', Sprockets::DirectiveProcessor
87
58
  #
88
59
  # A block can be passed for to create a shorthand processor.
89
60
  #
90
- # register_postprocessor 'text/css', :my_processor do |context, data|
61
+ # register_postprocessor 'application/javascript', :my_processor do |context, data|
91
62
  # data.gsub(...)
92
63
  # end
93
64
  #
94
- def register_postprocessor(mime_type, klass, &block)
95
- if block_given?
96
- name = klass.to_s
97
- klass = Class.new(Processor) do
98
- @name = name
99
- @processor = block
100
- end
101
- end
102
-
103
- @postprocessors[mime_type].push(klass)
104
- end
105
-
106
- # Deprecated alias for `unregister_preprocessor`.
107
- def unregister_processor(*args)
108
- unregister_preprocessor(*args)
65
+ def register_postprocessor(*args, &block)
66
+ register_config_processor(:postprocessors, *args, &block)
109
67
  end
110
68
 
111
69
  # Remove Preprocessor `klass` for `mime_type`.
112
70
  #
113
71
  # unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
114
72
  #
115
- def unregister_preprocessor(mime_type, klass)
116
- if klass.is_a?(String) || klass.is_a?(Symbol)
117
- klass = @preprocessors[mime_type].detect { |cls|
118
- cls.respond_to?(:name) &&
119
- cls.name == "Sprockets::Processor (#{klass})"
120
- }
121
- end
122
-
123
- @preprocessors[mime_type].delete(klass)
73
+ def unregister_preprocessor(*args)
74
+ unregister_config_processor(:preprocessors, *args)
124
75
  end
76
+ alias_method :unregister_processor, :unregister_preprocessor
125
77
 
126
78
  # Remove Postprocessor `klass` for `mime_type`.
127
79
  #
128
80
  # unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
129
81
  #
130
- def unregister_postprocessor(mime_type, klass)
131
- if klass.is_a?(String) || klass.is_a?(Symbol)
132
- klass = @postprocessors[mime_type].detect { |cls|
133
- cls.respond_to?(:name) &&
134
- cls.name == "Sprockets::Processor (#{klass})"
135
- }
136
- end
137
-
138
- @postprocessors[mime_type].delete(klass)
82
+ def unregister_postprocessor(*args)
83
+ unregister_config_processor(:postprocessors, *args)
139
84
  end
140
85
 
141
- # Returns an `Array` of `Processor` classes. If a `mime_type`
142
- # argument is supplied, the processors registered under that
143
- # extension will be returned.
144
- #
145
86
  # Bundle Processors are ran on concatenated assets rather than
146
87
  # individual files.
147
- #
148
- # All `Processor`s must follow the `Tilt::Template` interface. It is
149
- # recommended to subclass `Tilt::Template`.
150
- def bundle_processors(mime_type = nil)
151
- if mime_type
152
- @bundle_processors[mime_type].dup
153
- else
154
- deep_copy_hash(@bundle_processors)
155
- end
88
+ def bundle_processors
89
+ config[:bundle_processors]
156
90
  end
157
91
 
158
92
  # Registers a new Bundle Processor `klass` for `mime_type`.
159
93
  #
160
- # register_bundle_processor 'text/css', Sprockets::CharsetNormalizer
94
+ # register_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
161
95
  #
162
96
  # A block can be passed for to create a shorthand processor.
163
97
  #
164
- # register_bundle_processor 'text/css', :my_processor do |context, data|
98
+ # register_bundle_processor 'application/javascript', :my_processor do |context, data|
165
99
  # data.gsub(...)
166
100
  # end
167
101
  #
168
- def register_bundle_processor(mime_type, klass, &block)
169
- if block_given?
170
- name = klass.to_s
171
- klass = Class.new(Processor) do
172
- @name = name
173
- @processor = block
174
- end
175
- end
176
-
177
- @bundle_processors[mime_type].push(klass)
102
+ def register_bundle_processor(*args, &block)
103
+ register_config_processor(:bundle_processors, *args, &block)
178
104
  end
179
105
 
180
106
  # Remove Bundle Processor `klass` for `mime_type`.
181
107
  #
182
- # unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
108
+ # unregister_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
109
+ #
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
183
117
  #
184
- def unregister_bundle_processor(mime_type, klass)
185
- if klass.is_a?(String) || klass.is_a?(Symbol)
186
- klass = @bundle_processors[mime_type].detect { |cls|
187
- cls.respond_to?(:name) &&
188
- cls.name == "Sprockets::Processor (#{klass})"
189
- }
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)"
190
147
  end
191
148
 
192
- @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
193
152
  end
194
153
 
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
161
+
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
172
+
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
177
+
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
185
+ end
186
+
187
+ def self_processors_for(type, file_type, engine_extnames)
188
+ processors = []
189
+
190
+ processors.concat config[:postprocessors][type]
191
+
192
+ if type != file_type && processor = config[:transformers][file_type][type]
193
+ processors << processor
194
+ end
195
+
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
202
+
203
+ processors
204
+ end
205
+
195
206
  private
196
- def add_engine_to_trail(ext, klass)
197
- @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
233
+
234
+ def deprecate_legacy_processor_interface(interface)
235
+ msg = "You are using a deprecated processor interface #{ interface.inspect }.\n" +
236
+ "Please update your processor interface:\n" +
237
+ "https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md#supporting-all-versions-of-sprockets-in-processors\n"
238
+
239
+ Deprecation.new([caller[3]]).warn msg
240
+ end
198
241
 
199
- if klass.respond_to?(:default_mime_type) && klass.default_mime_type
200
- if format_ext = extension_for_mime_type(klass.default_mime_type)
201
- @trail.alias_extension(ext.to_s, format_ext)
242
+ def wrap_processor(klass, proc)
243
+ if !proc
244
+ if klass.respond_to?(:call)
245
+ klass
246
+ else
247
+ deprecate_legacy_processor_interface(klass)
248
+ LegacyTiltProcessor.new(klass)
202
249
  end
250
+ elsif proc.respond_to?(:arity) && proc.arity == 2
251
+ deprecate_legacy_processor_interface(proc)
252
+ LegacyProcProcessor.new(klass.to_s, proc)
253
+ else
254
+ proc
203
255
  end
204
256
  end
205
257
  end
@@ -0,0 +1,180 @@
1
+ require 'set'
2
+
3
+ module Sprockets
4
+ # Functional utilities for dealing with Processor functions.
5
+ #
6
+ # A Processor is a general function that my modify or transform an asset as
7
+ # part of the pipeline. CoffeeScript to JavaScript conversion, Minification
8
+ # or Concatenation are all implemented as seperate Processor steps.
9
+ #
10
+ # Processors maybe any object that responds to call. So procs or a class that
11
+ # defines a self.call method.
12
+ #
13
+ # For ergonomics, processors may return a number of shorthand values.
14
+ # Unfortunately, this means that processors can not compose via ordinary
15
+ # function composition. The composition helpers here can help.
16
+ module ProcessorUtils
17
+ extend self
18
+
19
+ # Public: Compose processors in right to left order.
20
+ #
21
+ # processors - Array of processors callables
22
+ #
23
+ # Returns a composed Proc.
24
+ def compose_processors(*processors)
25
+ context = self
26
+
27
+ if processors.length == 1
28
+ obj = method(:call_processor).to_proc.curry[processors.first]
29
+ else
30
+ obj = method(:call_processors).to_proc.curry[processors]
31
+ end
32
+
33
+ metaclass = (class << obj; self; end)
34
+ metaclass.send(:define_method, :cache_key) do
35
+ context.processors_cache_keys(processors)
36
+ end
37
+
38
+ obj
39
+ end
40
+
41
+ # Public: Invoke list of processors in right to left order.
42
+ #
43
+ # The right to left order processing mirrors standard function composition.
44
+ # Think about:
45
+ #
46
+ # bundle.call(uglify.call(coffee.call(input)))
47
+ #
48
+ # processors - Array of processor callables
49
+ # input - Hash of input data to pass to each processor
50
+ #
51
+ # Returns a Hash with :data and other processor metadata key/values.
52
+ def call_processors(processors, input)
53
+ data = input[:data] || ""
54
+ metadata = (input[:metadata] || {}).dup
55
+
56
+ processors.reverse_each do |processor|
57
+ result = call_processor(processor, input.merge(data: data, metadata: metadata))
58
+ data = result.delete(:data)
59
+ metadata.merge!(result)
60
+ end
61
+
62
+ metadata.merge(data: data)
63
+ end
64
+
65
+ # Public: Invoke processor.
66
+ #
67
+ # processor - Processor callables
68
+ # input - Hash of input data to pass to processor
69
+ #
70
+ # Returns a Hash with :data and other processor metadata key/values.
71
+ def call_processor(processor, input)
72
+ metadata = (input[:metadata] || {}).dup
73
+ metadata[:data] = input[:data]
74
+
75
+ case result = processor.call({data: "", metadata: {}}.merge(input))
76
+ when NilClass
77
+ metadata
78
+ when Hash
79
+ metadata.merge(result)
80
+ when String
81
+ metadata.merge(data: result)
82
+ else
83
+ raise TypeError, "invalid processor return type: #{result.class}"
84
+ end
85
+ end
86
+
87
+ # Internal: Get processor defined cached key.
88
+ #
89
+ # processor - Processor function
90
+ #
91
+ # Returns JSON serializable key or nil.
92
+ def processor_cache_key(processor)
93
+ processor.cache_key if processor.respond_to?(:cache_key)
94
+ end
95
+
96
+ # Internal: Get combined cache keys for set of processors.
97
+ #
98
+ # processors - Array of processor functions
99
+ #
100
+ # Returns Array of JSON serializable keys.
101
+ def processors_cache_keys(processors)
102
+ processors.map { |processor| processor_cache_key(processor) }
103
+ end
104
+
105
+ # Internal: Set of all "simple" value types allowed to be returned in
106
+ # processor metadata.
107
+ VALID_METADATA_VALUE_TYPES = Set.new([
108
+ String,
109
+ Symbol,
110
+ TrueClass,
111
+ FalseClass,
112
+ NilClass
113
+ ] + (0.class == Integer ? [Integer] : [Bignum, Fixnum])).freeze
114
+
115
+ # Internal: Set of all nested compound metadata types that can nest values.
116
+ VALID_METADATA_COMPOUND_TYPES = Set.new([
117
+ Array,
118
+ Hash,
119
+ Set
120
+ ]).freeze
121
+
122
+ # Internal: Hash of all "simple" value types allowed to be returned in
123
+ # processor metadata.
124
+ VALID_METADATA_VALUE_TYPES_HASH = VALID_METADATA_VALUE_TYPES.each_with_object({}) do |type, hash|
125
+ hash[type] = true
126
+ end.freeze
127
+
128
+ # Internal: Hash of all nested compound metadata types that can nest values.
129
+ VALID_METADATA_COMPOUND_TYPES_HASH = VALID_METADATA_COMPOUND_TYPES.each_with_object({}) do |type, hash|
130
+ hash[type] = true
131
+ end.freeze
132
+
133
+ # Internal: Set of all allowed metadata types.
134
+ VALID_METADATA_TYPES = (VALID_METADATA_VALUE_TYPES + VALID_METADATA_COMPOUND_TYPES).freeze
135
+
136
+ # Internal: Validate returned result of calling a processor pipeline and
137
+ # raise a friendly user error message.
138
+ #
139
+ # result - Metadata Hash returned from call_processors
140
+ #
141
+ # Returns result or raises a TypeError.
142
+ def validate_processor_result!(result)
143
+ if !result.instance_of?(Hash)
144
+ raise TypeError, "processor metadata result was expected to be a Hash, but was #{result.class}"
145
+ end
146
+
147
+ if !result[:data].instance_of?(String)
148
+ raise TypeError, "processor :data was expected to be a String, but as #{result[:data].class}"
149
+ end
150
+
151
+ result.each do |key, value|
152
+ if !key.instance_of?(Symbol)
153
+ raise TypeError, "processor metadata[#{key.inspect}] expected to be a Symbol"
154
+ end
155
+
156
+ if !valid_processor_metadata_value?(value)
157
+ raise TypeError, "processor metadata[:#{key}] returned a complex type: #{value.inspect}\n" +
158
+ "Only #{VALID_METADATA_TYPES.to_a.join(", ")} maybe used."
159
+ end
160
+ end
161
+
162
+ result
163
+ end
164
+
165
+ # Internal: Validate object is in validate metadata whitelist.
166
+ #
167
+ # value - Any Object
168
+ #
169
+ # Returns true if class is in whitelist otherwise false.
170
+ def valid_processor_metadata_value?(value)
171
+ if VALID_METADATA_VALUE_TYPES_HASH[value.class]
172
+ true
173
+ elsif VALID_METADATA_COMPOUND_TYPES_HASH[value.class]
174
+ value.all? { |v| valid_processor_metadata_value?(v) }
175
+ else
176
+ false
177
+ end
178
+ end
179
+ end
180
+ end