mime-types 1.17.2 → 3.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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +35 -0
  3. data/.gitignore +17 -0
  4. data/.hoerc +20 -12
  5. data/Code-of-Conduct.rdoc +41 -0
  6. data/Contributing.rdoc +169 -0
  7. data/History.rdoc +531 -30
  8. data/Licence.rdoc +25 -0
  9. data/Manifest.txt +32 -34
  10. data/README.rdoc +198 -13
  11. data/Rakefile +181 -159
  12. data/lib/mime/type/columnar.rb +55 -0
  13. data/lib/mime/type.rb +566 -0
  14. data/lib/mime/types/cache.rb +56 -0
  15. data/lib/mime/types/columnar.rb +142 -0
  16. data/lib/mime/types/container.rb +30 -0
  17. data/lib/mime/types/deprecations.rb +32 -0
  18. data/lib/mime/types/full.rb +17 -0
  19. data/lib/mime/types/loader.rb +148 -0
  20. data/lib/mime/types/logger.rb +37 -0
  21. data/lib/mime/types/registry.rb +81 -0
  22. data/lib/mime/types.rb +199 -819
  23. data/lib/mime-types.rb +1 -0
  24. data/support/benchmarks/load.rb +65 -0
  25. data/support/benchmarks/load_allocations.rb +90 -0
  26. data/support/benchmarks/object_counts.rb +43 -0
  27. data/support/profile/columnar.rb +5 -0
  28. data/support/profile/columnar_full.rb +5 -0
  29. data/support/profile/full.rb +5 -0
  30. data/test/bad-fixtures/malformed +9 -0
  31. data/test/fixture/json.json +1 -0
  32. data/test/fixture/old-data +9 -0
  33. data/test/fixture/yaml.yaml +55 -0
  34. data/test/minitest_helper.rb +12 -0
  35. data/test/test_mime_type.rb +527 -242
  36. data/test/test_mime_types.rb +130 -68
  37. data/test/test_mime_types_cache.rb +100 -0
  38. data/test/test_mime_types_class.rb +155 -0
  39. data/test/test_mime_types_lazy.rb +43 -0
  40. data/test/test_mime_types_loader.rb +32 -0
  41. metadata +286 -229
  42. data/License.rdoc +0 -10
  43. data/lib/mime/types/application +0 -940
  44. data/lib/mime/types/application.mac +0 -2
  45. data/lib/mime/types/application.nonstandard +0 -114
  46. data/lib/mime/types/application.obsolete +0 -40
  47. data/lib/mime/types/audio +0 -131
  48. data/lib/mime/types/audio.nonstandard +0 -10
  49. data/lib/mime/types/audio.obsolete +0 -1
  50. data/lib/mime/types/image +0 -43
  51. data/lib/mime/types/image.nonstandard +0 -17
  52. data/lib/mime/types/image.obsolete +0 -5
  53. data/lib/mime/types/message +0 -19
  54. data/lib/mime/types/message.obsolete +0 -1
  55. data/lib/mime/types/model +0 -15
  56. data/lib/mime/types/multipart +0 -14
  57. data/lib/mime/types/multipart.nonstandard +0 -1
  58. data/lib/mime/types/multipart.obsolete +0 -7
  59. data/lib/mime/types/other.nonstandard +0 -8
  60. data/lib/mime/types/text +0 -54
  61. data/lib/mime/types/text.nonstandard +0 -5
  62. data/lib/mime/types/text.obsolete +0 -7
  63. data/lib/mime/types/text.vms +0 -1
  64. data/lib/mime/types/video +0 -68
  65. data/lib/mime/types/video.nonstandard +0 -11
  66. data/lib/mime/types/video.obsolete +0 -3
  67. data/mime-types.gemspec +0 -57
  68. data/type-lists/application.txt +0 -951
  69. data/type-lists/audio.txt +0 -132
  70. data/type-lists/image.txt +0 -43
  71. data/type-lists/message.txt +0 -20
  72. data/type-lists/model.txt +0 -15
  73. data/type-lists/multipart.txt +0 -14
  74. data/type-lists/text.txt +0 -57
  75. data/type-lists/video.txt +0 -67
@@ -0,0 +1,55 @@
1
+ require 'mime/type'
2
+
3
+ # A version of MIME::Type that works hand-in-hand with a MIME::Types::Columnar
4
+ # container to load data by columns.
5
+ #
6
+ # When a field is has not yet been loaded, that data will be loaded for all
7
+ # types in the container before forwarding the message to MIME::Type.
8
+ #
9
+ # More information can be found in MIME::Types::Columnar.
10
+ #
11
+ # MIME::Type::Columnar is *not* intended to be created except by
12
+ # MIME::Types::Columnar containers.
13
+ class MIME::Type::Columnar < MIME::Type
14
+ def initialize(container, content_type, extensions) # :nodoc:
15
+ @container = container
16
+ self.content_type = content_type
17
+ self.extensions = extensions
18
+ end
19
+
20
+ def self.column(*methods, file: nil) # :nodoc:
21
+ file = methods.first unless file
22
+
23
+ file_method = :"load_#{file}"
24
+ methods.each do |m|
25
+ define_method m do |*args|
26
+ @container.send(file_method)
27
+ super(*args)
28
+ end
29
+ end
30
+ end
31
+
32
+ column :friendly
33
+ column :encoding, :encoding=
34
+ column :docs, :docs=
35
+ column :preferred_extension, :preferred_extension=
36
+ column :obsolete, :obsolete=, :obsolete?, :registered, :registered=,
37
+ :registered?, :signature, :signature=, :signature?, file: 'flags'
38
+ column :xrefs, :xrefs=, :xref_urls
39
+ column :use_instead, :use_instead=
40
+
41
+ def encode_with(coder) # :nodoc:
42
+ @container.send(:load_friendly)
43
+ @container.send(:load_encoding)
44
+ @container.send(:load_docs)
45
+ @container.send(:load_flags)
46
+ @container.send(:load_use_instead)
47
+ @container.send(:load_xrefs)
48
+ @container.send(:load_preferred_extension)
49
+ super
50
+ end
51
+
52
+ class << self
53
+ undef column
54
+ end
55
+ end
data/lib/mime/type.rb ADDED
@@ -0,0 +1,566 @@
1
+ ##
2
+ module MIME
3
+ end
4
+
5
+ # The definition of one MIME content-type.
6
+ #
7
+ # == Usage
8
+ # require 'mime/types'
9
+ #
10
+ # plaintext = MIME::Types['text/plain'].first
11
+ # # returns [text/plain, text/plain]
12
+ # text = plaintext.first
13
+ # print text.media_type # => 'text'
14
+ # print text.sub_type # => 'plain'
15
+ #
16
+ # puts text.extensions.join(" ") # => 'asc txt c cc h hh cpp'
17
+ #
18
+ # puts text.encoding # => 8bit
19
+ # puts text.binary? # => false
20
+ # puts text.ascii? # => true
21
+ # puts text == 'text/plain' # => true
22
+ # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
23
+ #
24
+ # puts MIME::Types.any? { |type|
25
+ # type.content_type == 'text/plain'
26
+ # } # => true
27
+ # puts MIME::Types.all?(&:registered?)
28
+ # # => false
29
+ class MIME::Type
30
+ # Reflects a MIME content-type specification that is not correctly
31
+ # formatted (it isn't +type+/+subtype+).
32
+ class InvalidContentType < ArgumentError
33
+ # :stopdoc:
34
+ def initialize(type_string)
35
+ @type_string = type_string
36
+ end
37
+
38
+ def to_s
39
+ "Invalid Content-Type #{@type_string.inspect}"
40
+ end
41
+ # :startdoc:
42
+ end
43
+
44
+ # Reflects an unsupported MIME encoding.
45
+ class InvalidEncoding < ArgumentError
46
+ # :stopdoc:
47
+ def initialize(encoding)
48
+ @encoding = encoding
49
+ end
50
+
51
+ def to_s
52
+ "Invalid Encoding #{@encoding.inspect}"
53
+ end
54
+ # :startdoc:
55
+ end
56
+
57
+ # The released version of the mime-types library.
58
+ VERSION = '3.0'
59
+
60
+ include Comparable
61
+
62
+ # :stopdoc:
63
+ # TODO verify mime-type character restrictions; I am pretty sure that this is
64
+ # too wide open.
65
+ MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}
66
+ I18N_RE = %r{[^[:alnum:]]}
67
+ BINARY_ENCODINGS = %w(base64 8bit)
68
+ ASCII_ENCODINGS = %w(7bit quoted-printable)
69
+ # :startdoc:
70
+
71
+ private_constant :MEDIA_TYPE_RE, :I18N_RE, :BINARY_ENCODINGS,
72
+ :ASCII_ENCODINGS
73
+
74
+ # Builds a MIME::Type object from the +content_type+, a MIME Content Type
75
+ # value (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object
76
+ # is yielded to an optional block for additional configuration, such as
77
+ # associating extensions and encoding information.
78
+ #
79
+ # * When provided a Hash or a MIME::Type, the MIME::Type will be
80
+ # constructed with #init_with.
81
+ # * When provided an Array, the MIME::Type will be constructed using
82
+ # the first element as the content type and the remaining flattened
83
+ # elements as extensions.
84
+ # * Otherwise, the content_type will be used as a string.
85
+ #
86
+ # Yields the newly constructed +self+ object.
87
+ def initialize(content_type) # :yields self:
88
+ @friendly = {}
89
+ @obsolete = @registered = false
90
+ @preferred_extension = @docs = @use_instead = nil
91
+ self.extensions = []
92
+
93
+ case content_type
94
+ when Hash
95
+ init_with(content_type)
96
+ when Array
97
+ self.content_type = content_type.shift
98
+ self.extensions = content_type.flatten
99
+ when MIME::Type
100
+ init_with(content_type.to_h)
101
+ else
102
+ self.content_type = content_type
103
+ end
104
+
105
+ self.encoding ||= :default
106
+ self.xrefs ||= {}
107
+
108
+ yield self if block_given?
109
+ end
110
+
111
+ # Indicates that a MIME type is like another type. This differs from
112
+ # <tt>==</tt> because <tt>x-</tt> prefixes are removed for this comparison.
113
+ def like?(other)
114
+ other = if other.respond_to?(:simplified)
115
+ MIME::Type.simplified(other.simplified, remove_x_prefix: true)
116
+ else
117
+ MIME::Type.simplified(other.to_s, remove_x_prefix: true)
118
+ end
119
+ MIME::Type.simplified(simplified, remove_x_prefix: true) == other
120
+ end
121
+
122
+ # Compares the +other+ MIME::Type against the exact content type or the
123
+ # simplified type (the simplified type will be used if comparing against
124
+ # something that can be treated as a String with #to_s). In comparisons, this
125
+ # is done against the lowercase version of the MIME::Type.
126
+ def <=>(other)
127
+ if other.nil?
128
+ -1
129
+ elsif other.respond_to?(:simplified)
130
+ simplified <=> other.simplified
131
+ else
132
+ simplified <=> MIME::Type.simplified(other.to_s)
133
+ end
134
+ end
135
+
136
+ # Compares the +other+ MIME::Type based on how reliable it is before doing a
137
+ # normal <=> comparison. Used by MIME::Types#[] to sort types. The
138
+ # comparisons involved are:
139
+ #
140
+ # 1. self.simplified <=> other.simplified (ensures that we
141
+ # don't try to compare different types)
142
+ # 2. IANA-registered definitions < other definitions.
143
+ # 3. Complete definitions < incomplete definitions.
144
+ # 4. Current definitions < obsolete definitions.
145
+ # 5. Obselete with use-instead names < obsolete without.
146
+ # 6. Obsolete use-instead definitions are compared.
147
+ #
148
+ # While this method is public, its use is strongly discouraged by consumers
149
+ # of mime-types. In mime-types 3, this method is likely to see substantial
150
+ # revision and simplification to ensure current registered content types sort
151
+ # before unregistered or obsolete content types.
152
+ def priority_compare(other)
153
+ pc = simplified <=> other.simplified
154
+ if pc.zero?
155
+ pc = if (reg = registered?) != other.registered?
156
+ reg ? -1 : 1 # registered < unregistered
157
+ elsif (comp = complete?) != other.complete?
158
+ comp ? -1 : 1 # complete < incomplete
159
+ elsif (obs = obsolete?) != other.obsolete?
160
+ obs ? 1 : -1 # current < obsolete
161
+ elsif obs and ((ui = use_instead) != (oui = other.use_instead))
162
+ if ui.nil?
163
+ 1
164
+ elsif oui.nil?
165
+ -1
166
+ else
167
+ ui <=> oui
168
+ end
169
+ else
170
+ 0
171
+ end
172
+ end
173
+
174
+ pc
175
+ end
176
+
177
+ # Returns +true+ if the +other+ object is a MIME::Type and the content types
178
+ # match.
179
+ def eql?(other)
180
+ other.kind_of?(MIME::Type) and self == other
181
+ end
182
+
183
+ # Returns the whole MIME content-type string.
184
+ #
185
+ # The content type is a presentation value from the MIME type registry and
186
+ # should not be used for comparison. The case of the content type is
187
+ # preserved, and extension markers (<tt>x-</tt>) are kept.
188
+ #
189
+ # text/plain => text/plain
190
+ # x-chemical/x-pdb => x-chemical/x-pdb
191
+ # audio/QCELP => audio/QCELP
192
+ attr_reader :content_type
193
+ # A simplified form of the MIME content-type string, suitable for
194
+ # case-insensitive comparison, with any extension markers (<tt>x-</tt)
195
+ # removed and converted to lowercase.
196
+ #
197
+ # text/plain => text/plain
198
+ # x-chemical/x-pdb => x-chemical/x-pdb
199
+ # audio/QCELP => audio/qcelp
200
+ attr_reader :simplified
201
+ # Returns the media type of the simplified MIME::Type.
202
+ #
203
+ # text/plain => text
204
+ # x-chemical/x-pdb => x-chemical
205
+ # audio/QCELP => audio
206
+ attr_reader :media_type
207
+ # Returns the media type of the unmodified MIME::Type.
208
+ #
209
+ # text/plain => text
210
+ # x-chemical/x-pdb => x-chemical
211
+ # audio/QCELP => audio
212
+ attr_reader :raw_media_type
213
+ # Returns the sub-type of the simplified MIME::Type.
214
+ #
215
+ # text/plain => plain
216
+ # x-chemical/x-pdb => pdb
217
+ # audio/QCELP => QCELP
218
+ attr_reader :sub_type
219
+ # Returns the media type of the unmodified MIME::Type.
220
+ #
221
+ # text/plain => plain
222
+ # x-chemical/x-pdb => x-pdb
223
+ # audio/QCELP => qcelp
224
+ attr_reader :raw_sub_type
225
+
226
+ ##
227
+ # The list of extensions which are known to be used for this MIME::Type.
228
+ # Non-array values will be coerced into an array with #to_a. Array values
229
+ # will be flattened, +nil+ values removed, and made unique.
230
+ #
231
+ # :attr_accessor: extensions
232
+ def extensions
233
+ @extensions.to_a
234
+ end
235
+
236
+ ##
237
+ def extensions=(value) # :nodoc:
238
+ @extensions = Set[*Array(value).flatten.compact].freeze
239
+ MIME::Types.send(:reindex_extensions, self)
240
+ end
241
+
242
+ # Merge the +extensions+ provided into this MIME::Type. The extensions added
243
+ # will be merged uniquely.
244
+ def add_extensions(*extensions)
245
+ self.extensions += extensions
246
+ end
247
+
248
+ ##
249
+ # The preferred extension for this MIME type. If one is not set and there are
250
+ # exceptions defined, the first extension will be used.
251
+ #
252
+ # When setting #preferred_extensions, if #extensions does not contain this
253
+ # extension, this will be added to #xtensions.
254
+ #
255
+ # :attr_accessor: preferred_extension
256
+
257
+ ##
258
+ def preferred_extension
259
+ @preferred_extension || extensions.first
260
+ end
261
+
262
+ ##
263
+ def preferred_extension=(value) # :nodoc:
264
+ add_extensions(value) if value
265
+ @preferred_extension = value
266
+ end
267
+
268
+ ##
269
+ # The encoding (+7bit+, +8bit+, <tt>quoted-printable</tt>, or +base64+)
270
+ # required to transport the data of this content type safely across a
271
+ # network, which roughly corresponds to Content-Transfer-Encoding. A value of
272
+ # +nil+ or <tt>:default</tt> will reset the #encoding to the
273
+ # #default_encoding for the MIME::Type. Raises ArgumentError if the encoding
274
+ # provided is invalid.
275
+ #
276
+ # If the encoding is not provided on construction, this will be either
277
+ # 'quoted-printable' (for text/* media types) and 'base64' for eveything
278
+ # else.
279
+ #
280
+ # :attr_accessor: encoding
281
+
282
+ ##
283
+ attr_reader :encoding
284
+
285
+ ##
286
+ def encoding=(enc) # :nodoc:
287
+ if enc.nil? or enc == :default
288
+ @encoding = default_encoding
289
+ elsif BINARY_ENCODINGS.include?(enc) or ASCII_ENCODINGS.include?(enc)
290
+ @encoding = enc
291
+ else
292
+ fail InvalidEncoding, enc
293
+ end
294
+ end
295
+
296
+ # Returns the default encoding for the MIME::Type based on the media type.
297
+ def default_encoding
298
+ (@media_type == 'text') ? 'quoted-printable' : 'base64'
299
+ end
300
+
301
+ ##
302
+ # Returns the media type or types that should be used instead of this media
303
+ # type, if it is obsolete. If there is no replacement media type, or it is
304
+ # not obsolete, +nil+ will be returned.
305
+ #
306
+ # :attr_accessor: use_instead
307
+
308
+ ##
309
+ def use_instead
310
+ obsolete? ? @use_instead : nil
311
+ end
312
+
313
+ ##
314
+ attr_writer :use_instead
315
+
316
+ # Returns +true+ if the media type is obsolete.
317
+ attr_accessor :obsolete
318
+ alias_method :obsolete?, :obsolete
319
+
320
+ # The documentation for this MIME::Type.
321
+ attr_accessor :docs
322
+
323
+ # A friendly short description for this MIME::Type.
324
+ #
325
+ # call-seq:
326
+ # text_plain.friendly # => "Text File"
327
+ # text_plain.friendly('en') # => "Text File"
328
+ def friendly(lang = 'en'.freeze)
329
+ @friendly ||= {}
330
+
331
+ case lang
332
+ when String, Symbol
333
+ @friendly[lang.to_s]
334
+ when Array
335
+ @friendly.update(Hash[*lang])
336
+ when Hash
337
+ @friendly.update(lang)
338
+ else
339
+ fail ArgumentError,
340
+ "Expected a language or translation set, not #{lang.inspect}"
341
+ end
342
+ end
343
+
344
+ # A key suitable for use as a lookup key for translations, such as with
345
+ # the I18n library.
346
+ #
347
+ # call-seq:
348
+ # text_plain.i18n_key # => "text.plain"
349
+ # 3gpp_xml.i18n_key # => "application.vnd-3gpp-bsf-xml"
350
+ # # from application/vnd.3gpp.bsf+xml
351
+ # x_msword.i18n_key # => "application.word"
352
+ # # from application/x-msword
353
+ attr_reader :i18n_key
354
+
355
+ ##
356
+ # The cross-references list for this MIME::Type.
357
+ #
358
+ # :attr_accessor: xrefs
359
+
360
+ ##
361
+ attr_reader :xrefs
362
+
363
+ ##
364
+ def xrefs=(x) # :nodoc:
365
+ MIME::Types::Container.new.merge(x).tap do |xr|
366
+ xr.each do |k, v|
367
+ xr[k] = Set[*v] unless v.kind_of? Set
368
+ end
369
+
370
+ @xrefs = xr
371
+ end
372
+ end
373
+
374
+ # The decoded cross-reference URL list for this MIME::Type.
375
+ def xref_urls
376
+ xrefs.flat_map { |type, values|
377
+ name = :"xref_url_for_#{type.tr('-', '_')}"
378
+ respond_to?(name, true) and xref_map(values, name) or values.to_a
379
+ }
380
+ end
381
+
382
+ # Indicates whether the MIME type has been registered with IANA.
383
+ attr_accessor :registered
384
+ alias_method :registered?, :registered
385
+
386
+ # MIME types can be specified to be sent across a network in particular
387
+ # formats. This method returns +true+ when the MIME::Type encoding is set
388
+ # to <tt>base64</tt>.
389
+ def binary?
390
+ BINARY_ENCODINGS.include?(encoding)
391
+ end
392
+
393
+ # MIME types can be specified to be sent across a network in particular
394
+ # formats. This method returns +false+ when the MIME::Type encoding is
395
+ # set to <tt>base64</tt>.
396
+ def ascii?
397
+ ASCII_ENCODINGS.include?(encoding)
398
+ end
399
+
400
+ # Indicateswhether the MIME type is declared as a signature type.
401
+ attr_accessor :signature
402
+ alias_method :signature?, :signature
403
+
404
+ # Returns +true+ if the MIME::Type specifies an extension list,
405
+ # indicating that it is a complete MIME::Type.
406
+ def complete?
407
+ !@extensions.empty?
408
+ end
409
+
410
+ # Returns the MIME::Type as a string.
411
+ def to_s
412
+ content_type
413
+ end
414
+
415
+ # Returns the MIME::Type as a string for implicit conversions. This allows
416
+ # MIME::Type objects to appear on either side of a comparison.
417
+ #
418
+ # 'text/plain' == MIME::Type.new('text/plain')
419
+ def to_str
420
+ content_type
421
+ end
422
+
423
+ # Converts the MIME::Type to a JSON string.
424
+ def to_json(*args)
425
+ require 'json'
426
+ to_h.to_json(*args)
427
+ end
428
+
429
+ # Converts the MIME::Type to a hash. The output of this method can also be
430
+ # used to initialize a MIME::Type.
431
+ def to_h
432
+ encode_with({})
433
+ end
434
+
435
+ # Populates the +coder+ with attributes about this record for
436
+ # serialization. The structure of +coder+ should match the structure used
437
+ # with #init_with.
438
+ #
439
+ # This method should be considered a private implementation detail.
440
+ def encode_with(coder)
441
+ coder['content-type'] = @content_type
442
+ coder['docs'] = @docs unless @docs.nil? or @docs.empty?
443
+ unless @friendly.nil? or @friendly.empty?
444
+ coder['friendly'] = @friendly
445
+ end
446
+ coder['encoding'] = @encoding
447
+ coder['extensions'] = @extensions.to_a unless @extensions.empty?
448
+ coder['preferred-extension'] = @preferred_extension if @preferred_extension
449
+ if obsolete?
450
+ coder['obsolete'] = obsolete?
451
+ coder['use-instead'] = use_instead if use_instead
452
+ end
453
+ coder['xrefs'] = xrefs unless xrefs.empty?
454
+ coder['registered'] = registered?
455
+ coder['signature'] = signature? if signature?
456
+ coder
457
+ end
458
+
459
+ # Initialize an empty object from +coder+, which must contain the
460
+ # attributes necessary for initializing an empty object.
461
+ #
462
+ # This method should be considered a private implementation detail.
463
+ def init_with(coder)
464
+ self.content_type = coder['content-type']
465
+ self.docs = coder['docs'] || ''
466
+ self.encoding = coder['encoding']
467
+ self.extensions = coder['extensions'] || []
468
+ self.preferred_extension = coder['preferred-extension']
469
+ self.obsolete = coder['obsolete'] || false
470
+ self.registered = coder['registered'] || false
471
+ self.signature = coder['signature']
472
+ self.xrefs = coder['xrefs'] || {}
473
+ self.use_instead = coder['use-instead']
474
+
475
+ friendly(coder['friendly'] || {})
476
+ end
477
+
478
+ def inspect # :nodoc:
479
+ # We are intentionally lying here because MIME::Type::Columnar is an
480
+ # implementation detail.
481
+ "#<MIME::Type: #{self}>"
482
+ end
483
+
484
+ class << self
485
+ # MIME media types are case-insensitive, but are typically presented in a
486
+ # case-preserving format in the type registry. This method converts
487
+ # +content_type+ to lowercase.
488
+ #
489
+ # In previous versions of mime-types, this would also remove any extension
490
+ # prefix (<tt>x-</tt>). This is no longer default behaviour, but may be
491
+ # provided by providing a truth value to +remove_x_prefix+.
492
+ def simplified(content_type, remove_x_prefix: false)
493
+ simplify_matchdata(match(content_type), remove_x_prefix)
494
+ end
495
+
496
+ # Converts a provided +content_type+ into a translation key suitable for
497
+ # use with the I18n library.
498
+ def i18n_key(content_type)
499
+ simplify_matchdata(match(content_type), joiner: '.') { |e|
500
+ e.gsub!(I18N_RE, '-'.freeze)
501
+ }
502
+ end
503
+
504
+ # Return a +MatchData+ object of the +content_type+ against pattern of
505
+ # media types.
506
+ def match(content_type)
507
+ case content_type
508
+ when MatchData
509
+ content_type
510
+ else
511
+ MEDIA_TYPE_RE.match(content_type)
512
+ end
513
+ end
514
+
515
+ private
516
+
517
+ def simplify_matchdata(matchdata, remove_x = false, joiner: '/'.freeze)
518
+ return nil unless matchdata
519
+
520
+ matchdata.captures.map { |e|
521
+ e.downcase!
522
+ e.sub!(%r{^x-}, ''.freeze) if remove_x
523
+ yield e if block_given?
524
+ e
525
+ }.join(joiner)
526
+ end
527
+ end
528
+
529
+ private
530
+
531
+ def content_type=(type_string)
532
+ match = MEDIA_TYPE_RE.match(type_string)
533
+ fail InvalidContentType, type_string if match.nil?
534
+
535
+ @content_type = type_string
536
+ @raw_media_type, @raw_sub_type = match.captures
537
+ @simplified = MIME::Type.simplified(match)
538
+ @i18n_key = MIME::Type.i18n_key(match)
539
+ @media_type, @sub_type = MEDIA_TYPE_RE.match(@simplified).captures
540
+ end
541
+
542
+ def xref_map(values, helper)
543
+ values.map { |value| send(helper, value) }
544
+ end
545
+
546
+ def xref_url_for_rfc(value)
547
+ 'http://www.iana.org/go/%s'.freeze % value
548
+ end
549
+
550
+ def xref_url_for_draft(value)
551
+ 'http://www.iana.org/go/%s'.freeze % value.sub(/\ARFC/, 'draft')
552
+ end
553
+
554
+ def xref_url_for_rfc_errata(value)
555
+ 'http://www.rfc-editor.org/errata_search.php?eid=%s'.freeze % value
556
+ end
557
+
558
+ def xref_url_for_person(value)
559
+ 'http://www.iana.org/assignments/media-types/media-types.xhtml#%s'.freeze %
560
+ value
561
+ end
562
+
563
+ def xref_url_for_template(value)
564
+ 'http://www.iana.org/assignments/media-types/%s'.freeze % value
565
+ end
566
+ end
@@ -0,0 +1,56 @@
1
+ MIME::Types::Cache = Struct.new(:version, :data) # :nodoc:
2
+
3
+ # Caching of MIME::Types registries is advisable if you will be loading
4
+ # the default registry relatively frequently. With the class methods on
5
+ # MIME::Types::Cache, any MIME::Types registry can be marshaled quickly
6
+ # and easily.
7
+ #
8
+ # The cache is invalidated on a per-data-version basis; a cache file for
9
+ # version 3.2015.1118 will not be reused with version 3.2015.1201.
10
+ class << MIME::Types::Cache
11
+ # Attempts to load the cache from the file provided as a parameter or in
12
+ # the environment variable +RUBY_MIME_TYPES_CACHE+. Returns +nil+ if the
13
+ # file does not exist, if the file cannot be loaded, or if the data in
14
+ # the cache version is different than this version.
15
+ def load(cache_file = nil)
16
+ cache_file ||= ENV['RUBY_MIME_TYPES_CACHE']
17
+ return nil unless cache_file and File.exist?(cache_file)
18
+
19
+ cache = Marshal.load(File.binread(cache_file))
20
+ if cache.version == MIME::Types::Data::VERSION
21
+ Marshal.load(cache.data)
22
+ else
23
+ MIME::Types.logger.warn <<-warning.chomp
24
+ Could not load MIME::Types cache: invalid version
25
+ warning
26
+ nil
27
+ end
28
+ rescue => e
29
+ MIME::Types.logger.warn <<-warning.chomp
30
+ Could not load MIME::Types cache: #{e}
31
+ warning
32
+ return nil
33
+ end
34
+
35
+ # Attempts to save the types provided to the cache file provided.
36
+ #
37
+ # If +types+ is not provided or is +nil+, the cache will contain the
38
+ # current MIME::Types default registry.
39
+ #
40
+ # If +cache_file+ is not provided or is +nil+, the cache will be written
41
+ # to the file specified in the environment variable
42
+ # +RUBY_MIME_TYPES_CACHE+. If there is no cache file specified either
43
+ # directly or through the environment, this method will return +nil+
44
+ def save(types = nil, cache_file = nil)
45
+ cache_file ||= ENV['RUBY_MIME_TYPES_CACHE']
46
+ return nil unless cache_file
47
+
48
+ types ||= MIME::Types.send(:__types__)
49
+
50
+ File.open(cache_file, 'wb') do |f|
51
+ f.write(
52
+ Marshal.dump(new(MIME::Types::Data::VERSION, Marshal.dump(types)))
53
+ )
54
+ end
55
+ end
56
+ end