@@ -133,7 +133,8 @@ def to_s
133
133
def initialize ( content_type ) # :yields: self
134
134
@friendly = { }
135
135
@obsolete = @registered = @provisional = false
136
- @preferred_extension = @docs = @use_instead = nil
136
+ @preferred_extension = @docs = @use_instead = @__sort_priority = nil
137
+
137
138
self . extensions = [ ]
138
139
139
140
case content_type
@@ -164,6 +165,8 @@ def initialize(content_type) # :yields: self
164
165
self . xrefs ||= { }
165
166
166
167
yield self if block_given?
168
+
169
+ update_sort_priority
167
170
end
168
171
169
172
# Indicates that a MIME type is like another type. This differs from
@@ -182,60 +185,54 @@ def like?(other)
182
185
# simplified type (the simplified type will be used if comparing against
183
186
# something that can be treated as a String with #to_s). In comparisons, this
184
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.
185
193
def <=>( other )
186
- if other . nil?
187
- -1
188
- 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
189
205
simplified <=> other . simplified
190
206
else
191
- filtered = "silent" if other == :silent
192
- filtered ||= "true" if other == true
193
- filtered ||= other . to_s
194
-
195
- simplified <=> MIME ::Type . simplified ( filtered )
207
+ cmp
196
208
end
197
209
end
198
210
199
- # Compares the +other+ MIME::Type based on how reliable it is before doing a
200
- # normal <=> comparison. Used by MIME::Types#[] to sort types. The
201
- # comparisons involved are:
202
- #
203
- # 1. self.simplified <=> other.simplified (ensures that we
204
- # do not try to compare different types)
205
- # 2. IANA-registered definitions < other definitions.
206
- # 3. Complete definitions < incomplete definitions.
207
- # 4. Current definitions < obsolete definitions.
208
- # 5. Obselete with use-instead names < obsolete without.
209
- # 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.
210
213
#
211
- # While this method is public, its use is strongly discouraged by consumers
212
- # of mime-types. In mime-types 3, this method is likely to see substantial
213
- # revision and simplification to ensure current registered content types sort
214
- # before unregistered or obsolete content types.
215
- def priority_compare ( other )
216
- pc = simplified <=> other . simplified
217
- if pc . zero? || !( extensions & other . extensions ) . empty?
218
- pc =
219
- if ( reg = registered? ) != other . registered?
220
- reg ? -1 : 1 # registered < unregistered
221
- elsif ( comp = complete? ) != other . complete?
222
- comp ? -1 : 1 # complete < incomplete
223
- elsif ( obs = obsolete? ) != other . obsolete?
224
- obs ? 1 : -1 # current < obsolete
225
- elsif obs && ( ( ui = use_instead ) != ( oui = other . use_instead ) )
226
- if ui . nil?
227
- 1
228
- elsif oui . nil?
229
- -1
230
- else
231
- ui <=> oui
232
- end
233
- else
234
- 0
235
- 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
236
229
end
237
230
238
- pc
231
+ if ( cmp = tsp <=> osp ) == 0
232
+ simplified <=> other . simplified
233
+ else
234
+ cmp
235
+ end
239
236
end
240
237
241
238
# Returns +true+ if the +other+ object is a MIME::Type and the content types
@@ -270,6 +267,13 @@ def hash
270
267
simplified . hash
271
268
end
272
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
275
+ end
276
+
273
277
# Returns the whole MIME content-type string.
274
278
#
275
279
# The content type is a presentation value from the MIME type registry and
@@ -324,6 +328,7 @@ def extensions
324
328
325
329
##
326
330
def extensions = ( value ) # :nodoc:
331
+ clear_sort_priority
327
332
@extensions = Set [ *Array ( value ) . flatten . compact ] . freeze
328
333
MIME ::Types . send ( :reindex_extensions , self )
329
334
end
@@ -350,9 +355,7 @@ def preferred_extension
350
355
351
356
##
352
357
def preferred_extension = ( value ) # :nodoc:
353
- if value
354
- add_extensions ( value )
355
- end
358
+ add_extensions ( value ) if value
356
359
@preferred_extension = value
357
360
end
358
361
@@ -405,9 +408,17 @@ def use_instead
405
408
attr_writer :use_instead
406
409
407
410
# Returns +true+ if the media type is obsolete.
408
- attr_accessor :obsolete
411
+ #
412
+ # :attr_accessor: obsolete
413
+ attr_reader :obsolete
409
414
alias_method :obsolete? , :obsolete
410
415
416
+ ##
417
+ def obsolete = ( value )
418
+ clear_sort_priority
419
+ @obsolete = !!value
420
+ end
421
+
411
422
# The documentation for this MIME::Type.
412
423
attr_accessor :docs
413
424
@@ -465,11 +476,27 @@ def xref_urls
465
476
end
466
477
467
478
# Indicates whether the MIME type has been registered with IANA.
468
- attr_accessor :registered
479
+ #
480
+ # :attr_accessor: registered
481
+ attr_reader :registered
469
482
alias_method :registered? , :registered
470
483
484
+ ##
485
+ def registered = ( value )
486
+ clear_sort_priority
487
+ @registered = !!value
488
+ end
489
+
471
490
# Indicates whether the MIME type's registration with IANA is provisional.
472
- attr_accessor :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
473
500
474
501
# Indicates whether the MIME type's registration with IANA is provisional.
475
502
def provisional?
@@ -552,6 +579,7 @@ def encode_with(coder)
552
579
coder [ "registered" ] = registered?
553
580
coder [ "provisional" ] = provisional? if provisional?
554
581
coder [ "signature" ] = signature? if signature?
582
+ coder [ "sort-priority" ] = __sort_priority || 0b11111111
555
583
coder
556
584
end
557
585
@@ -560,6 +588,7 @@ def encode_with(coder)
560
588
#
561
589
# This method should be considered a private implementation detail.
562
590
def init_with ( coder )
591
+ @__sort_priority = 0
563
592
self . content_type = coder [ "content-type" ]
564
593
self . docs = coder [ "docs" ] || ""
565
594
self . encoding = coder [ "encoding" ]
@@ -573,6 +602,8 @@ def init_with(coder)
573
602
self . use_instead = coder [ "use-instead" ]
574
603
575
604
friendly ( coder [ "friendly" ] || { } )
605
+
606
+ update_sort_priority
576
607
end
577
608
578
609
def inspect # :nodoc:
@@ -628,6 +659,37 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/")
628
659
629
660
private
630
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
+
631
693
def content_type = ( type_string )
632
694
match = MEDIA_TYPE_RE . match ( type_string )
633
695
fail InvalidContentType , type_string if match . nil?
0 commit comments