darkroom 0.0.8 → 0.0.10

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.
@@ -4,6 +4,7 @@ require('base64')
4
4
  require('digest')
5
5
  require('set')
6
6
 
7
+ require_relative('delegate')
7
8
  require_relative('errors/asset_error')
8
9
  require_relative('errors/asset_not_found_error')
9
10
  require_relative('errors/circular_reference_error')
@@ -12,9 +13,7 @@ require_relative('errors/processing_error')
12
13
  require_relative('errors/unrecognized_extension_error')
13
14
 
14
15
  class Darkroom
15
- ##
16
16
  # Represents an asset.
17
- #
18
17
  class Asset
19
18
  EXTENSION_REGEX = /(?=\.\w+)/.freeze
20
19
  DEFAULT_QUOTE = '\''
@@ -28,6 +27,8 @@ class Darkroom
28
27
  \k<quote>
29
28
  /x.freeze
30
29
 
30
+ BUILT_IN_PARSE_KINDS = [:import, :reference].freeze
31
+
31
32
  # First item of each set is used as default, so order is important.
32
33
  REFERENCE_FORMATS = {
33
34
  'path' => Set.new(%w[versioned unversioned]),
@@ -36,18 +37,16 @@ class Darkroom
36
37
 
37
38
  attr_reader(:errors, :path, :path_unversioned)
38
39
 
39
- ##
40
- # Creates a new instance.
41
- #
42
- # [file] Path of file on disk.
43
- # [path] Path this asset will be referenced by (e.g. /js/app.js).
44
- # [darkroom] Darkroom instance that the asset is a member of.
45
- # [prefix:] Prefix to apply to unversioned and versioned paths.
46
- # [entry:] Boolean indicating whether or not the asset is an entry point (i.e. accessible externally).
47
- # [minify:] Boolean specifying whether or not the asset should be minified when processed.
48
- # [intermediate:] Boolean indicating whether or not the asset exists solely to provide an intermediate
49
- # form (e.g. compiled) to another asset instance.
40
+ # Public: Create a new instance.
50
41
  #
42
+ # path - String path this asset will be referenced by (e.g. /js/app.js).
43
+ # file - String absolute path of file on disk.
44
+ # darkroom - Darkroom instance that the asset is a member of.
45
+ # prefix: - String prefix to apply to unversioned and versioned paths.
46
+ # entry: - Boolean specifying if the asset is an entry point (i.e. accessible externally).
47
+ # minify: - Boolean specifying if the asset should be minified when processed.
48
+ # intermediate: - Boolean specifying if the asset exists solely to provide an intermediate form (e.g.
49
+ # compiled) for another Asset instance.
51
50
  def initialize(path, file, darkroom, prefix: nil, entry: true, minify: false, intermediate: false)
52
51
  @path = path
53
52
  @dir = File.dirname(path)
@@ -65,7 +64,8 @@ class Darkroom
65
64
 
66
65
  if @delegate.compile_delegate && !intermediate
67
66
  @delegate = @delegate.compile_delegate
68
- @intermediate_asset = Asset.new(@path, @file, @darkroom,
67
+ @intermediate_asset = Asset.new(
68
+ @path, @file, @darkroom,
69
69
  prefix: @prefix,
70
70
  entry: false,
71
71
  minify: false,
@@ -77,12 +77,13 @@ class Darkroom
77
77
  clear
78
78
  end
79
79
 
80
- ##
81
- # Processes the asset if modified since the last run (see #modified? for how modification is
82
- # determined). File is read from disk, references are substituted (if supported), content is compiled
83
- # (if required), imports are prefixed to its content (if supported), and content is minified
84
- # (if supported and enabled and the asset is an entry point).
80
+ # Public: Process the asset if it's been modified since the last run (see #modified? for how
81
+ # modification is determined). The asset file is read from disk, references are substituted (if
82
+ # supported for the asset type), content is compiled (if required), imports are prefixed to the asset's
83
+ # own content (if supported), and content is minified (if supported and enabled and the asset is an
84
+ # entry point).
85
85
  #
86
+ # Returns nothing.
86
87
  def process
87
88
  return if ran?(:process)
88
89
 
@@ -90,107 +91,98 @@ class Darkroom
90
91
  content if entry?
91
92
  end
92
93
 
93
- ##
94
- # Returns the HTTP MIME type string.
94
+ # Public: Get the HTTP MIME type string for this asset.
95
95
  #
96
+ # Returns the String content type from the asset's Delegate.
96
97
  def content_type
97
98
  @delegate.content_type
98
99
  end
99
100
 
100
- ##
101
- # Returns boolean indicating whether or not the asset is binary.
101
+ # Public: Check if the asset's content is binary.
102
102
  #
103
+ # Returns the boolean result.
103
104
  def binary?
104
- return @is_binary if defined?(@is_binary)
105
+ return @binary if defined?(@binary)
105
106
 
106
107
  type, subtype = content_type.split('/')
107
108
 
108
- @is_binary = type != 'text' && !subtype.include?('json') && !subtype.include?('xml')
109
+ @binary = type != 'text' && !subtype.include?('json') && !subtype.include?('xml')
109
110
  end
110
111
 
111
- ##
112
- # Returns boolean indicating whether or not the asset is a font.
112
+ # Public: Check if the asset is a font.
113
113
  #
114
+ # Returns the boolean result.
114
115
  def font?
115
116
  defined?(@is_font) ? @is_font : (@is_font = content_type.start_with?('font/'))
116
117
  end
117
118
 
118
- ##
119
- # Returns boolean indicating whether or not the asset is an image.
119
+ # Public: Check if the asset is an image.
120
120
  #
121
+ # Returns the boolean result.
121
122
  def image?
122
123
  defined?(@is_image) ? @is_image : (@is_image = content_type.start_with?('image/'))
123
124
  end
124
125
 
125
- ##
126
- # Returns boolean indicating whether or not the asset is an entry point.
126
+ # Public: Check if the asset is an entry point.
127
127
  #
128
+ # Returns the boolean result.
128
129
  def entry?
129
130
  @entry
130
131
  end
131
132
 
132
- ##
133
- # DEPRECATED: use #entry? instead. Returns boolean indicating whether or not the asset is marked as
134
- # internal.
135
- #
136
- def internal?
137
- Darkroom.deprecated("#{self.class.name}#internal? is deprecated: use #entry? instead")
138
-
139
- !entry?
140
- end
141
-
142
- ##
143
- # Returns boolean indicating whether or not an error was encountered the last time the asset was
144
- # processed.
133
+ # Public: Check if one or more errors were encountered the last time the asset was processed.
145
134
  #
135
+ # Returns the boolean result.
146
136
  def error?
147
- !@errors.empty?
137
+ @errors && !@errors.empty?
148
138
  end
149
139
 
150
- ##
151
- # Returns ProcessingError wrapper of all errors if any exist, or nil if there are none.
140
+ # Public: Get a single error wrapper object for all errors.
152
141
  #
142
+ # Returns a ProcessingError if one or more errors exit or nil otherwise.
153
143
  def error
154
- @error ||= @errors.empty? ? nil : ProcessingError.new(@errors)
144
+ @error ||= error? ? ProcessingError.new(@errors) : nil
155
145
  end
156
146
 
157
- ##
158
- # Returns hash of content.
147
+ # Public: Get an MD5 hash of the asset's content.
159
148
  #
149
+ # Returns the String hash.
160
150
  def fingerprint
161
151
  content
162
152
 
163
153
  @fingerprint
164
154
  end
165
155
 
166
- ##
167
- # Returns versioned path.
156
+ # Public: Get the versioned path of the asset (includes the fingerprint).
168
157
  #
158
+ # Returns the String versioned path.
169
159
  def path_versioned
170
160
  content
171
161
 
172
162
  @path_versioned
173
163
  end
174
164
 
175
- ##
176
- # Returns appropriate HTTP headers.
165
+ # Public: Get the asset's HTTP headers.
177
166
  #
178
- # [versioned:] Uses Cache-Control header with max-age if +true+ and ETag header if +false+.
167
+ # versioned: - Boolean indicating if this is for the versioned or unversioned asset path. If true,
168
+ # a Cache-Control header with max-age is included; if false, an ETag header is used.
179
169
  #
170
+ # Returns a Hash of String HTTP header names and String values.
180
171
  def headers(versioned: true)
181
172
  {
182
173
  'Content-Type' => content_type,
183
174
  'Cache-Control' => ('public, max-age=31536000' if versioned),
184
- 'ETag' => ("\"#{fingerprint}\"" if !versioned),
175
+ 'ETag' => ("\"#{fingerprint}\"" unless versioned),
185
176
  }.compact!
186
177
  end
187
178
 
188
- ##
189
- # Returns subresource integrity string.
179
+ # Public: Get a subresource integrity string (SHA digest).
190
180
  #
191
- # [algorithm] Hash algorithm to use to generate the integrity string (one of +:sha256+, +:sha384+, or
192
- # +:sha512+).
181
+ # algorithm - Symbol hash algorithm name to use to generate the integrity string (must be one of
182
+ # :sha256, :sha384, :sha512).
193
183
  #
184
+ # Returns the String SHA digest.
185
+ # Raises RuntimeError if the request algorithm is not valid.
194
186
  def integrity(algorithm = :sha384)
195
187
  @integrity[algorithm] ||= "#{algorithm}-#{Base64.strict_encode64(
196
188
  case algorithm
@@ -202,11 +194,11 @@ class Darkroom
202
194
  )}".freeze
203
195
  end
204
196
 
205
- ##
206
- # Returns full asset content.
197
+ # Public: Get the full asset content, including imports and asset reference content substitutions.
207
198
  #
208
- # [minified:] Boolean indicating whether or not to return minified version if it is available.
199
+ # minified: - Boolean specifying if the minified version is desired.
209
200
  #
201
+ # Returns the String asset content, minified if requested (and the asset is minifiable).
210
202
  def content(minified: @minify)
211
203
  unless ran?(:content)
212
204
  compile
@@ -231,7 +223,7 @@ class Darkroom
231
223
  )
232
224
 
233
225
  @content = finalized if finalized.kind_of?(String)
234
- rescue => e
226
+ rescue StandardError => e
235
227
  @errors << e
236
228
  end
237
229
  end
@@ -243,7 +235,7 @@ class Darkroom
243
235
  path: @path,
244
236
  content: @content,
245
237
  )
246
- rescue => e
238
+ rescue StandardError => e
247
239
  @errors << e
248
240
  end
249
241
  end
@@ -257,36 +249,39 @@ class Darkroom
257
249
  @content_minified.freeze
258
250
  end
259
251
 
260
- ##
261
- # Returns high-level object info string.
252
+ # Public: Get a high-level object info string about this Asset instance.
262
253
  #
254
+ # Returns the String.
263
255
  def inspect
264
- "#<#{self.class}: "\
265
- "@entry=#{@entry.inspect}, "\
266
- "@errors=#{@errors.inspect}, "\
267
- "@extension=#{@extension.inspect}, "\
268
- "@file=#{@file.inspect}, "\
269
- "@fingerprint=#{@fingerprint.inspect}, "\
270
- "@minify=#{@minify.inspect}, "\
271
- "@mtime=#{@mtime.inspect}, "\
272
- "@path=#{@path.inspect}, "\
273
- "@path_unversioned=#{@path_unversioned.inspect}, "\
274
- "@path_versioned=#{@path_versioned.inspect}, "\
275
- "@prefix=#{@prefix.inspect}"\
256
+ "#<#{self.class} " \
257
+ "@delegate=#{@delegate.inspect}, " \
258
+ "@dir=#{@dir.inspect}, " \
259
+ "@entry=#{@entry.inspect}, " \
260
+ "@errors=#{@errors.inspect}, " \
261
+ "@extension=#{@extension.inspect}, " \
262
+ "@file=#{@file.inspect}, " \
263
+ "@fingerprint=#{@fingerprint.inspect}, " \
264
+ "@minify=#{@minify.inspect}, " \
265
+ "@modified=#{@modified.inspect}, " \
266
+ "@mtime=#{@mtime.inspect}, " \
267
+ "@path=#{@path.inspect}, " \
268
+ "@path_unversioned=#{@path_unversioned.inspect}, " \
269
+ "@path_versioned=#{@path_versioned.inspect}, " \
270
+ "@prefix=#{@prefix.inspect}" \
276
271
  '>'
277
272
  end
278
273
 
279
274
  protected
280
275
 
281
- ##
282
- # Returns true if the asset or any of its dependencies were modified since last processed, or if an
276
+ # Internal: Check if the asset or any of its dependencies were modified since last processed, or if an
283
277
  # error was recorded during the last processing run.
284
278
  #
279
+ # Returns the boolean result.
285
280
  def modified?
286
281
  @modified_key == @darkroom.process_key ? (return @modified) : @modified_key = @darkroom.process_key
287
282
 
288
283
  begin
289
- @modified = !!@error
284
+ @modified = error?
290
285
  @modified ||= (@mtime != (@mtime = File.mtime(@file)))
291
286
  @modified ||= @intermediate_asset.modified? if @intermediate_asset
292
287
  @modified ||= dependencies.any? { |d| d.modified? }
@@ -299,9 +294,9 @@ class Darkroom
299
294
  end
300
295
  end
301
296
 
302
- ##
303
- # Clears content, dependencies, and errors so asset is ready for (re)processing.
297
+ # Internal: Clear content, dependencies, and errors so asset is ready for (re)processing.
304
298
  #
299
+ # Returns nothing.
305
300
  def clear
306
301
  return if ran?(:clear)
307
302
 
@@ -325,9 +320,9 @@ class Darkroom
325
320
  @errors = []
326
321
  end
327
322
 
328
- ##
329
- # Reads the asset file into memory.
323
+ # Internal: Read the asset file into memory.
330
324
  #
325
+ # Returns nothing.
331
326
  def read
332
327
  return if ran?(:read)
333
328
 
@@ -346,9 +341,9 @@ class Darkroom
346
341
  end
347
342
  end
348
343
 
349
- ##
350
- # Parses own content to build list of imports and references.
344
+ # Internal: Parse the asset's own content to build the lists of imports and references.
351
345
  #
346
+ # Returns nothing.
352
347
  def parse
353
348
  return if ran?(:parse)
354
349
 
@@ -359,7 +354,7 @@ class Darkroom
359
354
  match = Regexp.last_match
360
355
  asset = nil
361
356
 
362
- if kind == :import || kind == :reference
357
+ if BUILT_IN_PARSE_KINDS.include?(kind)
363
358
  path = File.expand_path(match[:path], @dir)
364
359
  asset = @darkroom.manifest(path)
365
360
 
@@ -372,18 +367,18 @@ class Darkroom
372
367
  end
373
368
  end
374
369
 
375
- ##
376
- # Returns direct dependencies (ones explicitly specified in the asset's own content.)
370
+ # Internal: Get direct dependencies (ones explicitly specified in the asset's own content).
377
371
  #
372
+ # Returns an Array of Asset objects.
378
373
  def own_dependencies
379
374
  parse
380
375
 
381
376
  @own_dependencies
382
377
  end
383
378
 
384
- ##
385
- # Returns all dependencies (including dependencies of dependencies).
379
+ # Internal: Get all dependencies (including dependencies of dependencies).
386
380
  #
381
+ # Returns an Array of Asset objects.
387
382
  def dependencies
388
383
  unless ran?(:dependencies)
389
384
  parse
@@ -393,18 +388,18 @@ class Darkroom
393
388
  @dependencies
394
389
  end
395
390
 
396
- ##
397
- # Returns direct imports (ones explicitly specified in the asset's own content.)
391
+ # Internal: Get direct imports (ones explicitly specified in the asset's own content).
398
392
  #
393
+ # Returns an Array of Asset objects.
399
394
  def own_imports
400
395
  parse
401
396
 
402
397
  @own_imports
403
398
  end
404
399
 
405
- ##
406
- # Returns all imports (including imports of imports).
400
+ # Internal: Get all imports (including imports of imports).
407
401
  #
402
+ # Returns an Array of Asset objects.
408
403
  def imports
409
404
  unless ran?(:imports)
410
405
  parse
@@ -414,18 +409,18 @@ class Darkroom
414
409
  @imports
415
410
  end
416
411
 
417
- ##
418
- # Performs import and reference substitutions based on parse matches.
412
+ # Internal: Perform import and reference substitutions based on parse matches.
419
413
  #
414
+ # Returns nothing.
420
415
  def substitute
421
416
  return if ran?(:substitute)
422
417
 
423
418
  parse
424
419
  substitutions = []
425
420
 
426
- @parse_matches.sort_by! { |_, match, __| match.begin(0) }.each do |kind, match, asset|
427
- format = nil
421
+ @parse_matches.sort_by! { |_kind, match, _asset| match.begin(0) }
428
422
 
423
+ @parse_matches.each do |kind, match, asset|
429
424
  handler = @delegate.handler(kind)
430
425
  handler_args = {
431
426
  parse_data: @parse_data,
@@ -433,24 +428,29 @@ class Darkroom
433
428
  }
434
429
  handler_args[:asset] = asset if asset
435
430
 
436
- if !asset && (kind == :import || kind == :reference)
437
- add_parse_error(match, AssetNotFoundError)
431
+ if !asset && BUILT_IN_PARSE_KINDS.include?(kind)
432
+ add_parse_error(:not_found, match)
438
433
  next
439
434
  elsif kind == :reference
435
+ entity = match[:entity]
440
436
  format = match[:format]
441
- format = REFERENCE_FORMATS[match[:entity]].first if format.nil? || format == ''
437
+
438
+ allowed_formats = REFERENCE_FORMATS[entity]
439
+ format = allowed_formats&.first if format.nil? || format == ''
442
440
 
443
441
  handler_args[:format] = format
444
442
 
445
443
  if asset.dependencies.include?(self)
446
- add_parse_error(match, CircularReferenceError)
444
+ add_parse_error(:circular_reference, match)
445
+ next
446
+ elsif allowed_formats.nil?
447
+ add_parse_error(:unrecognized_reference_entity, match)
447
448
  next
448
- elsif !REFERENCE_FORMATS[match[:entity]].include?(format)
449
- formats = REFERENCE_FORMATS[match[:entity]].join("', '")
450
- add_parse_error(match, "Invalid reference format '#{format}' (must be one of '#{formats}')")
449
+ elsif !allowed_formats.include?(format)
450
+ add_parse_error(:unrecognized_reference_format, match)
451
451
  next
452
- elsif match[:entity] == 'content' && format != 'base64' && asset.binary?
453
- add_parse_error(match, 'Base64 encoding is required for binary assets')
452
+ elsif entity == 'content' && format != 'base64' && asset.binary?
453
+ add_parse_error(:format_not_base64, match)
454
454
  next
455
455
  end
456
456
  end
@@ -459,10 +459,10 @@ class Darkroom
459
459
  substitution, start, finish = handler&.call(**handler_args)
460
460
 
461
461
  min_start, max_finish = match.offset(0)
462
- start ||= (!format || format == 'displace') ? min_start : match.begin(:quoted)
463
- finish ||= (!format || format == 'displace') ? max_finish : match.end(:quoted)
464
- start = [[start, min_start].max, max_finish].min
465
- finish = [[finish, max_finish].min, min_start].max
462
+ start ||= !format || format == 'displace' ? min_start : match.begin(:quoted)
463
+ finish ||= !format || format == 'displace' ? max_finish : match.end(:quoted)
464
+ start = start.clamp(min_start, max_finish)
465
+ finish = finish.clamp(min_start, max_finish)
466
466
 
467
467
  if kind == :reference
468
468
  case "#{match[:entity]}-#{format}"
@@ -487,7 +487,7 @@ class Darkroom
487
487
  nil
488
488
  end
489
489
 
490
- add_parse_error(match, error) if error
490
+ add_parse_error(error, match) if error
491
491
  end
492
492
 
493
493
  substitutions.reverse_each do |content, start, finish|
@@ -495,9 +495,9 @@ class Darkroom
495
495
  end
496
496
  end
497
497
 
498
- ##
499
- # Compiles the asset if compilation is supported for the asset's type.
498
+ # Internal: Compile the asset if compilation is supported for the asset's type.
500
499
  #
500
+ # Returns nothing.
501
501
  def compile
502
502
  return if ran?(:compile)
503
503
 
@@ -511,16 +511,16 @@ class Darkroom
511
511
  )
512
512
 
513
513
  @own_content = compiled if compiled.kind_of?(String)
514
- rescue => e
514
+ rescue StandardError => e
515
515
  @errors << e
516
516
  ensure
517
517
  @own_content.freeze
518
518
  end
519
519
  end
520
520
 
521
- ##
522
- # Returns the processed content of the asset without dependencies concatenated.
521
+ # Internal: Get the processed content of the asset without dependencies concatenated.
523
522
  #
523
+ # Returns the String result.
524
524
  def own_content
525
525
  compile
526
526
 
@@ -529,37 +529,35 @@ class Darkroom
529
529
 
530
530
  private
531
531
 
532
- ##
533
- # Requires any libraries necessary for compiling and minifying the asset based on its type. Raises a
534
- # MissingLibraryError if library cannot be loaded.
532
+ # Internal: Require any libraries necessary for compiling, finalizing, and minifying the asset based on
533
+ # its type.
535
534
  #
536
- # Darkroom does not explicitly depend on any libraries necessary for asset compilation or minification,
537
- # since not every app will use every kind of asset or use minification. It is instead up to each app
538
- # using Darkroom to specify any needed compilation and minification libraries as direct dependencies
539
- # (e.g. specify +gem('terser')+ in the app's Gemfile if JavaScript minification is desired).
535
+ # Darkroom does not explicitly depend on any libraries necessary for asset compilation, finalization, or
536
+ # minification. This is because not every app will use every kind of asset or use minification. It is
537
+ # instead up to each app using Darkroom to specify any needed libraries as direct dependencies (e.g. add
538
+ # gem('terser') to the app's Gemfile if JavaScript minification is desired).
540
539
  #
540
+ # Returns nothing.
541
+ # Raises MissingLibraryError if a library cannot be loaded.
541
542
  def require_libs
542
- begin
543
- require(@delegate.compile_lib) if @delegate.compile_lib
544
- rescue LoadError
545
- compile_load_error = true
546
- end
543
+ Delegate::LIB_REQUIRES.each do |need|
544
+ next if need == :minify && !@minify
547
545
 
548
- begin
549
- require(@delegate.minify_lib) if @delegate.minify_lib && @minify
550
- rescue LoadError
551
- minify_load_error = true
546
+ begin
547
+ lib = @delegate.send(:"#{need}_lib")
548
+ require(lib) if lib
549
+ rescue LoadError
550
+ raise(MissingLibraryError.new(lib, need, @extension), cause: nil)
551
+ end
552
552
  end
553
-
554
- raise(MissingLibraryError.new(@delegate.compile_lib, 'compile', @extension)) if compile_load_error
555
- raise(MissingLibraryError.new(@delegate.minify_lib, 'minify', @extension)) if minify_load_error
556
553
  end
557
554
 
558
- ##
559
- # Returns boolean indicating if a method has already been run during the current round of processing.
555
+ # Internal: Check if a method has already been run during the current round of processing and mark it as
556
+ # having been run if not.
560
557
  #
561
- # [name] Name of the method.
558
+ # name - Symbol name of the method.
562
559
  #
560
+ # Returns the boolean result.
563
561
  def ran?(name)
564
562
  modified?
565
563
 
@@ -571,11 +569,11 @@ class Darkroom
571
569
  end
572
570
  end
573
571
 
574
- ##
575
- # Utility method used by #dependencies and #imports to recursively build arrays.
572
+ # Internal: Recursively build an array of Asset objects (used by #dependencies and #imports).
576
573
  #
577
- # [name] Name of the array to accumulate (:dependencies or :imports).
574
+ # name - Symbol name of the array to accumulate (:dependencies or :imports).
578
575
  #
576
+ # Returns an Array of Asset objects.
579
577
  def accumulate(name)
580
578
  done = Set.new
581
579
  assets = [self]
@@ -593,28 +591,42 @@ class Darkroom
593
591
  assets
594
592
  end
595
593
 
596
- ##
597
- # Utility method to create a parse error of the appropriate class and append it to the errors array.
598
- #
599
- # [match] MatchData object for where the parse error occurred.
600
- # [error] Error class or message.
601
- #
602
- def add_parse_error(match, error)
603
- klass = error
604
- args = []
605
-
606
- if error == AssetNotFoundError
607
- klass = UnrecognizedExtensionError unless Darkroom.delegate(File.extname(match[:path]))
608
- args << match[:path]
594
+ # Internal: Create a parse error of the appropriate class and append it to the errors array.
595
+ #
596
+ # error - Symbol error type or String error message.
597
+ # match - MatchData object for where the parse error occurred.
598
+ #
599
+ # Returns nothing.
600
+ # Raises RuntimeError if error is not a String or recognized Symbol error name.
601
+ def add_parse_error(error, match)
602
+ klass = AssetError
603
+ args = [match[0].strip]
604
+
605
+ case error
606
+ when :not_found
607
+ path = match[:path]
608
+ delegate = Darkroom.delegate(File.extname(path)) if path
609
+ klass = delegate ? AssetNotFoundError : UnrecognizedExtensionError
610
+ args.clear << path
611
+ when :circular_reference
612
+ klass = CircularReferenceError
613
+ when :unrecognized_reference_entity
614
+ entity = match[:entity].nil? ? 'nil' : "'#{match[:entity]}'"
615
+ allowed = "'#{Asset::REFERENCE_FORMATS.keys.join("', '")}'"
616
+ message = "Invalid reference entity #{entity} (must be one of #{allowed})"
617
+ when :unrecognized_reference_format
618
+ entity = match[:entity]
619
+ format = match[:format].nil? ? 'nil' : "'#{match[:format]}'"
620
+ allowed = "'#{Asset::REFERENCE_FORMATS[entity].join("', '")}'"
621
+ message = "Invalid reference format #{format} (must be one of #{allowed})"
622
+ when :format_not_base64
623
+ message = 'Base64 encoding is required for binary assets'
609
624
  else
610
- if error.kind_of?(String)
611
- klass = AssetError
612
- args << error
613
- end
614
-
615
- args << match[0].strip
625
+ message = error.to_s
616
626
  end
617
627
 
628
+ args.unshift(message) if message
629
+
618
630
  @errors << klass.new(*args, @path, @own_content[0..match.begin(:path)].count("\n") + 1)
619
631
  end
620
632
  end