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
data/lib/mime/types.rb CHANGED
@@ -1,848 +1,228 @@
1
- # -*- ruby encoding: utf-8 -*-
2
-
3
- # The namespace for MIME applications, tools, and libraries.
1
+ ##
4
2
  module MIME
5
- # Reflects a MIME Content-Type which is in invalid format (e.g., it isn't
6
- # in the form of type/subtype).
7
- class InvalidContentType < RuntimeError; end
8
-
9
- # The definition of one MIME content-type.
10
- #
11
- # == Usage
12
- # require 'mime/types'
13
- #
14
- # plaintext = MIME::Types['text/plain']
15
- # print plaintext.media_type # => 'text'
16
- # print plaintext.sub_type # => 'plain'
17
- #
18
- # puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
19
- #
20
- # puts plaintext.encoding # => 8bit
21
- # puts plaintext.binary? # => false
22
- # puts plaintext.ascii? # => true
23
- # puts plaintext == 'text/plain' # => true
24
- # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
25
- #
26
- class Type
27
- VERSION = '1.17.2'
28
-
29
- include Comparable
30
-
31
- MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}o
32
- UNREG_RE = %r{[Xx]-}o
33
- ENCODING_RE = %r{(?:base64|7bit|8bit|quoted\-printable)}o
34
- PLATFORM_RE = %r|#{RUBY_PLATFORM}|o
35
-
36
- SIGNATURES = %w(application/pgp-keys application/pgp
37
- application/pgp-signature application/pkcs10
38
- application/pkcs7-mime application/pkcs7-signature
39
- text/vcard)
40
-
41
- IANA_URL = "http://www.iana.org/assignments/media-types/%s/%s"
42
- RFC_URL = "http://rfc-editor.org/rfc/rfc%s.txt"
43
- DRAFT_URL = "http://datatracker.ietf.org/public/idindex.cgi?command=id_details&filename=%s"
44
- LTSW_URL = "http://www.ltsw.se/knbase/internet/%s.htp"
45
- CONTACT_URL = "http://www.iana.org/assignments/contact-people.htm#%s"
46
-
47
- # Returns +true+ if the simplified type matches the current
48
- def like?(other)
49
- if other.respond_to?(:simplified)
50
- @simplified == other.simplified
51
- else
52
- @simplified == Type.simplified(other)
53
- end
54
- end
55
-
56
- # Compares the MIME::Type against the exact content type or the
57
- # simplified type (the simplified type will be used if comparing against
58
- # something that can be treated as a String with #to_s). In comparisons,
59
- # this is done against the lowercase version of the MIME::Type.
60
- def <=>(other)
61
- if other.respond_to?(:content_type)
62
- @content_type.downcase <=> other.content_type.downcase
63
- elsif other.respond_to?(:to_s)
64
- @simplified <=> Type.simplified(other.to_s)
65
- else
66
- @content_type.downcase <=> other.downcase
67
- end
68
- end
69
-
70
- # Compares the MIME::Type based on how reliable it is before doing a
71
- # normal <=> comparison. Used by MIME::Types#[] to sort types. The
72
- # comparisons involved are:
73
- #
74
- # 1. self.simplified <=> other.simplified (ensures that we
75
- # don't try to compare different types)
76
- # 2. IANA-registered definitions > other definitions.
77
- # 3. Generic definitions > platform definitions.
78
- # 3. Complete definitions > incomplete definitions.
79
- # 4. Current definitions > obsolete definitions.
80
- # 5. Obselete with use-instead references > obsolete without.
81
- # 6. Obsolete use-instead definitions are compared.
82
- def priority_compare(other)
83
- pc = simplified <=> other.simplified
84
-
85
- if pc.zero? and registered? != other.registered?
86
- pc = registered? ? -1 : 1
87
- end
88
-
89
- if pc.zero? and platform? != other.platform?
90
- pc = platform? ? 1 : -1
91
- end
92
-
93
- if pc.zero? and complete? != other.complete?
94
- pc = complete? ? -1 : 1
95
- end
96
-
97
- if pc.zero? and obsolete? != other.obsolete?
98
- pc = obsolete? ? 1 : -1
99
- end
100
-
101
- if pc.zero? and obsolete? and (use_instead != other.use_instead)
102
- pc = if use_instead.nil?
103
- -1
104
- elsif other.use_instead.nil?
105
- 1
106
- else
107
- use_instead <=> other.use_instead
108
- end
109
- end
110
-
111
- pc
112
- end
113
-
114
- # Returns +true+ if the other object is a MIME::Type and the content
115
- # types match.
116
- def eql?(other)
117
- other.kind_of?(MIME::Type) and self == other
118
- end
119
-
120
- # Returns the whole MIME content-type string.
121
- #
122
- # text/plain => text/plain
123
- # x-chemical/x-pdb => x-chemical/x-pdb
124
- attr_reader :content_type
125
- # Returns the media type of the simplified MIME type.
126
- #
127
- # text/plain => text
128
- # x-chemical/x-pdb => chemical
129
- attr_reader :media_type
130
- # Returns the media type of the unmodified MIME type.
131
- #
132
- # text/plain => text
133
- # x-chemical/x-pdb => x-chemical
134
- attr_reader :raw_media_type
135
- # Returns the sub-type of the simplified MIME type.
136
- #
137
- # text/plain => plain
138
- # x-chemical/x-pdb => pdb
139
- attr_reader :sub_type
140
- # Returns the media type of the unmodified MIME type.
141
- #
142
- # text/plain => plain
143
- # x-chemical/x-pdb => x-pdb
144
- attr_reader :raw_sub_type
145
- # The MIME types main- and sub-label can both start with <tt>x-</tt>,
146
- # which indicates that it is a non-registered name. Of course, after
147
- # registration this flag can disappear, adds to the confusing
148
- # proliferation of MIME types. The simplified string has the <tt>x-</tt>
149
- # removed and are translated to lowercase.
150
- #
151
- # text/plain => text/plain
152
- # x-chemical/x-pdb => chemical/pdb
153
- attr_reader :simplified
154
- # The list of extensions which are known to be used for this MIME::Type.
155
- # Non-array values will be coerced into an array with #to_a. Array
156
- # values will be flattened and +nil+ values removed.
157
- attr_accessor :extensions
158
- remove_method :extensions= ;
159
- def extensions=(ext) #:nodoc:
160
- @extensions = [ext].flatten.compact
161
- end
162
-
163
- # The encoding (7bit, 8bit, quoted-printable, or base64) required to
164
- # transport the data of this content type safely across a network, which
165
- # roughly corresponds to Content-Transfer-Encoding. A value of +nil+ or
166
- # <tt>:default</tt> will reset the #encoding to the #default_encoding
167
- # for the MIME::Type. Raises ArgumentError if the encoding provided is
168
- # invalid.
169
- #
170
- # If the encoding is not provided on construction, this will be either
171
- # 'quoted-printable' (for text/* media types) and 'base64' for eveything
172
- # else.
173
- attr_accessor :encoding
174
- remove_method :encoding= ;
175
- def encoding=(enc) #:nodoc:
176
- if enc.nil? or enc == :default
177
- @encoding = self.default_encoding
178
- elsif enc =~ ENCODING_RE
179
- @encoding = enc
180
- else
181
- raise ArgumentError, "The encoding must be nil, :default, base64, 7bit, 8bit, or quoted-printable."
182
- end
183
- end
184
-
185
- # The regexp for the operating system that this MIME::Type is specific
186
- # to.
187
- attr_accessor :system
188
- remove_method :system= ;
189
- def system=(os) #:nodoc:
190
- if os.nil? or os.kind_of?(Regexp)
191
- @system = os
192
- else
193
- @system = %r|#{os}|
194
- end
195
- end
196
- # Returns the default encoding for the MIME::Type based on the media
197
- # type.
198
- attr_reader :default_encoding
199
- remove_method :default_encoding
200
- def default_encoding
201
- (@media_type == 'text') ? 'quoted-printable' : 'base64'
202
- end
203
-
204
- # Returns the media type or types that should be used instead of this
205
- # media type, if it is obsolete. If there is no replacement media type,
206
- # or it is not obsolete, +nil+ will be returned.
207
- attr_reader :use_instead
208
- remove_method :use_instead
209
- def use_instead
210
- return nil unless @obsolete
211
- @use_instead
212
- end
213
-
214
- # Returns +true+ if the media type is obsolete.
215
- def obsolete?
216
- @obsolete ? true : false
217
- end
218
- # Sets the obsolescence indicator for this media type.
219
- attr_writer :obsolete
220
-
221
- # The documentation for this MIME::Type. Documentation about media
222
- # types will be found on a media type definition as a comment.
223
- # Documentation will be found through #docs.
224
- attr_accessor :docs
225
- remove_method :docs= ;
226
- def docs=(d)
227
- if d
228
- a = d.scan(%r{use-instead:#{MEDIA_TYPE_RE}})
229
-
230
- if a.empty?
231
- @use_instead = nil
232
- else
233
- @use_instead = a.map { |el| "#{el[0]}/#{el[1]}" }
234
- end
235
- end
236
- @docs = d
237
- end
238
-
239
- # The encoded URL list for this MIME::Type. See #urls for more
240
- # information.
241
- attr_accessor :url
242
- # The decoded URL list for this MIME::Type.
243
- # The special URL value IANA will be translated into:
244
- # http://www.iana.org/assignments/media-types/<mediatype>/<subtype>
245
- #
246
- # The special URL value RFC### will be translated into:
247
- # http://www.rfc-editor.org/rfc/rfc###.txt
248
- #
249
- # The special URL value DRAFT:name will be translated into:
250
- # https://datatracker.ietf.org/public/idindex.cgi?
251
- # command=id_detail&filename=<name>
252
- #
253
- # The special URL value LTSW will be translated into:
254
- # http://www.ltsw.se/knbase/internet/<mediatype>.htp
255
- #
256
- # The special URL value [token] will be translated into:
257
- # http://www.iana.org/assignments/contact-people.htm#<token>
258
- #
259
- # These values will be accessible through #urls, which always returns an
260
- # array.
261
- def urls
262
- @url.map do |el|
263
- case el
264
- when %r{^IANA$}
265
- IANA_URL % [ @media_type, @sub_type ]
266
- when %r{^RFC(\d+)$}
267
- RFC_URL % $1
268
- when %r{^DRAFT:(.+)$}
269
- DRAFT_URL % $1
270
- when %r{^LTSW$}
271
- LTSW_URL % @media_type
272
- when %r<^\{([^=]+)=([^\]]+)\}>
273
- [$1, $2]
274
- when %r{^\[([^=]+)=([^\]]+)\]}
275
- [$1, CONTACT_URL % $2]
276
- when %r{^\[([^\]]+)\]}
277
- CONTACT_URL % $1
278
- else
279
- el
280
- end
281
- end
282
- end
283
-
284
- class << self
285
- # The MIME types main- and sub-label can both start with <tt>x-</tt>,
286
- # which indicates that it is a non-registered name. Of course, after
287
- # registration this flag can disappear, adds to the confusing
288
- # proliferation of MIME types. The simplified string has the
289
- # <tt>x-</tt> removed and are translated to lowercase.
290
- def simplified(content_type)
291
- matchdata = MEDIA_TYPE_RE.match(content_type)
292
-
293
- if matchdata.nil?
294
- simplified = nil
295
- else
296
- media_type = matchdata.captures[0].downcase.gsub(UNREG_RE, '')
297
- subtype = matchdata.captures[1].downcase.gsub(UNREG_RE, '')
298
- simplified = "#{media_type}/#{subtype}"
299
- end
300
- simplified
301
- end
302
-
303
- # Creates a MIME::Type from an array in the form of:
304
- # [type-name, [extensions], encoding, system]
305
- #
306
- # +extensions+, +encoding+, and +system+ are optional.
307
- #
308
- # MIME::Type.from_array("application/x-ruby", ['rb'], '8bit')
309
- # MIME::Type.from_array(["application/x-ruby", ['rb'], '8bit'])
310
- #
311
- # These are equivalent to:
312
- #
313
- # MIME::Type.new('application/x-ruby') do |t|
314
- # t.extensions = %w(rb)
315
- # t.encoding = '8bit'
316
- # end
317
- def from_array(*args) #:yields MIME::Type.new:
318
- # Dereferences the array one level, if necessary.
319
- args = args[0] if args[0].kind_of?(Array)
320
-
321
- if args.size.between?(1, 8)
322
- m = MIME::Type.new(args[0]) do |t|
323
- t.extensions = args[1] if args.size > 1
324
- t.encoding = args[2] if args.size > 2
325
- t.system = args[3] if args.size > 3
326
- t.obsolete = args[4] if args.size > 4
327
- t.docs = args[5] if args.size > 5
328
- t.url = args[6] if args.size > 6
329
- t.registered = args[7] if args.size > 7
330
- end
331
- yield m if block_given?
332
- else
333
- raise ArgumentError, "Array provided must contain between one and eight elements."
334
- end
335
- m
336
- end
337
-
338
- # Creates a MIME::Type from a hash. Keys are case-insensitive,
339
- # dashes may be replaced with underscores, and the internal Symbol
340
- # of the lowercase-underscore version can be used as well. That is,
341
- # Content-Type can be provided as content-type, Content_Type,
342
- # content_type, or :content_type.
343
- #
344
- # Known keys are <tt>Content-Type</tt>,
345
- # <tt>Content-Transfer-Encoding</tt>, <tt>Extensions</tt>, and
346
- # <tt>System</tt>.
347
- #
348
- # MIME::Type.from_hash('Content-Type' => 'text/x-yaml',
349
- # 'Content-Transfer-Encoding' => '8bit',
350
- # 'System' => 'linux',
351
- # 'Extensions' => ['yaml', 'yml'])
352
- #
353
- # This is equivalent to:
354
- #
355
- # MIME::Type.new('text/x-yaml') do |t|
356
- # t.encoding = '8bit'
357
- # t.system = 'linux'
358
- # t.extensions = ['yaml', 'yml']
359
- # end
360
- def from_hash(hash) #:yields MIME::Type.new:
361
- type = {}
362
- hash.each_pair do |k, v|
363
- type[k.to_s.tr('A-Z', 'a-z').gsub(/-/, '_').to_sym] = v
364
- end
365
-
366
- m = MIME::Type.new(type[:content_type]) do |t|
367
- t.extensions = type[:extensions]
368
- t.encoding = type[:content_transfer_encoding]
369
- t.system = type[:system]
370
- t.obsolete = type[:obsolete]
371
- t.docs = type[:docs]
372
- t.url = type[:url]
373
- t.registered = type[:registered]
374
- end
375
-
376
- yield m if block_given?
377
- m
378
- end
379
-
380
- # Essentially a copy constructor.
381
- #
382
- # MIME::Type.from_mime_type(plaintext)
383
- #
384
- # is equivalent to:
385
- #
386
- # MIME::Type.new(plaintext.content_type.dup) do |t|
387
- # t.extensions = plaintext.extensions.dup
388
- # t.system = plaintext.system.dup
389
- # t.encoding = plaintext.encoding.dup
390
- # end
391
- def from_mime_type(mime_type) #:yields the new MIME::Type:
392
- m = MIME::Type.new(mime_type.content_type.dup) do |t|
393
- t.extensions = mime_type.extensions.map { |e| e.dup }
394
- t.url = mime_type.url && mime_type.url.map { |e| e.dup }
395
-
396
- mime_type.system && t.system = mime_type.system.dup
397
- mime_type.encoding && t.encoding = mime_type.encoding.dup
398
-
399
- t.obsolete = mime_type.obsolete?
400
- t.registered = mime_type.registered?
401
-
402
- mime_type.docs && t.docs = mime_type.docs.dup
403
-
404
- end
405
-
406
- yield m if block_given?
407
- end
408
- end
409
-
410
- # Builds a MIME::Type object from the provided MIME Content Type value
411
- # (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object
412
- # is yielded to an optional block for additional configuration, such as
413
- # associating extensions and encoding information.
414
- def initialize(content_type) #:yields self:
415
- matchdata = MEDIA_TYPE_RE.match(content_type)
416
-
417
- if matchdata.nil?
418
- raise InvalidContentType, "Invalid Content-Type provided ('#{content_type}')"
419
- end
420
-
421
- @content_type = content_type
422
- @raw_media_type = matchdata.captures[0]
423
- @raw_sub_type = matchdata.captures[1]
424
-
425
- @simplified = MIME::Type.simplified(@content_type)
426
- matchdata = MEDIA_TYPE_RE.match(@simplified)
427
- @media_type = matchdata.captures[0]
428
- @sub_type = matchdata.captures[1]
429
-
430
- self.extensions = nil
431
- self.encoding = :default
432
- self.system = nil
433
- self.registered = true
434
- self.url = nil
435
- self.obsolete = nil
436
- self.docs = nil
437
-
438
- yield self if block_given?
439
- end
440
-
441
- # MIME content-types which are not regestered by IANA nor defined in
442
- # RFCs are required to start with <tt>x-</tt>. This counts as well for
443
- # a new media type as well as a new sub-type of an existing media
444
- # type. If either the media-type or the content-type begins with
445
- # <tt>x-</tt>, this method will return +false+.
446
- def registered?
447
- if (@raw_media_type =~ UNREG_RE) || (@raw_sub_type =~ UNREG_RE)
448
- false
449
- else
450
- @registered
451
- end
452
- end
453
- attr_writer :registered #:nodoc:
454
-
455
- # MIME types can be specified to be sent across a network in particular
456
- # formats. This method returns +true+ when the MIME type encoding is set
457
- # to <tt>base64</tt>.
458
- def binary?
459
- @encoding == 'base64'
460
- end
461
-
462
- # MIME types can be specified to be sent across a network in particular
463
- # formats. This method returns +false+ when the MIME type encoding is
464
- # set to <tt>base64</tt>.
465
- def ascii?
466
- not binary?
467
- end
468
-
469
- # Returns +true+ when the simplified MIME type is in the list of known
470
- # digital signatures.
471
- def signature?
472
- SIGNATURES.include?(@simplified.downcase)
473
- end
474
-
475
- # Returns +true+ if the MIME::Type is specific to an operating system.
476
- def system?
477
- not @system.nil?
478
- end
479
-
480
- # Returns +true+ if the MIME::Type is specific to the current operating
481
- # system as represented by RUBY_PLATFORM.
482
- def platform?
483
- system? and (RUBY_PLATFORM =~ @system)
484
- end
485
-
486
- # Returns +true+ if the MIME::Type specifies an extension list,
487
- # indicating that it is a complete MIME::Type.
488
- def complete?
489
- not @extensions.empty?
490
- end
3
+ ##
4
+ class Types
5
+ end
6
+ end
491
7
 
492
- # Returns the MIME type as a string.
493
- def to_s
494
- @content_type
495
- end
8
+ require 'mime/types/logger'
9
+ require 'mime/types/container'
10
+ require 'mime/type'
11
+ require 'mime/types/cache'
12
+ require 'mime/types/loader'
13
+
14
+ # MIME::Types is a registry of MIME types. It is both a class (created with
15
+ # MIME::Types.new) and a default registry (loaded automatically or through
16
+ # interactions with MIME::Types.[] and MIME::Types.type_for).
17
+ #
18
+ # == The Default mime-types Registry
19
+ #
20
+ # The default mime-types registry is loaded automatically when the library
21
+ # is required (<tt>require 'mime/types'</tt>), but it may be lazily loaded
22
+ # (loaded on first use) with the use of the environment variable
23
+ # +RUBY_MIME_TYPES_LAZY_LOAD+ having any value other than +false+. The
24
+ # initial startup is about 14× faster (~10 ms vs ~140 ms), but the
25
+ # registry will be loaded at some point in the future.
26
+ #
27
+ # The default mime-types registry can also be loaded from a Marshal cache
28
+ # file specific to the version of MIME::Types being loaded. This will be
29
+ # handled automatically with the use of a file referred to in the
30
+ # environment variable +RUBY_MIME_TYPES_CACHE+. MIME::Types will attempt to
31
+ # load the registry from this cache file (MIME::Type::Cache.load); if it
32
+ # cannot be loaded (because the file does not exist, there is an error, or
33
+ # the data is for a different version of mime-types), the default registry
34
+ # will be loaded from the normal JSON version and then the cache file will
35
+ # be *written* to the location indicated by +RUBY_MIME_TYPES_CACHE+. Cache
36
+ # file loads just over 4½× faster (~30 ms vs ~140 ms).
37
+ # loads.
38
+ #
39
+ # Notes:
40
+ # * The loading of the default registry is *not* atomic; when using a
41
+ # multi-threaded environment, it is recommended that lazy loading is not
42
+ # used and mime-types is loaded as early as possible.
43
+ # * Cache files should be specified per application in a multiprocess
44
+ # environment and should be initialized during deployment or before
45
+ # forking to minimize the chance that the multiple processes will be
46
+ # trying to write to the same cache file at the same time, or that two
47
+ # applications that are on different versions of mime-types would be
48
+ # thrashing the cache.
49
+ # * Unless cache files are preinitialized, the application using the
50
+ # mime-types cache file must have read/write permission to the cache file.
51
+ #
52
+ # == Usage
53
+ # require 'mime/types'
54
+ #
55
+ # plaintext = MIME::Types['text/plain']
56
+ # print plaintext.media_type # => 'text'
57
+ # print plaintext.sub_type # => 'plain'
58
+ #
59
+ # puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
60
+ #
61
+ # puts plaintext.encoding # => 8bit
62
+ # puts plaintext.binary? # => false
63
+ # puts plaintext.ascii? # => true
64
+ # puts plaintext.obsolete? # => false
65
+ # puts plaintext.registered? # => true
66
+ # puts plaintext == 'text/plain' # => true
67
+ # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
68
+ #
69
+ class MIME::Types
70
+ # The release version of Ruby MIME::Types
71
+ VERSION = MIME::Type::VERSION
72
+
73
+ include Enumerable
74
+
75
+ # Creates a new MIME::Types registry.
76
+ def initialize
77
+ @type_variants = Container.new
78
+ @extension_index = Container.new
79
+ end
496
80
 
497
- # Returns the MIME type as a string for implicit conversions.
498
- def to_str
499
- @content_type
500
- end
81
+ # Returns the number of known type variants.
82
+ def count
83
+ @type_variants.values.inject(0) { |a, e| a + e.size }
84
+ end
501
85
 
502
- # Returns the MIME type as an array suitable for use with
503
- # MIME::Type.from_array.
504
- def to_a
505
- [ @content_type, @extensions, @encoding, @system, @obsolete, @docs,
506
- @url, registered? ]
507
- end
86
+ def inspect # :nodoc:
87
+ "#<#{self.class}: #{count} variants, #{@extension_index.count} extensions>"
88
+ end
508
89
 
509
- # Returns the MIME type as an array suitable for use with
510
- # MIME::Type.from_hash.
511
- def to_hash
512
- { 'Content-Type' => @content_type,
513
- 'Content-Transfer-Encoding' => @encoding,
514
- 'Extensions' => @extensions,
515
- 'System' => @system,
516
- 'Obsolete' => @obsolete,
517
- 'Docs' => @docs,
518
- 'URL' => @url,
519
- 'Registered' => registered?,
520
- }
90
+ # Iterates through the type variants.
91
+ def each
92
+ if block_given?
93
+ @type_variants.each_value { |tv| tv.each { |t| yield t } }
94
+ else
95
+ enum_for(:each)
521
96
  end
522
97
  end
523
98
 
524
- # = MIME::Types
525
- # MIME types are used in MIME-compliant communications, as in e-mail or
526
- # HTTP traffic, to indicate the type of content which is transmitted.
527
- # MIME::Types provides the ability for detailed information about MIME
528
- # entities (provided as a set of MIME::Type objects) to be determined and
529
- # used programmatically. There are many types defined by RFCs and vendors,
530
- # so the list is long but not complete; don't hesitate to ask to add
531
- # additional information. This library follows the IANA collection of MIME
532
- # types (see below for reference).
533
- #
534
- # == Description
535
- # MIME types are used in MIME entities, as in email or HTTP traffic. It is
536
- # useful at times to have information available about MIME types (or,
537
- # inversely, about files). A MIME::Type stores the known information about
538
- # one MIME type.
539
- #
540
- # == Usage
541
- # require 'mime/types'
542
- #
543
- # plaintext = MIME::Types['text/plain']
544
- # print plaintext.media_type # => 'text'
545
- # print plaintext.sub_type # => 'plain'
546
- #
547
- # puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
99
+ @__types__ = nil
100
+
101
+ # Returns a list of MIME::Type objects, which may be empty. The optional
102
+ # flag parameters are <tt>:complete</tt> (finds only complete MIME::Type
103
+ # objects) and <tt>:registered</tt> (finds only MIME::Types that are
104
+ # registered). It is possible for multiple matches to be returned for
105
+ # either type (in the example below, 'text/plain' returns two values --
106
+ # one for the general case, and one for VMS systems).
548
107
  #
549
- # puts plaintext.encoding # => 8bit
550
- # puts plaintext.binary? # => false
551
- # puts plaintext.ascii? # => true
552
- # puts plaintext.obsolete? # => false
553
- # puts plaintext.registered? # => true
554
- # puts plaintext == 'text/plain' # => true
555
- # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
108
+ # puts "\nMIME::Types['text/plain']"
109
+ # MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
556
110
  #
557
- # This module is built to conform to the MIME types of RFCs 2045 and 2231.
558
- # It follows the official IANA registry at
559
- # http://www.iana.org/assignments/media-types/ and
560
- # ftp://ftp.iana.org/assignments/media-types with some unofficial types
561
- # added from the the collection at
562
- # http://www.ltsw.se/knbase/internet/mime.htp
111
+ # puts "\nMIME::Types[/^image/, complete: true]"
112
+ # MIME::Types[/^image/, :complete => true].each do |t|
113
+ # puts t.to_a.join(", ")
114
+ # end
563
115
  #
564
- # This is originally based on Perl MIME::Types by Mark Overmeer.
116
+ # If multiple type definitions are returned, returns them sorted as
117
+ # follows:
118
+ # 1. Complete definitions sort before incomplete ones;
119
+ # 2. IANA-registered definitions sort before LTSW-recorded
120
+ # definitions.
121
+ # 3. Current definitions sort before obsolete ones;
122
+ # 4. Obsolete definitions with use-instead clauses sort before those
123
+ # without;
124
+ # 5. Obsolete definitions use-instead clauses are compared.
125
+ # 6. Sort on name.
126
+ def [](type_id, complete: false, registered: false)
127
+ matches = case type_id
128
+ when MIME::Type
129
+ @type_variants[type_id.simplified]
130
+ when Regexp
131
+ match(type_id)
132
+ else
133
+ @type_variants[MIME::Type.simplified(type_id)]
134
+ end
135
+
136
+ prune_matches(matches, complete, registered).sort { |a, b|
137
+ a.priority_compare(b)
138
+ }
139
+ end
140
+
141
+ # Return the list of MIME::Types which belongs to the file based on its
142
+ # filename extension. If there is no extension, the filename will be used
143
+ # as the matching criteria on its own.
565
144
  #
566
- # = Author
567
- # Copyright:: Copyright (c) 2002 - 2009 by Austin Ziegler
568
- # <austin@rubyforge.org>
569
- # Version:: 1.17.2
570
- # Based On:: Perl
571
- # MIME::Types[http://search.cpan.org/author/MARKOV/MIME-Types-1.27/MIME/Types.pm],
572
- # Copyright (c) 2001 - 2009 by Mark Overmeer
573
- # <mimetypes@overmeer.net>.
574
- # Licence:: Ruby's, Perl Artistic, or GPL version 2 (or later)
575
- # See Also:: http://www.iana.org/assignments/media-types/
576
- # http://www.ltsw.se/knbase/internet/mime.htp
145
+ # This will always return a merged, flatten, priority sorted, unique array.
577
146
  #
578
- class Types
579
- # The released version of Ruby MIME::Types
580
- VERSION = '1.17.2'
581
-
582
- # The data version.
583
- attr_reader :data_version
584
-
585
- def initialize(data_version = nil)
586
- @type_variants = Hash.new { |h, k| h[k] = [] }
587
- @extension_index = Hash.new { |h, k| h[k] = [] }
588
- @data_version = data_version
589
- end
590
-
591
- def add_type_variant(mime_type) #:nodoc:
592
- @type_variants[mime_type.simplified] << mime_type
593
- end
594
-
595
- def index_extensions(mime_type) #:nodoc:
596
- mime_type.extensions.each { |ext| @extension_index[ext] << mime_type }
597
- end
598
-
599
- def defined_types #:nodoc:
600
- @type_variants.values.flatten
601
- end
602
-
603
- @__types__ = self.new(VERSION)
147
+ # puts MIME::Types.type_for('citydesk.xml')
148
+ # => [application/xml, text/xml]
149
+ # puts MIME::Types.type_for('citydesk.gif')
150
+ # => [image/gif]
151
+ # puts MIME::Types.type_for(%w(citydesk.xml citydesk.gif))
152
+ # => [application/xml, image/gif, text/xml]
153
+ def type_for(filename)
154
+ Array(filename).flat_map { |fn|
155
+ @extension_index[fn.chomp.downcase[/\.?([^.]*?)$/, 1]]
156
+ }.compact.inject(:+).sort { |a, b|
157
+ a.priority_compare(b)
158
+ }
159
+ end
160
+ alias_method :of, :type_for
604
161
 
605
- # Returns a list of MIME::Type objects, which may be empty. The optional
606
- # flag parameters are :complete (finds only complete MIME::Type objects)
607
- # and :platform (finds only MIME::Types for the current platform). It is
608
- # possible for multiple matches to be returned for either type (in the
609
- # example below, 'text/plain' returns two values -- one for the general
610
- # case, and one for VMS systems.
611
- #
612
- # puts "\nMIME::Types['text/plain']"
613
- # MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
614
- #
615
- # puts "\nMIME::Types[/^image/, :complete => true]"
616
- # MIME::Types[/^image/, :complete => true].each do |t|
617
- # puts t.to_a.join(", ")
618
- # end
619
- #
620
- # If multiple type definitions are returned, returns them sorted as
621
- # follows:
622
- # 1. Complete definitions sort before incomplete ones;
623
- # 2. IANA-registered definitions sort before LTSW-recorded
624
- # definitions.
625
- # 3. Generic definitions sort before platform-specific ones;
626
- # 4. Current definitions sort before obsolete ones;
627
- # 5. Obsolete definitions with use-instead clauses sort before those
628
- # without;
629
- # 6. Obsolete definitions use-instead clauses are compared.
630
- # 7. Sort on name.
631
- def [](type_id, flags = {})
632
- if type_id.kind_of?(Regexp)
633
- matches = []
634
- @type_variants.each_key do |k|
635
- matches << @type_variants[k] if k =~ type_id
636
- end
637
- matches.flatten!
638
- elsif type_id.kind_of?(MIME::Type)
639
- matches = [type_id]
162
+ # Add one or more MIME::Type objects to the set of known types. If the
163
+ # type is already known, a warning will be displayed.
164
+ #
165
+ # The last parameter may be the value <tt>:silent</tt> or +true+ which
166
+ # will suppress duplicate MIME type warnings.
167
+ def add(*types)
168
+ quiet = ((types.last == :silent) or (types.last == true))
169
+
170
+ types.each do |mime_type|
171
+ case mime_type
172
+ when true, false, nil, Symbol
173
+ nil
174
+ when MIME::Types
175
+ variants = mime_type.instance_variable_get(:@type_variants)
176
+ add(*variants.values.inject(:+).to_a, quiet)
177
+ when Array
178
+ add(*mime_type, quiet)
640
179
  else
641
- matches = @type_variants[MIME::Type.simplified(type_id)]
180
+ add_type(mime_type, quiet)
642
181
  end
643
-
644
- matches.delete_if { |e| not e.complete? } if flags[:complete]
645
- matches.delete_if { |e| not e.platform? } if flags[:platform]
646
-
647
- matches.sort { |a, b| a.priority_compare(b) }
648
- end
649
-
650
- # Return the list of MIME::Types which belongs to the file based on its
651
- # filename extension. If +platform+ is +true+, then only file types that
652
- # are specific to the current platform will be returned.
653
- #
654
- # puts "MIME::Types.type_for('citydesk.xml')
655
- # => "#{MIME::Types.type_for('citydesk.xml')}"
656
- # puts "MIME::Types.type_for('citydesk.gif')
657
- # => "#{MIME::Types.type_for('citydesk.gif')}"
658
- def type_for(filename, platform = false)
659
- ext = filename.chomp.downcase.gsub(/.*\./o, '')
660
- list = @extension_index[ext]
661
- list.delete_if { |e| not e.platform? } if platform
662
- list
663
- end
664
-
665
- # A synonym for MIME::Types.type_for
666
- def of(filename, platform = false)
667
- type_for(filename, platform)
668
182
  end
183
+ end
669
184
 
670
- # Add one or more MIME::Type objects to the set of known types. Each
671
- # type should be experimental (e.g., 'application/x-ruby'). If the type
672
- # is already known, a warning will be displayed.
673
- #
674
- # <strong>Please inform the maintainer of this module when registered
675
- # types are missing.</strong>
676
- def add(*types)
677
- types.each do |mime_type|
678
- if mime_type.kind_of? MIME::Types
679
- add(*mime_type.defined_types)
680
- else
681
- if @type_variants.include?(mime_type.simplified)
682
- if @type_variants[mime_type.simplified].include?(mime_type)
683
- warn "Type #{mime_type} already registered as a variant of #{mime_type.simplified}." unless defined? MIME::Types::STARTUP
684
- end
685
- end
686
- add_type_variant(mime_type)
687
- index_extensions(mime_type)
688
- end
689
- end
185
+ # Add a single MIME::Type object to the set of known types. If the +type+ is
186
+ # already known, a warning will be displayed. The +quiet+ parameter may be a
187
+ # truthy value to suppress that warning.
188
+ def add_type(type, quiet = false)
189
+ if !quiet and @type_variants[type.simplified].include?(type)
190
+ MIME::Types.logger.warn <<-warning
191
+ Type #{type} is already registered as a variant of #{type.simplified}.
192
+ warning
690
193
  end
691
194
 
692
- class << self
693
- def add_type_variant(mime_type) #:nodoc:
694
- @__types__.add_type_variant(mime_type)
695
- end
696
-
697
- def index_extensions(mime_type) #:nodoc:
698
- @__types__.index_extensions(mime_type)
699
- end
700
-
701
- # The regular expression used to match a file-based MIME type
702
- # definition.
703
- TEXT_FORMAT_RE = %r{
704
- ^
705
- \s*
706
- ([*])? # 0: Unregistered?
707
- (!)? # 1: Obsolete?
708
- (?:(\w+):)? # 2: Platform marker
709
- #{MIME::Type::MEDIA_TYPE_RE} # 3,4: Media type
710
- (?:\s+@([^\s]+))? # 5: Extensions
711
- (?:\s+:(#{MIME::Type::ENCODING_RE}))? # 6: Encoding
712
- (?:\s+'(.+))? # 7: URL list
713
- (?:\s+=(.+))? # 8: Documentation
714
- \s*
715
- $
716
- }x
717
-
718
- # Build the type list from a file in the format:
719
- #
720
- # [*][!][os:]mt/st[<ws>@ext][<ws>:enc][<ws>'url-list][<ws>=docs]
721
- #
722
- # == *
723
- # An unofficial MIME type. This should be used if and only if the MIME type
724
- # is not properly specified (that is, not under either x-type or
725
- # vnd.name.type).
726
- #
727
- # == !
728
- # An obsolete MIME type. May be used with an unofficial MIME type.
729
- #
730
- # == os:
731
- # Platform-specific MIME type definition.
732
- #
733
- # == mt
734
- # The media type.
735
- #
736
- # == st
737
- # The media subtype.
738
- #
739
- # == <ws>@ext
740
- # The list of comma-separated extensions.
741
- #
742
- # == <ws>:enc
743
- # The encoding.
744
- #
745
- # == <ws>'url-list
746
- # The list of comma-separated URLs.
747
- #
748
- # == <ws>=docs
749
- # The documentation string.
750
- #
751
- # That is, everything except the media type and the subtype is optional. The
752
- # more information that's available, though, the richer the values that can
753
- # be provided.
754
- def load_from_file(filename) #:nodoc:
755
- if defined? ::Encoding
756
- data = File.open(filename, 'r:UTF-8') { |f| f.read }
757
- else
758
- data = File.open(filename) { |f| f.read }
759
- end
760
- data = data.split($/)
761
- mime = MIME::Types.new
762
- data.each_with_index { |line, index|
763
- item = line.chomp.strip.gsub(%r{#.*}o, '')
764
- next if item.empty?
765
-
766
- begin
767
- m = TEXT_FORMAT_RE.match(item).captures
768
- rescue Exception => ex
769
- puts "#{filename}:#{index}: Parsing error in MIME type definitions."
770
- puts "=> #{line}"
771
- raise
772
- end
773
-
774
- unregistered, obsolete, platform, mediatype, subtype, extensions,
775
- encoding, urls, docs = *m
776
-
777
- extensions &&= extensions.split(/,/)
778
- urls &&= urls.split(/,/)
779
-
780
- mime_type = MIME::Type.new("#{mediatype}/#{subtype}") do |t|
781
- t.extensions = extensions
782
- t.encoding = encoding
783
- t.system = platform
784
- t.obsolete = obsolete
785
- t.registered = false if unregistered
786
- t.docs = docs
787
- t.url = urls
788
- end
195
+ add_type_variant!(type)
196
+ index_extensions!(type)
197
+ end
789
198
 
790
- mime.add(mime_type)
791
- }
792
- mime
793
- end
199
+ private
794
200
 
795
- # Returns a list of MIME::Type objects, which may be empty. The
796
- # optional flag parameters are :complete (finds only complete
797
- # MIME::Type objects) and :platform (finds only MIME::Types for the
798
- # current platform). It is possible for multiple matches to be
799
- # returned for either type (in the example below, 'text/plain' returns
800
- # two values -- one for the general case, and one for VMS systems.
801
- #
802
- # puts "\nMIME::Types['text/plain']"
803
- # MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
804
- #
805
- # puts "\nMIME::Types[/^image/, :complete => true]"
806
- # MIME::Types[/^image/, :complete => true].each do |t|
807
- # puts t.to_a.join(", ")
808
- # end
809
- def [](type_id, flags = {})
810
- @__types__[type_id, flags]
811
- end
201
+ def add_type_variant!(mime_type)
202
+ @type_variants[mime_type.simplified] << mime_type
203
+ end
812
204
 
813
- # Return the list of MIME::Types which belongs to the file based on
814
- # its filename extension. If +platform+ is +true+, then only file
815
- # types that are specific to the current platform will be returned.
816
- #
817
- # puts "MIME::Types.type_for('citydesk.xml')
818
- # => "#{MIME::Types.type_for('citydesk.xml')}"
819
- # puts "MIME::Types.type_for('citydesk.gif')
820
- # => "#{MIME::Types.type_for('citydesk.gif')}"
821
- def type_for(filename, platform = false)
822
- @__types__.type_for(filename, platform)
823
- end
205
+ def reindex_extensions!(mime_type)
206
+ return unless @type_variants[mime_type.simplified].include?(mime_type)
207
+ index_extensions!(mime_type)
208
+ end
824
209
 
825
- # A synonym for MIME::Types.type_for
826
- def of(filename, platform = false)
827
- @__types__.type_for(filename, platform)
828
- end
210
+ def index_extensions!(mime_type)
211
+ mime_type.extensions.each { |ext| @extension_index[ext] << mime_type }
212
+ end
829
213
 
830
- # Add one or more MIME::Type objects to the set of known types. Each
831
- # type should be experimental (e.g., 'application/x-ruby'). If the
832
- # type is already known, a warning will be displayed.
833
- #
834
- # <strong>Please inform the maintainer of this module when registered
835
- # types are missing.</strong>
836
- def add(*types)
837
- @__types__.add(*types)
838
- end
839
- end
214
+ def prune_matches(matches, complete, registered)
215
+ matches.delete_if { |e| !e.complete? } if complete
216
+ matches.delete_if { |e| !e.registered? } if registered
217
+ matches
218
+ end
840
219
 
841
- files = Dir[File.join(File.dirname(__FILE__), 'types', '*')]
842
- MIME::Types::STARTUP = true unless $DEBUG
843
- files.sort.each { |file| add load_from_file(file) }
844
- remove_const :STARTUP if defined? STARTUP
220
+ def match(pattern)
221
+ @type_variants.select { |k, _|
222
+ k =~ pattern
223
+ }.values.inject(:+)
845
224
  end
846
225
  end
847
226
 
848
- # vim: ft=ruby
227
+ require 'mime/types/columnar' unless defined?(MIME::Types::Columnar)
228
+ require 'mime/types/registry'