mime-types 3.3.1 → 3.7.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.
data/lib/mime/type.rb CHANGED
@@ -4,33 +4,71 @@
4
4
  module MIME
5
5
  end
6
6
 
7
+ require "mime/types/deprecations"
8
+
7
9
  # The definition of one MIME content-type.
8
10
  #
9
11
  # == Usage
10
- # require 'mime/types'
12
+ # require "mime/types"
11
13
  #
12
- # plaintext = MIME::Types['text/plain'].first
13
- # # returns [text/plain, text/plain]
14
+ # plaintext = MIME::Types["text/plain"] # => [ text/plain ]
14
15
  # text = plaintext.first
15
- # print text.media_type # => 'text'
16
- # print text.sub_type # => 'plain'
16
+ # puts text.media_type # => "text"
17
+ # puts text.sub_type # => "plain"
17
18
  #
18
- # puts text.extensions.join(" ") # => 'asc txt c cc h hh cpp'
19
+ # puts text.extensions.join(" ") # => "txt asc c cc h hh cpp hpp dat hlp"
20
+ # puts text.preferred_extension # => "txt"
21
+ # puts text.friendly # => "Text Document"
22
+ # puts text.i18n_key # => "text.plain"
19
23
  #
20
- # puts text.encoding # => 8bit
24
+ # puts text.encoding # => quoted-printable
25
+ # puts text.default_encoding # => quoted-printable
21
26
  # puts text.binary? # => false
22
27
  # puts text.ascii? # => true
23
- # puts text == 'text/plain' # => true
24
- # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
28
+ # puts text.obsolete? # => false
29
+ # puts text.registered? # => true
30
+ # puts text.provisional? # => false
31
+ # puts text.complete? # => true
32
+ #
33
+ # puts text # => "text/plain"
34
+ #
35
+ # puts text == "text/plain" # => true
36
+ # puts "text/plain" == text # => true
37
+ # puts text == "text/x-plain" # => false
38
+ # puts "text/x-plain" == text # => false
39
+ #
40
+ # puts MIME::Type.simplified("x-appl/x-zip") # => "x-appl/x-zip"
41
+ # puts MIME::Type.i18n_key("x-appl/x-zip") # => "x-appl.x-zip"
42
+ #
43
+ # puts text.like?("text/x-plain") # => true
44
+ # puts text.like?(MIME::Type.new("content-type" => "x-text/x-plain")) # => true
45
+ #
46
+ # puts text.xrefs.inspect # => { "rfc" => [ "rfc2046", "rfc3676", "rfc5147" ] }
47
+ # puts text.xref_urls # => [ "http://www.iana.org/go/rfc2046",
48
+ # # "http://www.iana.org/go/rfc3676",
49
+ # # "http://www.iana.org/go/rfc5147" ]
50
+ #
51
+ # xtext = MIME::Type.new("x-text/x-plain")
52
+ # puts xtext.media_type # => "text"
53
+ # puts xtext.raw_media_type # => "x-text"
54
+ # puts xtext.sub_type # => "plain"
55
+ # puts xtext.raw_sub_type # => "x-plain"
56
+ # puts xtext.complete? # => false
57
+ #
58
+ # puts MIME::Types.any? { |type| type.content_type == "text/plain" } # => true
59
+ # puts MIME::Types.all?(&:registered?) # => false
25
60
  #
26
- # puts MIME::Types.any? { |type|
27
- # type.content_type == 'text/plain'
28
- # } # => true
29
- # puts MIME::Types.all?(&:registered?)
30
- # # => false
61
+ # # Various string representations of MIME types
62
+ # qcelp = MIME::Types["audio/QCELP"].first # => audio/QCELP
63
+ # puts qcelp.content_type # => "audio/QCELP"
64
+ # puts qcelp.simplified # => "audio/qcelp"
65
+ #
66
+ # xwingz = MIME::Types["application/x-Wingz"].first # => application/x-Wingz
67
+ # puts xwingz.content_type # => "application/x-Wingz"
68
+ # puts xwingz.simplified # => "application/x-wingz"
31
69
  class MIME::Type
32
70
  # Reflects a MIME content-type specification that is not correctly
33
- # formatted (it isn't +type+/+subtype+).
71
+ # formatted (it is not +type+/+subtype+).
34
72
  class InvalidContentType < ArgumentError
35
73
  # :stopdoc:
36
74
  def initialize(type_string)
@@ -56,51 +94,70 @@ class MIME::Type
56
94
  # :startdoc:
57
95
  end
58
96
 
59
- # The released version of the mime-types library.
60
- VERSION = '3.3.1'
61
-
62
97
  include Comparable
63
98
 
64
99
  # :stopdoc:
65
- # TODO verify mime-type character restrictions; I am pretty sure that this is
66
- # too wide open.
67
- MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}.freeze
68
- I18N_RE = /[^[:alnum:]]/.freeze
69
- BINARY_ENCODINGS = %w(base64 8bit).freeze
70
- ASCII_ENCODINGS = %w(7bit quoted-printable).freeze
100
+ # Full conformance with RFC 6838 §4.2 (the recommendation for < 64 characters is not
101
+ # enforced or reported because MIME::Types mostly deals with registered data). RFC 4288
102
+ # §4.2 does not restrict the first character to alphanumeric, but the total length of
103
+ # each part is limited to 127 characters. RFCC 2045 §5.1 does not restrict the character
104
+ # composition except for whitespace, but MIME::Type was always more strict than this.
105
+ restricted_name_first = "[0-9a-zA-Z]"
106
+ restricted_name_chars = "[-!#{$&}^_.+0-9a-zA-Z]{0,126}"
107
+ restricted_name = "#{restricted_name_first}#{restricted_name_chars}"
108
+ MEDIA_TYPE_RE = %r{(#{restricted_name})/(#{restricted_name})}.freeze
109
+ I18N_RE = /[^[:alnum:]]/.freeze
110
+ BINARY_ENCODINGS = %w[base64 8bit].freeze
111
+ ASCII_ENCODINGS = %w[7bit quoted-printable].freeze
71
112
  # :startdoc:
72
113
 
73
114
  private_constant :MEDIA_TYPE_RE, :I18N_RE, :BINARY_ENCODINGS,
74
- :ASCII_ENCODINGS
115
+ :ASCII_ENCODINGS
75
116
 
76
117
  # Builds a MIME::Type object from the +content_type+, a MIME Content Type
77
- # value (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object
118
+ # value (e.g., "text/plain" or "application/x-eruby"). The constructed object
78
119
  # is yielded to an optional block for additional configuration, such as
79
120
  # associating extensions and encoding information.
80
121
  #
81
122
  # * When provided a Hash or a MIME::Type, the MIME::Type will be
82
123
  # constructed with #init_with.
124
+ #
125
+ # There are two deprecated initialization forms:
126
+ #
83
127
  # * When provided an Array, the MIME::Type will be constructed using
84
128
  # the first element as the content type and the remaining flattened
85
129
  # elements as extensions.
86
130
  # * Otherwise, the content_type will be used as a string.
87
131
  #
88
132
  # Yields the newly constructed +self+ object.
89
- def initialize(content_type) # :yields self:
133
+ def initialize(content_type) # :yields: self
90
134
  @friendly = {}
91
- @obsolete = @registered = false
92
- @preferred_extension = @docs = @use_instead = nil
135
+ @obsolete = @registered = @provisional = false
136
+ @preferred_extension = @docs = @use_instead = @__sort_priority = nil
137
+
93
138
  self.extensions = []
94
139
 
95
140
  case content_type
96
141
  when Hash
97
142
  init_with(content_type)
98
143
  when Array
144
+ MIME::Types.deprecated(
145
+ class: MIME::Type,
146
+ method: :new,
147
+ pre: "when called with an Array",
148
+ once: true
149
+ )
99
150
  self.content_type = content_type.shift
100
151
  self.extensions = content_type.flatten
101
152
  when MIME::Type
102
153
  init_with(content_type.to_h)
103
154
  else
155
+ MIME::Types.deprecated(
156
+ class: MIME::Type,
157
+ method: :new,
158
+ pre: "when called with a String",
159
+ once: true
160
+ )
104
161
  self.content_type = content_type
105
162
  end
106
163
 
@@ -108,16 +165,19 @@ class MIME::Type
108
165
  self.xrefs ||= {}
109
166
 
110
167
  yield self if block_given?
168
+
169
+ update_sort_priority
111
170
  end
112
171
 
113
172
  # Indicates that a MIME type is like another type. This differs from
114
173
  # <tt>==</tt> because <tt>x-</tt> prefixes are removed for this comparison.
115
174
  def like?(other)
116
- other = if other.respond_to?(:simplified)
117
- MIME::Type.simplified(other.simplified, remove_x_prefix: true)
118
- else
119
- MIME::Type.simplified(other.to_s, remove_x_prefix: true)
120
- end
175
+ other =
176
+ if other.respond_to?(:simplified)
177
+ MIME::Type.simplified(other.simplified, remove_x_prefix: true)
178
+ else
179
+ MIME::Type.simplified(other.to_s, remove_x_prefix: true)
180
+ end
121
181
  MIME::Type.simplified(simplified, remove_x_prefix: true) == other
122
182
  end
123
183
 
@@ -125,65 +185,93 @@ class MIME::Type
125
185
  # simplified type (the simplified type will be used if comparing against
126
186
  # something that can be treated as a String with #to_s). In comparisons, this
127
187
  # is done against the lowercase version of the MIME::Type.
188
+ #
189
+ # Note that this implementation of #<=> is deprecated and will be changed
190
+ # in the next major version to be the same as #priority_compare.
191
+ #
192
+ # Note that MIME::Types no longer compare against nil.
128
193
  def <=>(other)
129
- if other.nil?
130
- -1
131
- elsif other.respond_to?(:simplified)
194
+ return priority_compare(other) if other.is_a?(MIME::Type)
195
+ simplified <=> other
196
+ end
197
+
198
+ # Compares the +other+ MIME::Type using a pre-computed sort priority value,
199
+ # then the simplified representation for an alphabetical sort.
200
+ #
201
+ # For the next major version of MIME::Types, this method will become #<=> and
202
+ # #priority_compare will be removed.
203
+ def priority_compare(other)
204
+ if (cmp = __sort_priority <=> other.__sort_priority) == 0
132
205
  simplified <=> other.simplified
133
206
  else
134
- filtered = 'silent' if other == :silent
135
- filtered ||= 'true' if other == true
136
- filtered ||= other.to_s
137
-
138
- simplified <=> MIME::Type.simplified(filtered)
207
+ cmp
139
208
  end
140
209
  end
141
210
 
142
- # Compares the +other+ MIME::Type based on how reliable it is before doing a
143
- # normal <=> comparison. Used by MIME::Types#[] to sort types. The
144
- # comparisons involved are:
145
- #
146
- # 1. self.simplified <=> other.simplified (ensures that we
147
- # don't try to compare different types)
148
- # 2. IANA-registered definitions < other definitions.
149
- # 3. Complete definitions < incomplete definitions.
150
- # 4. Current definitions < obsolete definitions.
151
- # 5. Obselete with use-instead names < obsolete without.
152
- # 6. Obsolete use-instead definitions are compared.
211
+ # Uses a modified pre-computed sort priority value based on whether one of the provided
212
+ # extensions is the preferred extension for a type.
153
213
  #
154
- # While this method is public, its use is strongly discouraged by consumers
155
- # of mime-types. In mime-types 3, this method is likely to see substantial
156
- # revision and simplification to ensure current registered content types sort
157
- # before unregistered or obsolete content types.
158
- def priority_compare(other)
159
- pc = simplified <=> other.simplified
160
- if pc.zero? || !(extensions & other.extensions).empty?
161
- pc = if (reg = registered?) != other.registered?
162
- reg ? -1 : 1 # registered < unregistered
163
- elsif (comp = complete?) != other.complete?
164
- comp ? -1 : 1 # complete < incomplete
165
- elsif (obs = obsolete?) != other.obsolete?
166
- obs ? 1 : -1 # current < obsolete
167
- elsif obs and ((ui = use_instead) != (oui = other.use_instead))
168
- if ui.nil?
169
- 1
170
- elsif oui.nil?
171
- -1
172
- else
173
- ui <=> oui
174
- end
175
- else
176
- 0
177
- end
214
+ # This is an internal function. If an extension provided is a preferred extension either
215
+ # for this instance or the compared instance, the corresponding extension has its top
216
+ # _extension_ bit cleared from its sort priority. That means that a type with between
217
+ # 0 and 8 extensions will be treated as if it had 9 extensions.
218
+ def __extension_priority_compare(other, exts) # :nodoc:
219
+ tsp = __sort_priority
220
+
221
+ if exts.include?(preferred_extension) && tsp & 0b1000 != 0
222
+ tsp = tsp & 0b11110111 | 0b0111
223
+ end
224
+
225
+ osp = other.__sort_priority
226
+
227
+ if exts.include?(other.preferred_extension) && osp & 0b1000 != 0
228
+ osp = osp & 0b11110111 | 0b0111
178
229
  end
179
230
 
180
- pc
231
+ if (cmp = tsp <=> osp) == 0
232
+ simplified <=> other.simplified
233
+ else
234
+ cmp
235
+ end
181
236
  end
182
237
 
183
238
  # Returns +true+ if the +other+ object is a MIME::Type and the content types
184
239
  # match.
185
240
  def eql?(other)
186
- other.kind_of?(MIME::Type) and self == other
241
+ other.is_a?(MIME::Type) && (self == other)
242
+ end
243
+
244
+ # Returns a hash based on the #simplified value.
245
+ #
246
+ # This maintains the invariant that two #eql? instances must have the same
247
+ # #hash (although having the same #hash does *not* imply that the objects are
248
+ # #eql?).
249
+ #
250
+ # To see why, suppose a MIME::Type instance +a+ is compared to another object
251
+ # +b+, and that <code>a.eql?(b)</code> is true. By the definition of #eql?,
252
+ # we know the following:
253
+ #
254
+ # 1. +b+ is a MIME::Type instance itself.
255
+ # 2. <code>a == b</code> is true.
256
+ #
257
+ # Due to the first point, we know that +b+ should respond to the #simplified
258
+ # method. Thus, per the definition of #<=>, we know that +a.simplified+ must
259
+ # be equal to +b.simplified+, as compared by the <=> method corresponding to
260
+ # +a.simplified+.
261
+ #
262
+ # Presumably, if <code>a.simplified <=> b.simplified</code> is +0+, then
263
+ # +a.simplified+ has the same hash as +b.simplified+. So we assume it is
264
+ # suitable for #hash to delegate to #simplified in service of the #eql?
265
+ # invariant.
266
+ def hash
267
+ simplified.hash
268
+ end
269
+
270
+ # The computed sort priority value. This is _not_ intended to be used by most
271
+ # callers.
272
+ def __sort_priority # :nodoc:
273
+ update_sort_priority if !instance_variable_defined?(:@__sort_priority) || @__sort_priority.nil?
274
+ @__sort_priority
187
275
  end
188
276
 
189
277
  # Returns the whole MIME content-type string.
@@ -197,8 +285,7 @@ class MIME::Type
197
285
  # audio/QCELP => audio/QCELP
198
286
  attr_reader :content_type
199
287
  # A simplified form of the MIME content-type string, suitable for
200
- # case-insensitive comparison, with any extension markers (<tt>x-</tt)
201
- # removed and converted to lowercase.
288
+ # case-insensitive comparison, with the content_type converted to lowercase.
202
289
  #
203
290
  # text/plain => text/plain
204
291
  # x-chemical/x-pdb => x-chemical/x-pdb
@@ -241,6 +328,7 @@ class MIME::Type
241
328
 
242
329
  ##
243
330
  def extensions=(value) # :nodoc:
331
+ clear_sort_priority
244
332
  @extensions = Set[*Array(value).flatten.compact].freeze
245
333
  MIME::Types.send(:reindex_extensions, self)
246
334
  end
@@ -256,7 +344,7 @@ class MIME::Type
256
344
  # exceptions defined, the first extension will be used.
257
345
  #
258
346
  # When setting #preferred_extensions, if #extensions does not contain this
259
- # extension, this will be added to #xtensions.
347
+ # extension, this will be added to #extensions.
260
348
  #
261
349
  # :attr_accessor: preferred_extension
262
350
 
@@ -280,7 +368,7 @@ class MIME::Type
280
368
  # provided is invalid.
281
369
  #
282
370
  # If the encoding is not provided on construction, this will be either
283
- # 'quoted-printable' (for text/* media types) and 'base64' for eveything
371
+ # "quoted-printable" (for text/* media types) and "base64" for eveything
284
372
  # else.
285
373
  #
286
374
  # :attr_accessor: encoding
@@ -290,9 +378,9 @@ class MIME::Type
290
378
 
291
379
  ##
292
380
  def encoding=(enc) # :nodoc:
293
- if enc.nil? or enc == :default
381
+ if enc.nil? || (enc == :default)
294
382
  @encoding = default_encoding
295
- elsif BINARY_ENCODINGS.include?(enc) or ASCII_ENCODINGS.include?(enc)
383
+ elsif BINARY_ENCODINGS.include?(enc) || ASCII_ENCODINGS.include?(enc)
296
384
  @encoding = enc
297
385
  else
298
386
  fail InvalidEncoding, enc
@@ -301,7 +389,7 @@ class MIME::Type
301
389
 
302
390
  # Returns the default encoding for the MIME::Type based on the media type.
303
391
  def default_encoding
304
- @media_type == 'text' ? 'quoted-printable' : 'base64'
392
+ (@media_type == "text") ? "quoted-printable" : "base64"
305
393
  end
306
394
 
307
395
  ##
@@ -320,8 +408,16 @@ class MIME::Type
320
408
  attr_writer :use_instead
321
409
 
322
410
  # Returns +true+ if the media type is obsolete.
323
- attr_accessor :obsolete
324
- alias obsolete? obsolete
411
+ #
412
+ # :attr_accessor: obsolete
413
+ attr_reader :obsolete
414
+ alias_method :obsolete?, :obsolete
415
+
416
+ ##
417
+ def obsolete=(value)
418
+ clear_sort_priority
419
+ @obsolete = !!value
420
+ end
325
421
 
326
422
  # The documentation for this MIME::Type.
327
423
  attr_accessor :docs
@@ -330,8 +426,8 @@ class MIME::Type
330
426
  #
331
427
  # call-seq:
332
428
  # text_plain.friendly # => "Text File"
333
- # text_plain.friendly('en') # => "Text File"
334
- def friendly(lang = 'en')
429
+ # text_plain.friendly("en") # => "Text File"
430
+ def friendly(lang = "en")
335
431
  @friendly ||= {}
336
432
 
337
433
  case lang
@@ -343,7 +439,7 @@ class MIME::Type
343
439
  @friendly.update(lang)
344
440
  else
345
441
  fail ArgumentError,
346
- "Expected a language or translation set, not #{lang.inspect}"
442
+ "Expected a language or translation set, not #{lang.inspect}"
347
443
  end
348
444
  end
349
445
 
@@ -374,14 +470,38 @@ class MIME::Type
374
470
  # The decoded cross-reference URL list for this MIME::Type.
375
471
  def xref_urls
376
472
  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
473
+ name = :"xref_url_for_#{type.tr("-", "_")}"
474
+ respond_to?(name, true) && xref_map(values, name) || values.to_a
379
475
  }
380
476
  end
381
477
 
382
478
  # Indicates whether the MIME type has been registered with IANA.
383
- attr_accessor :registered
384
- alias registered? registered
479
+ #
480
+ # :attr_accessor: registered
481
+ attr_reader :registered
482
+ alias_method :registered?, :registered
483
+
484
+ ##
485
+ def registered=(value)
486
+ clear_sort_priority
487
+ @registered = !!value
488
+ end
489
+
490
+ # Indicates whether the MIME type's registration with IANA is provisional.
491
+ #
492
+ # :attr_accessor: provisional
493
+ attr_reader :provisional
494
+
495
+ ##
496
+ def provisional=(value)
497
+ clear_sort_priority
498
+ @provisional = !!value
499
+ end
500
+
501
+ # Indicates whether the MIME type's registration with IANA is provisional.
502
+ def provisional?
503
+ registered? && @provisional
504
+ end
385
505
 
386
506
  # MIME types can be specified to be sent across a network in particular
387
507
  # formats. This method returns +true+ when the MIME::Type encoding is set
@@ -399,7 +519,7 @@ class MIME::Type
399
519
 
400
520
  # Indicateswhether the MIME type is declared as a signature type.
401
521
  attr_accessor :signature
402
- alias signature? signature
522
+ alias_method :signature?, :signature
403
523
 
404
524
  # Returns +true+ if the MIME::Type specifies an extension list,
405
525
  # indicating that it is a complete MIME::Type.
@@ -415,14 +535,14 @@ class MIME::Type
415
535
  # Returns the MIME::Type as a string for implicit conversions. This allows
416
536
  # MIME::Type objects to appear on either side of a comparison.
417
537
  #
418
- # 'text/plain' == MIME::Type.new('text/plain')
538
+ # "text/plain" == MIME::Type.new("content-type" => "text/plain")
419
539
  def to_str
420
540
  content_type
421
541
  end
422
542
 
423
543
  # Converts the MIME::Type to a JSON string.
424
544
  def to_json(*args)
425
- require 'json'
545
+ require "json"
426
546
  to_h.to_json(*args)
427
547
  end
428
548
 
@@ -438,26 +558,28 @@ class MIME::Type
438
558
  #
439
559
  # This method should be considered a private implementation detail.
440
560
  def encode_with(coder)
441
- coder['content-type'] = @content_type
442
- coder['docs'] = @docs unless @docs.nil? or @docs.empty?
443
- coder['friendly'] = @friendly unless @friendly.nil? or @friendly.empty?
444
- coder['encoding'] = @encoding
445
- coder['extensions'] = @extensions.to_a unless @extensions.empty?
446
- coder['preferred-extension'] = @preferred_extension if @preferred_extension
561
+ coder["content-type"] = @content_type
562
+ coder["docs"] = @docs unless @docs.nil? || @docs.empty?
563
+ coder["friendly"] = @friendly unless @friendly.nil? || @friendly.empty?
564
+ coder["encoding"] = @encoding
565
+ coder["extensions"] = @extensions.to_a unless @extensions.empty?
566
+ coder["preferred-extension"] = @preferred_extension if @preferred_extension
447
567
  if obsolete?
448
- coder['obsolete'] = obsolete?
449
- coder['use-instead'] = use_instead if use_instead
568
+ coder["obsolete"] = obsolete?
569
+ coder["use-instead"] = use_instead if use_instead
450
570
  end
451
571
  unless xrefs.empty?
452
572
  {}.tap do |hash|
453
573
  xrefs.each do |k, v|
454
574
  hash[k] = v.to_a.sort
455
575
  end
456
- coder['xrefs'] = hash
576
+ coder["xrefs"] = hash
457
577
  end
458
578
  end
459
- coder['registered'] = registered?
460
- coder['signature'] = signature? if signature?
579
+ coder["registered"] = registered?
580
+ coder["provisional"] = provisional? if provisional?
581
+ coder["signature"] = signature? if signature?
582
+ coder["sort-priority"] = __sort_priority || 0b11111111
461
583
  coder
462
584
  end
463
585
 
@@ -466,18 +588,22 @@ class MIME::Type
466
588
  #
467
589
  # This method should be considered a private implementation detail.
468
590
  def init_with(coder)
469
- self.content_type = coder['content-type']
470
- self.docs = coder['docs'] || ''
471
- self.encoding = coder['encoding']
472
- self.extensions = coder['extensions'] || []
473
- self.preferred_extension = coder['preferred-extension']
474
- self.obsolete = coder['obsolete'] || false
475
- self.registered = coder['registered'] || false
476
- self.signature = coder['signature']
477
- self.xrefs = coder['xrefs'] || {}
478
- self.use_instead = coder['use-instead']
591
+ @__sort_priority = 0
592
+ self.content_type = coder["content-type"]
593
+ self.docs = coder["docs"] || ""
594
+ self.encoding = coder["encoding"]
595
+ self.extensions = coder["extensions"] || []
596
+ self.preferred_extension = coder["preferred-extension"]
597
+ self.obsolete = coder["obsolete"] || false
598
+ self.registered = coder["registered"] || false
599
+ self.provisional = coder["provisional"] || false
600
+ self.signature = coder["signature"]
601
+ self.xrefs = coder["xrefs"] || {}
602
+ self.use_instead = coder["use-instead"]
603
+
604
+ friendly(coder["friendly"] || {})
479
605
 
480
- friendly(coder['friendly'] || {})
606
+ update_sort_priority
481
607
  end
482
608
 
483
609
  def inspect # :nodoc:
@@ -501,8 +627,8 @@ class MIME::Type
501
627
  # Converts a provided +content_type+ into a translation key suitable for
502
628
  # use with the I18n library.
503
629
  def i18n_key(content_type)
504
- simplify_matchdata(match(content_type), joiner: '.') { |e|
505
- e.gsub!(I18N_RE, '-')
630
+ simplify_matchdata(match(content_type), joiner: ".") { |e|
631
+ e.gsub!(I18N_RE, "-")
506
632
  }
507
633
  end
508
634
 
@@ -519,12 +645,12 @@ class MIME::Type
519
645
 
520
646
  private
521
647
 
522
- def simplify_matchdata(matchdata, remove_x = false, joiner: '/')
648
+ def simplify_matchdata(matchdata, remove_x = false, joiner: "/")
523
649
  return nil unless matchdata
524
650
 
525
651
  matchdata.captures.map { |e|
526
652
  e.downcase!
527
- e.sub!(/^x-/, '') if remove_x
653
+ e.sub!(/^x-/, "") if remove_x
528
654
  yield e if block_given?
529
655
  e
530
656
  }.join(joiner)
@@ -533,15 +659,46 @@ class MIME::Type
533
659
 
534
660
  private
535
661
 
662
+ def clear_sort_priority
663
+ @__sort_priority = nil
664
+ end
665
+
666
+ # Update the __sort_priority value. Lower numbers sort better, so the
667
+ # bitmapping may seem a little odd. The _best_ sort priority is 0.
668
+ #
669
+ # | bit | meaning | details |
670
+ # | --- | --------------- | --------- |
671
+ # | 7 | obsolete | 1 if true |
672
+ # | 6 | provisional | 1 if true |
673
+ # | 5 | registered | 0 if true |
674
+ # | 4 | complete | 0 if true |
675
+ # | 3 | # of extensions | see below |
676
+ # | 2 | # of extensions | see below |
677
+ # | 1 | # of extensions | see below |
678
+ # | 0 | # of extensions | see below |
679
+ #
680
+ # The # of extensions is marked as the number of extensions subtracted from
681
+ # 16, to a minimum of 0.
682
+ def update_sort_priority
683
+ extension_count = @extensions.length
684
+ obsolete = (instance_variable_defined?(:@obsolete) && @obsolete) ? 1 << 7 : 0
685
+ provisional = (instance_variable_defined?(:@provisional) && @provisional) ? 1 << 6 : 0
686
+ registered = (instance_variable_defined?(:@registered) && @registered) ? 0 : 1 << 5
687
+ complete = extension_count.nonzero? ? 0 : 1 << 4
688
+ extension_count = [0, 16 - extension_count].max
689
+
690
+ @__sort_priority = obsolete | registered | provisional | complete | extension_count
691
+ end
692
+
536
693
  def content_type=(type_string)
537
694
  match = MEDIA_TYPE_RE.match(type_string)
538
695
  fail InvalidContentType, type_string if match.nil?
539
696
 
540
- @content_type = intern_string(type_string)
697
+ @content_type = intern_string(type_string)
541
698
  @raw_media_type, @raw_sub_type = match.captures
542
- @simplified = intern_string(MIME::Type.simplified(match))
543
- @i18n_key = intern_string(MIME::Type.i18n_key(match))
544
- @media_type, @sub_type = MEDIA_TYPE_RE.match(@simplified).captures
699
+ @simplified = intern_string(MIME::Type.simplified(match))
700
+ @i18n_key = intern_string(MIME::Type.i18n_key(match))
701
+ @media_type, @sub_type = MEDIA_TYPE_RE.match(@simplified).captures
545
702
 
546
703
  @raw_media_type = intern_string(@raw_media_type)
547
704
  @raw_sub_type = intern_string(@raw_sub_type)
@@ -554,7 +711,7 @@ class MIME::Type
554
711
  -string
555
712
  end
556
713
  else
557
- # MRI 2.2 and older don't have a method for string interning,
714
+ # MRI 2.2 and older do not have a method for string interning,
558
715
  # so we simply freeze them for keeping a similar interface
559
716
  def intern_string(string)
560
717
  string.freeze
@@ -566,22 +723,24 @@ class MIME::Type
566
723
  end
567
724
 
568
725
  def xref_url_for_rfc(value)
569
- 'http://www.iana.org/go/%s' % value
726
+ "http://www.iana.org/go/%s" % value
570
727
  end
571
728
 
572
729
  def xref_url_for_draft(value)
573
- 'http://www.iana.org/go/%s' % value.sub(/\ARFC/, 'draft')
730
+ "http://www.iana.org/go/%s" % value.sub(/\ARFC/, "draft")
574
731
  end
575
732
 
576
733
  def xref_url_for_rfc_errata(value)
577
- 'http://www.rfc-editor.org/errata_search.php?eid=%s' % value
734
+ "http://www.rfc-editor.org/errata_search.php?eid=%s" % value
578
735
  end
579
736
 
580
737
  def xref_url_for_person(value)
581
- 'http://www.iana.org/assignments/media-types/media-types.xhtml#%s' % value
738
+ "http://www.iana.org/assignments/media-types/media-types.xhtml#%s" % value
582
739
  end
583
740
 
584
741
  def xref_url_for_template(value)
585
- 'http://www.iana.org/assignments/media-types/%s' % value
742
+ "http://www.iana.org/assignments/media-types/%s" % value
586
743
  end
587
744
  end
745
+
746
+ require "mime/types/version"