From 7ff705570518c458fe7911ac8e7919fc25a164c2 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Thu, 16 Dec 2021 21:37:01 -0500 Subject: [PATCH 01/15] Deprecate MIME::Types.new(Array) --- CHANGELOG.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ac73b2..1d610f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## NEXT / YYYY-MM-DD + +- 1 deprecation: + + - Deprecated `MIME::Type#priority_compare`. In a future release, this will be + will be renamed to `MIME::Type#<=>`. This method is used in tight loops, so + there is no warning message for either `MIME::Type#priority_compare` or + `MIME::Type#<=>`. + +- 1 enhancement: + + - Improved the performance of sorting by eliminating the complex comparison + flow from `MIME::Type#priority_compare`. The old version shows under 600 + i/s, and the new version shows over 900 i/s. In sorting the full set of MIME + data, there are three differences between the old and new versions; after + comparison, these differences are considered acceptable. + +- 1 bug fix: + + - Simplified the default compare implementation (`MIME::Type#<=>`) to use the + new `MIME::Type#priority_compare` operation and simplify the fallback to + `String` comparison. This _may_ result in exceptions where there had been + none, as explicit support for several special values (which should have + caused errors in any case) have been removed. + ## 3.6.2 / 2025-03-25 - Updated the reference to the changelog in the README, fixing RubyGems metadata @@ -151,7 +176,7 @@ there are some validation changes and updated code with formatting. ## 3.3 / 2019-09-04 -- 1 minor enhancement +- 1 minor enhancement: - Jean Boussier reduced memory usage for Ruby versions 2.3 or higher by interning various string values in each type. This is done with a From 5cd66d28a6c237cb94eb15b788e26b58fb18f46f Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Thu, 3 Apr 2025 00:00:57 -0400 Subject: [PATCH 02/15] mime/type Improve Comparison Performance Signed-off-by: Austin Ziegler --- lib/mime/type.rb | 141 ++++++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 50 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index 40688e8..c54aa7c 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -134,6 +134,8 @@ def initialize(content_type) # :yields: self @friendly = {} @obsolete = @registered = @provisional = false @preferred_extension = @docs = @use_instead = nil + @priority = 0 + self.extensions = [] case content_type @@ -180,62 +182,40 @@ def like?(other) # Compares the +other+ MIME::Type against the exact content type or the # simplified type (the simplified type will be used if comparing against - # something that can be treated as a String with #to_s). In comparisons, this - # is done against the lowercase version of the MIME::Type. + # something that can be treated as a String with #to_s). In comparisons, + # this is done against the lowercase version of the MIME::Type. + # + # Note that this implementation of #<=> is deprecated and will be changed + # in the next major version to be the same as #priority_compare. def <=>(other) - if other.nil? - -1 - elsif other.respond_to?(:simplified) - simplified <=> other.simplified - else - filtered = "silent" if other == :silent - filtered ||= "true" if other == true - filtered ||= other.to_s - - simplified <=> MIME::Type.simplified(filtered) - end + return priority_compare(other) if other.is_a?(MIME::Type) + simplified <=> other end - # Compares the +other+ MIME::Type based on how reliable it is before doing a - # normal <=> comparison. Used by MIME::Types#[] to sort types. The + # Compares the +other+ MIME::Type using the simplified representation, then + # a pre-computed priority value. Used by MIME::Types#[] to sort types. The # comparisons involved are: # # 1. self.simplified <=> other.simplified (ensures that we # do not try to compare different types) - # 2. IANA-registered definitions < other definitions. - # 3. Complete definitions < incomplete definitions. - # 4. Current definitions < obsolete definitions. - # 5. Obselete with use-instead names < obsolete without. - # 6. Obsolete use-instead definitions are compared. - # - # While this method is public, its use is strongly discouraged by consumers - # of mime-types. In mime-types 3, this method is likely to see substantial - # revision and simplification to ensure current registered content types sort - # before unregistered or obsolete content types. + # 2. active definitions < obsolete definitions + # 3. IANA-registered definitions < unregistered definitions + # 4. Normal registrations < Provisional registrations + # 5. Complete definitions < incomplete definitions. + # 6. self.extension count <=> other.extension count (capped to 16) + # + # After the first comparison, the comparison is simplified by using a + # precomputed 8-bit flag value. + # + # While this method is public, its direct use is strongly discouraged by + # consumers of mime-types. For the next major version of MIME::Types, this + # method will become #<=> and #priority_compare will be removed. def priority_compare(other) - pc = simplified <=> other.simplified - if pc.zero? || !(extensions & other.extensions).empty? - pc = - if (reg = registered?) != other.registered? - reg ? -1 : 1 # registered < unregistered - elsif (comp = complete?) != other.complete? - comp ? -1 : 1 # complete < incomplete - elsif (obs = obsolete?) != other.obsolete? - obs ? 1 : -1 # current < obsolete - elsif obs && ((ui = use_instead) != (oui = other.use_instead)) - if ui.nil? - 1 - elsif oui.nil? - -1 - else - ui <=> oui - end - else - 0 - end + if (cmp = simplified <=> other.simplified).zero? + __priority <=> other.__priority + else + cmp end - - pc end # Returns +true+ if the +other+ object is a MIME::Type and the content types @@ -270,6 +250,10 @@ def hash simplified.hash end + # The computed priority value. This is _not_ intended to be used + # by most callers. + attr_reader :__priority #:nodoc: + # Returns the whole MIME content-type string. # # The content type is a presentation value from the MIME type registry and @@ -325,6 +309,8 @@ def extensions ## def extensions=(value) # :nodoc: @extensions = Set[*Array(value).flatten.compact].freeze + update_priority + MIME::Types.send(:reindex_extensions, self) end @@ -405,9 +391,17 @@ def use_instead attr_writer :use_instead # Returns +true+ if the media type is obsolete. - attr_accessor :obsolete + # + # :attr_accessor: obsolete + attr_reader :obsolete alias_method :obsolete?, :obsolete + ## + def obsolete=(value) + @obsolete = !!value + update_priority + end + # The documentation for this MIME::Type. attr_accessor :docs @@ -465,11 +459,27 @@ def xref_urls end # Indicates whether the MIME type has been registered with IANA. - attr_accessor :registered + # + # :attr_accessor: registered + attr_reader :registered alias_method :registered?, :registered + ## + def registered=(value) + @registered = !!value + update_priority + end + # Indicates whether the MIME type's registration with IANA is provisional. - attr_accessor :provisional + # + # :attr_accessor: provisional + attr_reader :provisional + + ## + def provisional=(value) + @provisional = !!value + update_priority + end # Indicates whether the MIME type's registration with IANA is provisional. def provisional? @@ -628,6 +638,37 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/") private + # :stopdoc: + PRIORITY_MASK = 0b11111111 + # :startdoc: + private_constant :PRIORITY_MASK + + # Update the __priority value. Note that @provisional and @obsolete are + # _inverted_ values and are lower if false. + # + # The __priority value is masked as follows: + # + # | bit | meaning | + # | --- | -------------------- | + # | 7 | not obsolete | + # | 6 | registered | + # | 5 | not provisional | + # | 4 | complete | + # | 3 | number of extensions | + # | 2 | number of extensions | + # | 1 | number of extensions | + # | 0 | number of extensions | + def update_priority + extension_count = @extensions.length + obsolete = @obsolete ? 0 : 1 << 7 + registered = @registered ? 1 << 6 : 0 + provisional = @provisional ? 0 : 1 << 5 + complete = extension_count.nonzero? ? 1 << 6 : 0 + extension_count = [0, [extension_count, 16].max].min + + @__priority = obsolete | registered | provisional | complete | extension_count + end + def content_type=(type_string) match = MEDIA_TYPE_RE.match(type_string) fail InvalidContentType, type_string if match.nil? From 75da2cb79890442aa38b2b1d8bbd561774a29c81 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Fri, 17 Dec 2021 12:19:55 -0500 Subject: [PATCH 03/15] Reworked bitmap sort priority --- lib/mime/type.rb | 94 ++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index c54aa7c..7b5bb08 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -133,8 +133,7 @@ def to_s def initialize(content_type) # :yields: self @friendly = {} @obsolete = @registered = @provisional = false - @preferred_extension = @docs = @use_instead = nil - @priority = 0 + @preferred_extension = @docs = @use_instead = @__sort_priority = nil self.extensions = [] @@ -166,6 +165,8 @@ def initialize(content_type) # :yields: self self.xrefs ||= {} yield self if block_given? + + update_sort_priority end # Indicates that a MIME type is like another type. This differs from @@ -193,26 +194,14 @@ def <=>(other) end # Compares the +other+ MIME::Type using the simplified representation, then - # a pre-computed priority value. Used by MIME::Types#[] to sort types. The - # comparisons involved are: - # - # 1. self.simplified <=> other.simplified (ensures that we - # do not try to compare different types) - # 2. active definitions < obsolete definitions - # 3. IANA-registered definitions < unregistered definitions - # 4. Normal registrations < Provisional registrations - # 5. Complete definitions < incomplete definitions. - # 6. self.extension count <=> other.extension count (capped to 16) - # - # After the first comparison, the comparison is simplified by using a - # precomputed 8-bit flag value. + # a pre-computed sort priority value. Used by MIME::Types#[] to sort types. # # While this method is public, its direct use is strongly discouraged by # consumers of mime-types. For the next major version of MIME::Types, this # method will become #<=> and #priority_compare will be removed. def priority_compare(other) if (cmp = simplified <=> other.simplified).zero? - __priority <=> other.__priority + __sort_priority <=> other.__sort_priority else cmp end @@ -250,9 +239,11 @@ def hash simplified.hash end - # The computed priority value. This is _not_ intended to be used - # by most callers. - attr_reader :__priority #:nodoc: + # The computed sort priority value. This is _not_ intended to be used by most + # callers. + def __sort_priority + @__sort_priority || update_sort_priority + end # Returns the whole MIME content-type string. # @@ -308,9 +299,8 @@ def extensions ## def extensions=(value) # :nodoc: + clear_sort_priority @extensions = Set[*Array(value).flatten.compact].freeze - update_priority - MIME::Types.send(:reindex_extensions, self) end @@ -398,8 +388,8 @@ def use_instead ## def obsolete=(value) + clear_sort_priority @obsolete = !!value - update_priority end # The documentation for this MIME::Type. @@ -466,8 +456,8 @@ def xref_urls ## def registered=(value) + clear_sort_priority @registered = !!value - update_priority end # Indicates whether the MIME type's registration with IANA is provisional. @@ -477,8 +467,8 @@ def registered=(value) ## def provisional=(value) + clear_sort_priority @provisional = !!value - update_priority end # Indicates whether the MIME type's registration with IANA is provisional. @@ -562,6 +552,7 @@ def encode_with(coder) coder["registered"] = registered? coder["provisional"] = provisional? if provisional? coder["signature"] = signature? if signature? + coder["__sort_priority"] = __sort_priority coder end @@ -581,6 +572,7 @@ def init_with(coder) self.signature = coder["signature"] self.xrefs = coder["xrefs"] || {} self.use_instead = coder["use-instead"] + @__sort_priority = coder["__sort_priority"] friendly(coder["friendly"] || {}) end @@ -638,35 +630,35 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/") private - # :stopdoc: - PRIORITY_MASK = 0b11111111 - # :startdoc: - private_constant :PRIORITY_MASK - - # Update the __priority value. Note that @provisional and @obsolete are - # _inverted_ values and are lower if false. - # - # The __priority value is masked as follows: - # - # | bit | meaning | - # | --- | -------------------- | - # | 7 | not obsolete | - # | 6 | registered | - # | 5 | not provisional | - # | 4 | complete | - # | 3 | number of extensions | - # | 2 | number of extensions | - # | 1 | number of extensions | - # | 0 | number of extensions | - def update_priority + def clear_sort_priority + @__sort_priority = nil + end + + # Update the __sort_priority value. Lower numbers sort better, so the + # bitmapping may seem a little odd. The _best_ sort priority is 0. + # + # | bit | meaning | details | + # | --- | --------------- | --------- | + # | 7 | obsolete | 1 if true | + # | 6 | provisional | 1 if true | + # | 5 | registered | 0 if true | + # | 4 | complete | 0 if true | + # | 3 | # of extensions | see below | + # | 2 | # of extensions | see below | + # | 1 | # of extensions | see below | + # | 0 | # of extensions | see below | + # + # The # of extensions is marked as the number of extensions subtracted from + # 16, to a minimum of 0. + def update_sort_priority extension_count = @extensions.length - obsolete = @obsolete ? 0 : 1 << 7 - registered = @registered ? 1 << 6 : 0 - provisional = @provisional ? 0 : 1 << 5 - complete = extension_count.nonzero? ? 1 << 6 : 0 - extension_count = [0, [extension_count, 16].max].min + obsolete = @obsolete ? 1 << 7 : 0 + provisional = @provisional ? 1 << 6 : 0 + registered = @registered ? 0 : 1 << 5 + complete = extension_count.nonzero? ? 0 : 1 << 4 + extension_count = [0, 16 - extension_count].max - @__priority = obsolete | registered | provisional | complete | extension_count + @__sort_priority = obsolete | registered | provisional | complete | extension_count end def content_type=(type_string) From 7a531eb075170bb2559437c2f59cb3f17b9c7e8b Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Wed, 5 Jan 2022 11:45:44 -0500 Subject: [PATCH 04/15] Support extension priorities --- lib/mime/type.rb | 44 +++++++++++++++++++++++++++++++++++-- lib/mime/type/columnar.rb | 2 ++ lib/mime/types.rb | 14 +++++++++--- lib/mime/types/_columnar.rb | 35 ++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index 7b5bb08..12445dc 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -134,6 +134,7 @@ def initialize(content_type) # :yields: self @friendly = {} @obsolete = @registered = @provisional = false @preferred_extension = @docs = @use_instead = @__sort_priority = nil + __extension_priorities self.extensions = [] @@ -328,10 +329,29 @@ def preferred_extension def preferred_extension=(value) # :nodoc: if value add_extensions(value) + set_preferred_extension_priority(value) + else + clear_extension_priority(@preferred_extension) end @preferred_extension = value end + ## + # Optional extension priorities for this MIME type. This is a relative value + # similar to nice(1). An explicitly set `preferred_extension` is automatically + # given a relative priority of `-10`. + # + # :attr_reader: extension_priorities + attr_accessor :extension_priorities + + ## + # Returns the priority for the provided extension or extensions. If a priority + # is not set, the default priority is 0. The range for priorities is -20..20, + # inclusive. + def extension_priority(*exts) + exts.map { |ext| get_extension_priority(ext) }.min + end + ## # The encoding (+7bit+, +8bit+, quoted-printable, or +base64+) # required to transport the data of this content type safely across a @@ -552,7 +572,8 @@ def encode_with(coder) coder["registered"] = registered? coder["provisional"] = provisional? if provisional? coder["signature"] = signature? if signature? - coder["__sort_priority"] = __sort_priority + coder["sort-priority"] = __sort_priority + coder["extension-priorities"] = __extension_priorities unless __extension_priorities.empty? coder end @@ -561,6 +582,7 @@ def encode_with(coder) # # This method should be considered a private implementation detail. def init_with(coder) + @__sort_priority = 0 self.content_type = coder["content-type"] self.docs = coder["docs"] || "" self.encoding = coder["encoding"] @@ -572,9 +594,11 @@ def init_with(coder) self.signature = coder["signature"] self.xrefs = coder["xrefs"] || {} self.use_instead = coder["use-instead"] - @__sort_priority = coder["__sort_priority"] + self.extension_priorities = coder["extension-priorities"] friendly(coder["friendly"] || {}) + + update_sort_priority end def inspect # :nodoc: @@ -630,6 +654,22 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/") private + def __extension_priorities + @extension_priorities ||= {} + end + + def clear_extension_priority(ext) + __extension_priorities.delete(ext) if ext + end + + def get_extension_priority(ext) + [[-20, __extension_priorities[ext] || 0].max, 20].min + end + + def set_preferred_extension_priority(ext) + __extension_priorities[ext] = -10 unless __extension_priorities.has_key?(ext) + end + def clear_sort_priority @__sort_priority = nil end diff --git a/lib/mime/type/columnar.rb b/lib/mime/type/columnar.rb index 1b7c3ca..ec4dec2 100644 --- a/lib/mime/type/columnar.rb +++ b/lib/mime/type/columnar.rb @@ -39,6 +39,7 @@ def self.column(*methods, file: nil) # :nodoc: :signature?, :provisional, :provisional=, :provisional?, file: "flags" column :xrefs, :xrefs=, :xref_urls column :use_instead, :use_instead= + column :extension_priorities, :extension_priorities= def encode_with(coder) # :nodoc: @container.send(:load_friendly) @@ -48,6 +49,7 @@ def encode_with(coder) # :nodoc: @container.send(:load_use_instead) @container.send(:load_xrefs) @container.send(:load_preferred_extension) + @container.send(:load_extension_priorities) super end diff --git a/lib/mime/types.rb b/lib/mime/types.rb index 0aba5b1..f9ba09e 100644 --- a/lib/mime/types.rb +++ b/lib/mime/types.rb @@ -148,10 +148,18 @@ def [](type_id, complete: false, registered: false) # puts MIME::Types.type_for(%w(citydesk.xml citydesk.gif)) # => [application/xml, image/gif, text/xml] def type_for(filename) - Array(filename).flat_map { |fn| - @extension_index[fn.chomp.downcase[/\.?([^.]*?)\z/m, 1]] + extensions = Array(filename).map { |fn| fn.chomp.downcase[/\.?([^.]*?)$/, 1] } + + extensions.flat_map { |ext| + @extension_index[ext] }.compact.inject(Set.new, :+).sort { |a, b| - a.priority_compare(b) + by_ext = a.extension_priority(*extensions) <=> b.extension_priority(*extensions) + + if by_ext.zero? + a.priority_compare(b) + else + by_ext + end } end alias_method :of, :type_for diff --git a/lib/mime/types/_columnar.rb b/lib/mime/types/_columnar.rb index 9f8c132..253920c 100644 --- a/lib/mime/types/_columnar.rb +++ b/lib/mime/types/_columnar.rb @@ -91,7 +91,7 @@ def load_flags def load_xrefs each_file_line("xrefs") { |type, line| - type.instance_variable_set(:@xrefs, dict(line, array: true)) + type.instance_variable_set(:@xrefs, dict(line, transform: :array)) } end @@ -107,18 +107,47 @@ def load_use_instead end end - def dict(line, array: false) + def load_extension_priorities + each_file_line("extpri") do |type, line| + type.instance_variable_set(:@extension_priorities, dict(line, transform: :extension_priority)) + end + rescue + # This path preserves backwards compatibility. + end + + def dict(line, transform: nil) if line == "-" {} else line.split("|").each_with_object({}) { |l, h| k, v = l.split("^") v = nil if v.empty? - h[k] = array ? Array(v) : v + + if transform + send(:"dict_#{transform}", h, k, v) + else + h[k] = v + end } end end + def dict_extension_priority(h, k, v) + return if v.nil? + + v = v.to_i if v.kind_of?(String) + v = v.trunc if v.kind_of?(Float) + v = [[-20, v].max, 20].min + + return if v.zero? + + h[k] = v + end + + def dict_array(h, k, v) + h[k] = Array(v) + end + def arr(line) if line == "-" [] From 8d3b2c491283081b71f1f1102d1866de0c2d07f5 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Thu, 6 Jan 2022 23:06:12 -0500 Subject: [PATCH 05/15] Ensure priority sort over alpha sort - Added extension priority map. This is an imperfect solution, and is not used by default with default configuration (column-based data). - We may want to consider a revised columnar format for a future version that has a bit more information than is present in the base file. - Adding the sort priority and extension priority helped, but because the alphanumeric sort was first in `MIME::Type#priority_compare`, the results weren't as good as they should have been. We now sort by the sort priority values _first_ and the alphanumeric values _second_. - Stored sort priority was not respected because it depends on flags not kept in the base file. Added support for a binary file with this to ensure it is loaded. --- lib/mime/type.rb | 31 ++++++++++++++++++++++--------- lib/mime/type/columnar.rb | 11 +++++++++++ lib/mime/types.rb | 4 ++++ lib/mime/types/_columnar.rb | 27 +++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index 12445dc..acbda76 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -201,8 +201,8 @@ def <=>(other) # consumers of mime-types. For the next major version of MIME::Types, this # method will become #<=> and #priority_compare will be removed. def priority_compare(other) - if (cmp = simplified <=> other.simplified).zero? - __sort_priority <=> other.__sort_priority + if (cmp = __sort_priority <=> other.__sort_priority).zero? + simplified <=> other.simplified else cmp end @@ -242,7 +242,7 @@ def hash # The computed sort priority value. This is _not_ intended to be used by most # callers. - def __sort_priority + def __sort_priority # :nodoc: @__sort_priority || update_sort_priority end @@ -337,17 +337,24 @@ def preferred_extension=(value) # :nodoc: end ## - # Optional extension priorities for this MIME type. This is a relative value - # similar to nice(1). An explicitly set `preferred_extension` is automatically - # given a relative priority of `-10`. + # Optional extension priorities for this MIME type. This is a map of + # extensions to relative priority values (+-20..20+) similar to +nice(1)+. + # Unless otherwise specified in the data, an explicitly set + # +preferred_extension+ is automatically given a relative priority of +-10+. # # :attr_reader: extension_priorities attr_accessor :extension_priorities ## # Returns the priority for the provided extension or extensions. If a priority - # is not set, the default priority is 0. The range for priorities is -20..20, - # inclusive. + # is not set, the default priority is +0+. The range for priorities is + # +-20..20+, inclusive. + # + # Obsolete MIME types have a +3 penalty applied to their + # extension priority and unregistered MIME types have a +2 + # penalty to their extension priority, meaning that the highest priority an + # obsolete, unregistered MIME type can have is +-15+. The lowest priority is + # always +20. def extension_priority(*exts) exts.map { |ext| get_extension_priority(ext) }.min end @@ -663,7 +670,7 @@ def clear_extension_priority(ext) end def get_extension_priority(ext) - [[-20, __extension_priorities[ext] || 0].max, 20].min + [[-20, (__extension_priorities[ext] || 0) + __priority_penalty].max, 20].min end def set_preferred_extension_priority(ext) @@ -699,6 +706,12 @@ def update_sort_priority extension_count = [0, 16 - extension_count].max @__sort_priority = obsolete | registered | provisional | complete | extension_count + @__priority_penalty = (@obsolete ? 3 : 0) + (@registered ? 0 : 2) + end + + def __priority_penalty + update_sort_priority if @__priority_penalty.nil? + @__priority_penalty end def content_type=(type_string) diff --git a/lib/mime/type/columnar.rb b/lib/mime/type/columnar.rb index ec4dec2..a51f9d9 100644 --- a/lib/mime/type/columnar.rb +++ b/lib/mime/type/columnar.rb @@ -53,6 +53,17 @@ def encode_with(coder) # :nodoc: super end + def update_sort_priority + if @container.__fully_loaded? + super + else + obsolete = (@__sort_priority & (1 << 7)) != 0 + registered = (@__sort_priority & (1 << 5)) == 0 + + @__priority_penalty = (@obsolete ? 3 : 0) + (@registered ? 0 : 2) + end + end + class << self undef column end diff --git a/lib/mime/types.rb b/lib/mime/types.rb index f9ba09e..f5ed147 100644 --- a/lib/mime/types.rb +++ b/lib/mime/types.rb @@ -201,6 +201,10 @@ def add_type(type, quiet = false) index_extensions!(type) end + def __fully_loaded? # :nodoc: + true + end + private def add_type_variant!(mime_type) diff --git a/lib/mime/types/_columnar.rb b/lib/mime/types/_columnar.rb index 253920c..c191e39 100644 --- a/lib/mime/types/_columnar.rb +++ b/lib/mime/types/_columnar.rb @@ -18,6 +18,10 @@ def self.extended(obj) # :nodoc: obj.instance_variable_set(:@__files__, Set.new) end + def __fully_loaded? # :nodoc: + @__files__.size == 10 + end + # Load the first column data file (type and extensions). def load_base_data(path) # :nodoc: @__root__ = path @@ -33,6 +37,10 @@ def load_base_data(path) # :nodoc: add(type) end + each_file_byte("spri") do |type, byte| + type.instance_variable_set(:@__sort_priority, byte) + end + self end @@ -60,6 +68,25 @@ def each_file_line(name, lookup = true) end end + def each_file_byte(name) + LOAD_MUTEX.synchronize do + next if @__files__.include?(name) + + i = -1 + + filename = File.join(@__root__, "mime.#{name}.column") + + next unless File.exist?(filename) + + IO.binread(filename).unpack("C*").each do |byte| + (type = @__mime_data__[i += 1]) || next + yield type, byte + end + + @__files__ << name + end + end + def load_encoding each_file_line("encoding") do |type, line| pool ||= {} From 15135b54a634f05a7d68d45501dd8c7a65e6f14b Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Fri, 7 Jan 2022 11:48:24 -0500 Subject: [PATCH 06/15] # This is a combination of 2 commits. # This is the 1st commit message: WIP # This is the commit message #2: More WIP, still does not work --- lib/mime/type.rb | 30 +++++++++++--------- lib/mime/type/columnar.rb | 6 ++-- lib/mime/types.rb | 8 ++---- lib/mime/types/_columnar.rb | 7 ++--- test/test_mime_type.rb | 52 +++++++++++++++++++---------------- test/test_mime_types_class.rb | 7 ++--- 6 files changed, 58 insertions(+), 52 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index acbda76..a95a305 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -184,11 +184,13 @@ def like?(other) # Compares the +other+ MIME::Type against the exact content type or the # simplified type (the simplified type will be used if comparing against - # something that can be treated as a String with #to_s). In comparisons, - # this is done against the lowercase version of the MIME::Type. + # something that can be treated as a String with #to_s). In comparisons, this + # is done against the lowercase version of the MIME::Type. # # Note that this implementation of #<=> is deprecated and will be changed # in the next major version to be the same as #priority_compare. + # + # Note that MIME::Types no longer compare against nil. def <=>(other) return priority_compare(other) if other.is_a?(MIME::Type) simplified <=> other @@ -201,7 +203,7 @@ def <=>(other) # consumers of mime-types. For the next major version of MIME::Types, this # method will become #<=> and #priority_compare will be removed. def priority_compare(other) - if (cmp = __sort_priority <=> other.__sort_priority).zero? + if (cmp = __sort_priority <=> other.__sort_priority) == 0 simplified <=> other.simplified else cmp @@ -243,7 +245,8 @@ def hash # The computed sort priority value. This is _not_ intended to be used by most # callers. def __sort_priority # :nodoc: - @__sort_priority || update_sort_priority + update_sort_priority if !instance_variable_defined?(:@__sort_priority) || @__sort_priority.nil? + @__sort_priority end # Returns the whole MIME content-type string. @@ -330,7 +333,7 @@ def preferred_extension=(value) # :nodoc: if value add_extensions(value) set_preferred_extension_priority(value) - else + elsif instance_variable_defined?(:@preferred_extension) clear_extension_priority(@preferred_extension) end @preferred_extension = value @@ -579,7 +582,7 @@ def encode_with(coder) coder["registered"] = registered? coder["provisional"] = provisional? if provisional? coder["signature"] = signature? if signature? - coder["sort-priority"] = __sort_priority + coder["sort-priority"] = __sort_priority || 0b11111111 coder["extension-priorities"] = __extension_priorities unless __extension_priorities.empty? coder end @@ -659,12 +662,12 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/") end end - private - - def __extension_priorities + def __extension_priorities # :nodoc: @extension_priorities ||= {} end + private + def clear_extension_priority(ext) __extension_priorities.delete(ext) if ext end @@ -699,14 +702,15 @@ def clear_sort_priority # 16, to a minimum of 0. def update_sort_priority extension_count = @extensions.length - obsolete = @obsolete ? 1 << 7 : 0 - provisional = @provisional ? 1 << 6 : 0 - registered = @registered ? 0 : 1 << 5 + obsolete = (instance_variable_defined?(:@obsolete) && @obsolete) ? 1 << 7 : 0 + provisional = (instance_variable_defined?(:@provisional) && @provisional) ? 1 << 6 : 0 + registered = (instance_variable_defined?(:@registered) && @registered) ? 0 : 1 << 5 complete = extension_count.nonzero? ? 0 : 1 << 4 extension_count = [0, 16 - extension_count].max @__sort_priority = obsolete | registered | provisional | complete | extension_count - @__priority_penalty = (@obsolete ? 3 : 0) + (@registered ? 0 : 2) + @__priority_penalty = ((instance_variable_defined?(:@obsolete) && @obsolete) ? 3 : 0) + + ((instance_variable_defined?(:@registered) && @registered) ? 0 : 2) end def __priority_penalty diff --git a/lib/mime/type/columnar.rb b/lib/mime/type/columnar.rb index a51f9d9..11719d8 100644 --- a/lib/mime/type/columnar.rb +++ b/lib/mime/type/columnar.rb @@ -15,8 +15,10 @@ class MIME::Type::Columnar < MIME::Type def initialize(container, content_type, extensions) # :nodoc: @container = container + @__priority_penalty = nil self.content_type = content_type - self.extensions = extensions + @extensions = Set[*Array(extensions).flatten.compact].freeze + clear_sort_priority end def self.column(*methods, file: nil) # :nodoc: @@ -60,7 +62,7 @@ def update_sort_priority obsolete = (@__sort_priority & (1 << 7)) != 0 registered = (@__sort_priority & (1 << 5)) == 0 - @__priority_penalty = (@obsolete ? 3 : 0) + (@registered ? 0 : 2) + @__priority_penalty = (obsolete ? 3 : 0) + (registered ? 0 : 2) end end diff --git a/lib/mime/types.rb b/lib/mime/types.rb index f5ed147..7082376 100644 --- a/lib/mime/types.rb +++ b/lib/mime/types.rb @@ -148,12 +148,10 @@ def [](type_id, complete: false, registered: false) # puts MIME::Types.type_for(%w(citydesk.xml citydesk.gif)) # => [application/xml, image/gif, text/xml] def type_for(filename) - extensions = Array(filename).map { |fn| fn.chomp.downcase[/\.?([^.]*?)$/, 1] } - - extensions.flat_map { |ext| - @extension_index[ext] + Array(filename).flat_map { |fn| + @extension_index[fn.chomp.downcase[/\.?([^.]*?)\z/m, 1]] }.compact.inject(Set.new, :+).sort { |a, b| - by_ext = a.extension_priority(*extensions) <=> b.extension_priority(*extensions) + by_ext = a.extension_priority(*a.extensions) <=> b.extension_priority(*b.extensions) if by_ext.zero? a.priority_compare(b) diff --git a/lib/mime/types/_columnar.rb b/lib/mime/types/_columnar.rb index c191e39..ab39df1 100644 --- a/lib/mime/types/_columnar.rb +++ b/lib/mime/types/_columnar.rb @@ -30,7 +30,6 @@ def load_base_data(path) # :nodoc: line = line.split content_type = line.shift extensions = line - # content_type, *extensions = line.split type = MIME::Type::Columnar.new(self, content_type, extensions) @__mime_data__ << type @@ -162,9 +161,9 @@ def dict(line, transform: nil) def dict_extension_priority(h, k, v) return if v.nil? - v = v.to_i if v.kind_of?(String) - v = v.trunc if v.kind_of?(Float) - v = [[-20, v].max, 20].min + v = v.to_i if v.is_a?(String) + v = v.trunc if v.is_a?(Float) + v = [[-20, v].max, 20].min return if v.zero? diff --git a/test/test_mime_type.rb b/test/test_mime_type.rb index a2349ca..0a0666a 100644 --- a/test/test_mime_type.rb +++ b/test/test_mime_type.rb @@ -175,11 +175,6 @@ def mime_type(content_type) refute_equal text_plain, "text/html" assert_operator text_plain, :>, "text/html" end - - it "correctly compares against nil" do - refute_equal text_html, nil - assert_operator text_plain, :<, nil - end end describe "#ascii?" do @@ -326,16 +321,20 @@ def mime_type(content_type) end describe "#priority_compare" do + def priority(type) + "#{type} (#{("%08b" % type.__sort_priority).chars.join(" ")})" + end + def assert_priority_less(left, right) - assert_equal(-1, left.priority_compare(right)) + assert_equal(-1, left.priority_compare(right), "#{priority(left)} is not less than #{priority(right)}") end def assert_priority_same(left, right) - assert_equal 0, left.priority_compare(right) + assert_equal 0, left.priority_compare(right), "#{priority(left)} is not equal to #{priority(right)}" end def assert_priority_more(left, right) - assert_equal 1, left.priority_compare(right) + assert_equal 1, left.priority_compare(right), "#{priority(left)} is not more than #{priority(right)}" end def assert_priority(left, middle, right) @@ -348,19 +347,24 @@ def assert_priority(left, middle, right) let(:text_1p) { mime_type("content-type" => "text/1") } let(:text_2) { mime_type("content-type" => "text/2") } - it "sorts (1) based on the simplified type" do + it "sorts based on the simplified type when the sort priorities are the same" do assert_priority text_1, text_1p, text_2 end - it "sorts (2) based on extensions" do - text_1.extensions = ["foo", "bar"] - text_2.extensions = ["foo"] + it "sorts obsolete types higher than non-obsolete types" do + text_1.obsolete = text_1p.obsolete = false + text_1b = mime_type(text_1) { |t| t.obsolete = true } + + assert_priority_less text_1, text_1b - assert_priority_same text_1, text_2 + assert_priority text_1, text_1p, text_1b + end - text_2.registered = true + it "sorts provisional types higher than non-provisional types" do + text_1.provisional = text_1p.provisional = true + text_1b = mime_type(text_1) { |t| t.provisional = false } - assert_priority_more text_1, text_2 + assert_priority text_1, text_1p, text_1b end it "sorts (3) based on the registration state" do @@ -377,13 +381,6 @@ def assert_priority(left, middle, right) assert_priority text_1, text_1p, text_1b end - it "sorts (5) based on obsolete status" do - text_1.obsolete = text_1p.obsolete = false - text_1b = mime_type(text_1) { |t| t.obsolete = true } - - assert_priority text_1, text_1p, text_1b - end - it "sorts (5) based on the use-instead value" do text_1.obsolete = text_1p.obsolete = true text_1.use_instead = text_1p.use_instead = "abc/xyz" @@ -395,6 +392,13 @@ def assert_priority(left, middle, right) assert_priority text_1, text_1p, text_1b end + + it "sorts based on extensions (more extensions sort lower)" do + text_1.extensions = ["foo", "bar"] + text_2.extensions = ["foo"] + + assert_priority_less text_1, text_2 + end end describe "#raw_media_type" do @@ -502,10 +506,10 @@ def assert_has_keys(wanted_keys, actual, msg = nil) describe "#to_json" do let(:expected_1) { - '{"content-type":"a/b","encoding":"base64","registered":false}' + '{"content-type":"a/b","encoding":"base64","registered":false,"sort-priority":48}' } let(:expected_2) { - '{"content-type":"a/b","encoding":"base64","registered":true,"provisional":true}' + '{"content-type":"a/b","encoding":"base64","registered":true,"provisional":true,"sort-priority":80}' } it "converts to JSON when requested" do diff --git a/test/test_mime_types_class.rb b/test/test_mime_types_class.rb index c4b8a33..66122f3 100644 --- a/test/test_mime_types_class.rb +++ b/test/test_mime_types_class.rb @@ -47,7 +47,7 @@ def setup } # This is this way because of a new type ending with gzip that only # appears in some data files. - assert_equal %w[application/gzip application/x-gzip multipart/x-gzip], types + assert_equal %w[application/gzip multipart/x-gzip application/x-gzip], types assert_equal 3, types.size end @@ -86,9 +86,8 @@ def setup assert_equal %w[image/jpeg], MIME::Types.of(["foo.jpeg", "bar.jpeg"]) end - it "finds multiple extensions" do - assert_equal %w[image/jpeg text/plain], - MIME::Types.type_for(%w[foo.txt foo.jpeg]) + it "finds multiple extensions ordered by the filename list" do + assert_equal %w[text/plain image/jpeg], MIME::Types.type_for(%w[foo.txt foo.jpeg]) end it "does not find unknown extensions" do From 0c650fd042b4d84cfd45f441c9def46332dcf71c Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Thu, 3 Apr 2025 00:47:30 -0400 Subject: [PATCH 07/15] Fix last tests --- CHANGELOG.md | 37 ++++++++++++++++++------------------- Rakefile | 4 ++++ mime-types.gemspec | 11 ++++++----- test/minitest_helper.rb | 2 ++ test/test_mime_type.rb | 26 ++++++++++---------------- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d610f9..2601bc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,28 @@ # Changelog -## NEXT / YYYY-MM-DD +## 4.0.0 / YYYY-MM-DD -- 1 deprecation: +- The minimum supported Ruby version for mime-types 4 is Ruby 3.2. All backport + support for older versions of Ruby have been removed. - - Deprecated `MIME::Type#priority_compare`. In a future release, this will be - will be renamed to `MIME::Type#<=>`. This method is used in tight loops, so - there is no warning message for either `MIME::Type#priority_compare` or - `MIME::Type#<=>`. +## 3.7.0 / YYYY-MM-DD -- 1 enhancement: +- Deprecated `MIME::Type#priority_compare`. In a future release, this will be + will be renamed to `MIME::Type#<=>`. This method is used in tight loops, so + there is no warning message for either `MIME::Type#priority_compare` or + `MIME::Type#<=>`. - - Improved the performance of sorting by eliminating the complex comparison - flow from `MIME::Type#priority_compare`. The old version shows under 600 - i/s, and the new version shows over 900 i/s. In sorting the full set of MIME - data, there are three differences between the old and new versions; after - comparison, these differences are considered acceptable. +- Improved the performance of sorting by eliminating the complex comparison flow + from `MIME::Type#priority_compare`. The old version shows under 600 i/s, and + the new version shows over 900 i/s. In sorting the full set of MIME data, + there are three differences between the old and new versions; after + comparison, these differences are considered acceptable. -- 1 bug fix: - - - Simplified the default compare implementation (`MIME::Type#<=>`) to use the - new `MIME::Type#priority_compare` operation and simplify the fallback to - `String` comparison. This _may_ result in exceptions where there had been - none, as explicit support for several special values (which should have - caused errors in any case) have been removed. +- Simplified the default compare implementation (`MIME::Type#<=>`) to use the + new `MIME::Type#priority_compare` operation and simplify the fallback to + `String` comparison. This _may_ result in exceptions where there had been + none, as explicit support for several special values (which should have caused + errors in any case) have been removed. ## 3.6.2 / 2025-03-25 diff --git a/Rakefile b/Rakefile index 40c1759..39d1d87 100644 --- a/Rakefile +++ b/Rakefile @@ -2,6 +2,7 @@ require "rubygems" require "hoe" require "rake/clean" require "minitest" +require "minitest/test_task" Hoe.plugin :halostatue Hoe.plugin :rubygems @@ -10,6 +11,7 @@ Hoe.plugins.delete :debug Hoe.plugins.delete :newb Hoe.plugins.delete :publish Hoe.plugins.delete :signing +Hoe.plugins.delete :test spec = Hoe.spec "mime-types" do developer("Austin Ziegler", "halostatue@gmail.com") @@ -65,6 +67,8 @@ Minitest::TestTask.create :coverage do |t| RUBY end +task default: :test + namespace :benchmark do task :support do %w[lib support].each { |path| diff --git a/mime-types.gemspec b/mime-types.gemspec index c4f1961..1a29983 100644 --- a/mime-types.gemspec +++ b/mime-types.gemspec @@ -6,10 +6,10 @@ Gem::Specification.new do |s| s.version = "3.6.2".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= - s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/master/History.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= + s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/main/CHANGELOG.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Austin Ziegler".freeze] - s.date = "2025-03-24" + s.date = "2025-04-04" s.description = "The mime-types library provides a library and registry for information about\nMIME content type definitions. It can be used to determine defined filename\nextensions for MIME types, or to use filename extensions to look up the likely\nMIME type definitions.\n\nVersion 3.0 is a major release that requires Ruby 2.0 compatibility and removes\ndeprecated functions. The columnar registry format introduced in 2.6 has been\nmade the primary format; the registry data has been extracted from this library\nand put into {mime-types-data}[https://github.com/mime-types/mime-types-data].\nAdditionally, mime-types is now licensed exclusively under the MIT licence and\nthere is a code of conduct in effect. There are a number of other smaller\nchanges described in the History file.".freeze s.email = ["halostatue@gmail.com".freeze] s.extra_rdoc_files = ["CHANGELOG.md".freeze, "CODE_OF_CONDUCT.md".freeze, "CONTRIBUTING.md".freeze, "CONTRIBUTORS.md".freeze, "LICENCE.md".freeze, "Manifest.txt".freeze, "README.md".freeze, "SECURITY.md".freeze] @@ -18,20 +18,21 @@ Gem::Specification.new do |s| s.licenses = ["MIT".freeze] s.rdoc_options = ["--main".freeze, "README.md".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) - s.rubygems_version = "3.6.2".freeze + s.rubygems_version = "3.6.6".freeze s.summary = "The mime-types library provides a library and registry for information about MIME content type definitions".freeze s.specification_version = 4 s.add_runtime_dependency(%q.freeze, ["~> 3.2015".freeze]) s.add_runtime_dependency(%q.freeze, [">= 0".freeze]) - s.add_development_dependency(%q.freeze, ["~> 5.25".freeze]) s.add_development_dependency(%q.freeze, ["~> 4.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 2.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 1.0".freeze]) + s.add_development_dependency(%q.freeze, ["~> 5.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 1.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 1.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 1.4".freeze]) - s.add_development_dependency(%q.freeze, [">= 10.0".freeze, "< 14.0".freeze]) + s.add_development_dependency(%q.freeze, [">= 10.0".freeze, "< 14".freeze]) + s.add_development_dependency(%q.freeze, [">= 0.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 1.0".freeze]) end diff --git a/test/minitest_helper.rb b/test/minitest_helper.rb index fc2ddd0..40662c8 100644 --- a/test/minitest_helper.rb +++ b/test/minitest_helper.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +$debug = false + gem "minitest" require "minitest/focus" require "minitest/hooks" diff --git a/test/test_mime_type.rb b/test/test_mime_type.rb index 0a0666a..1190ced 100644 --- a/test/test_mime_type.rb +++ b/test/test_mime_type.rb @@ -322,7 +322,13 @@ def mime_type(content_type) describe "#priority_compare" do def priority(type) - "#{type} (#{("%08b" % type.__sort_priority).chars.join(" ")})" + priority = "OpRceXtN" + .chars + .zip(("%08b" % type.__sort_priority).chars) + .map { |e| e.join(":") } + .join(" ") + + "#{type} (#{priority} / #{type.__sort_priority})" end def assert_priority_less(left, right) @@ -340,7 +346,7 @@ def assert_priority_more(left, right) def assert_priority(left, middle, right) assert_priority_less left, right assert_priority_same left, middle - assert_priority_more right, left + assert_priority_more right, middle end let(:text_1) { mime_type("content-type" => "text/1") } @@ -361,8 +367,8 @@ def assert_priority(left, middle, right) end it "sorts provisional types higher than non-provisional types" do - text_1.provisional = text_1p.provisional = true - text_1b = mime_type(text_1) { |t| t.provisional = false } + text_1.provisional = text_1p.provisional = false + text_1b = mime_type(text_1) { |t| t.provisional = true } assert_priority text_1, text_1p, text_1b end @@ -381,18 +387,6 @@ def assert_priority(left, middle, right) assert_priority text_1, text_1p, text_1b end - it "sorts (5) based on the use-instead value" do - text_1.obsolete = text_1p.obsolete = true - text_1.use_instead = text_1p.use_instead = "abc/xyz" - text_1b = mime_type(text_1) { |t| t.use_instead = nil } - - assert_priority text_1, text_1p, text_1b - - text_1b.use_instead = "abc/zzz" - - assert_priority text_1, text_1p, text_1b - end - it "sorts based on extensions (more extensions sort lower)" do text_1.extensions = ["foo", "bar"] text_2.extensions = ["foo"] From 80f31bbdb56f798f0943c27539dd2d3872f4da4d Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Thu, 3 Apr 2025 22:35:43 -0400 Subject: [PATCH 08/15] chore: Prepare for pre1 release --- CHANGELOG.md | 7 +------ Rakefile | 7 ++++++- lib/mime/types/version.rb | 2 +- mime-types.gemspec | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2601bc7..2aaa026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,6 @@ # Changelog -## 4.0.0 / YYYY-MM-DD - -- The minimum supported Ruby version for mime-types 4 is Ruby 3.2. All backport - support for older versions of Ruby have been removed. - -## 3.7.0 / YYYY-MM-DD +## 3.7.0.pre1 / YYYY-MM-DD - Deprecated `MIME::Type#priority_compare`. In a future release, this will be will be renamed to `MIME::Type#<=>`. This method is used in tight loops, so diff --git a/Rakefile b/Rakefile index 39d1d87..66c5240 100644 --- a/Rakefile +++ b/Rakefile @@ -26,7 +26,7 @@ spec = Hoe.spec "mime-types" do val.merge!({"rubygems_mfa_required" => "true"}) } - extra_deps << ["mime-types-data", "~> 3.2015"] + extra_deps << ["mime-types-data", "~> 3.2025", ">= 3.2025.0506.pre1"] extra_deps << ["logger", ">= 0"] extra_dev_deps << ["hoe", "~> 4.0"] @@ -178,6 +178,11 @@ namespace :convert do task docs: "convert:docs:run" end +task :version do + require "mime/types/version" + puts MIME::Types::VERSION +end + namespace :deps do task :top, [:number] => "benchmark:support" do |_, args| require "deps" diff --git a/lib/mime/types/version.rb b/lib/mime/types/version.rb index 876a36e..ab10d6c 100644 --- a/lib/mime/types/version.rb +++ b/lib/mime/types/version.rb @@ -4,7 +4,7 @@ module MIME class Types # The released version of the mime-types library. - VERSION = "3.6.2" + VERSION = "3.7.0.pre1" end class Type diff --git a/mime-types.gemspec b/mime-types.gemspec index 1a29983..fdd6a17 100644 --- a/mime-types.gemspec +++ b/mime-types.gemspec @@ -1,9 +1,9 @@ # -*- encoding: utf-8 -*- -# stub: mime-types 3.6.2 ruby lib +# stub: mime-types 3.7.0.pre1 ruby lib Gem::Specification.new do |s| s.name = "mime-types".freeze - s.version = "3.6.2".freeze + s.version = "3.7.0.pre1".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/main/CHANGELOG.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.specification_version = 4 - s.add_runtime_dependency(%q.freeze, ["~> 3.2015".freeze]) + s.add_runtime_dependency(%q.freeze, ["~> 3.2025".freeze, ">= 3.2025.0506.pre1".freeze]) s.add_runtime_dependency(%q.freeze, [">= 0".freeze]) s.add_development_dependency(%q.freeze, ["~> 4.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 2.0".freeze]) From ff380148aa6ef510195ccc0bcc80904a7f3cd543 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Thu, 3 Apr 2025 22:41:25 -0400 Subject: [PATCH 09/15] remove test debug var --- test/minitest_helper.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/minitest_helper.rb b/test/minitest_helper.rb index 40662c8..fc2ddd0 100644 --- a/test/minitest_helper.rb +++ b/test/minitest_helper.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -$debug = false - gem "minitest" require "minitest/focus" require "minitest/hooks" From 96c9989c756410fcf00853179fd3fb7e048db008 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Sat, 5 Apr 2025 16:30:47 -0400 Subject: [PATCH 10/15] WIP-unstable-sort --- mime-types.gemspec | 2 +- test/test_mime_types_class.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mime-types.gemspec b/mime-types.gemspec index fdd6a17..f3c7198 100644 --- a/mime-types.gemspec +++ b/mime-types.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/main/CHANGELOG.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Austin Ziegler".freeze] - s.date = "2025-04-04" + s.date = "2025-04-05" s.description = "The mime-types library provides a library and registry for information about\nMIME content type definitions. It can be used to determine defined filename\nextensions for MIME types, or to use filename extensions to look up the likely\nMIME type definitions.\n\nVersion 3.0 is a major release that requires Ruby 2.0 compatibility and removes\ndeprecated functions. The columnar registry format introduced in 2.6 has been\nmade the primary format; the registry data has been extracted from this library\nand put into {mime-types-data}[https://github.com/mime-types/mime-types-data].\nAdditionally, mime-types is now licensed exclusively under the MIT licence and\nthere is a code of conduct in effect. There are a number of other smaller\nchanges described in the History file.".freeze s.email = ["halostatue@gmail.com".freeze] s.extra_rdoc_files = ["CHANGELOG.md".freeze, "CODE_OF_CONDUCT.md".freeze, "CONTRIBUTING.md".freeze, "CONTRIBUTORS.md".freeze, "LICENCE.md".freeze, "Manifest.txt".freeze, "README.md".freeze, "SECURITY.md".freeze] diff --git a/test/test_mime_types_class.rb b/test/test_mime_types_class.rb index 66122f3..2d1fd74 100644 --- a/test/test_mime_types_class.rb +++ b/test/test_mime_types_class.rb @@ -86,8 +86,13 @@ def setup assert_equal %w[image/jpeg], MIME::Types.of(["foo.jpeg", "bar.jpeg"]) end + focus it "finds multiple extensions ordered by the filename list" do - assert_equal %w[text/plain image/jpeg], MIME::Types.type_for(%w[foo.txt foo.jpeg]) + result = MIME::Types.type_for(%w[foo.txt foo.jpeg]) + p result.map { |e| [e.simplified, e.__sort_priority, e.extensions, e.extension_priority(*e.extensions)] } + + # assert_equal %w[text/plain image/jpeg], MIME::Types.type_for(%w[foo.txt foo.jpeg]) + assert_equal %w[text/plain image/jpeg], result end it "does not find unknown extensions" do From ad0f9e01034e799baff165ce1c37d279e0869f42 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Sun, 6 Apr 2025 00:05:11 -0400 Subject: [PATCH 11/15] Fix sort; drop extension priorities --- lib/mime/type.rb | 84 ++++++++++++++--------------------- lib/mime/types.rb | 16 +++---- mime-types.gemspec | 2 +- test/test_mime_types.rb | 2 +- test/test_mime_types_class.rb | 2 - 5 files changed, 42 insertions(+), 64 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index a95a305..55c4eab 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -134,7 +134,6 @@ def initialize(content_type) # :yields: self @friendly = {} @obsolete = @registered = @provisional = false @preferred_extension = @docs = @use_instead = @__sort_priority = nil - __extension_priorities self.extensions = [] @@ -196,12 +195,11 @@ def <=>(other) simplified <=> other end - # Compares the +other+ MIME::Type using the simplified representation, then - # a pre-computed sort priority value. Used by MIME::Types#[] to sort types. + # Compares the +other+ MIME::Type using a pre-computed sort priority value, + # then the simplified representation for an alphabetical sort. # - # While this method is public, its direct use is strongly discouraged by - # consumers of mime-types. For the next major version of MIME::Types, this - # method will become #<=> and #priority_compare will be removed. + # For the next major version of MIME::Types, this method will become #<=> and + # #priority_compare will be removed. def priority_compare(other) if (cmp = __sort_priority <=> other.__sort_priority) == 0 simplified <=> other.simplified @@ -210,6 +208,33 @@ def priority_compare(other) end end + # Uses a modified pre-computed sort priority value based on whether one of the provided + # extensions is the preferred extension for a type. + # + # This is an internal function. If an extension provided is a preferred extension either + # for this instance or the compared instance, the corresponding extension has its top + # _extension_ bit cleared from its sort priority. That means that a type with between + # 0 and 8 extensions will be treated as if it had 9 extensions. + def __extension_priority_compare(other, exts) # :nodoc: + tsp = __sort_priority + + if exts.include?(preferred_extension) && tsp & 0b1000 != 0 + tsp = tsp & 0b11110111 | 0b0111 + end + + osp = other.__sort_priority + + if exts.include?(other.preferred_extension) && osp & 0b1000 != 0 + osp = osp & 0b11110111 | 0b0111 + end + + if (cmp = tsp <=> osp) == 0 + simplified <=> other.simplified + else + cmp + end + end + # Returns +true+ if the +other+ object is a MIME::Type and the content types # match. def eql?(other) @@ -330,38 +355,10 @@ def preferred_extension ## def preferred_extension=(value) # :nodoc: - if value - add_extensions(value) - set_preferred_extension_priority(value) - elsif instance_variable_defined?(:@preferred_extension) - clear_extension_priority(@preferred_extension) - end + add_extensions(value) if value @preferred_extension = value end - ## - # Optional extension priorities for this MIME type. This is a map of - # extensions to relative priority values (+-20..20+) similar to +nice(1)+. - # Unless otherwise specified in the data, an explicitly set - # +preferred_extension+ is automatically given a relative priority of +-10+. - # - # :attr_reader: extension_priorities - attr_accessor :extension_priorities - - ## - # Returns the priority for the provided extension or extensions. If a priority - # is not set, the default priority is +0+. The range for priorities is - # +-20..20+, inclusive. - # - # Obsolete MIME types have a +3 penalty applied to their - # extension priority and unregistered MIME types have a +2 - # penalty to their extension priority, meaning that the highest priority an - # obsolete, unregistered MIME type can have is +-15+. The lowest priority is - # always +20. - def extension_priority(*exts) - exts.map { |ext| get_extension_priority(ext) }.min - end - ## # The encoding (+7bit+, +8bit+, quoted-printable, or +base64+) # required to transport the data of this content type safely across a @@ -583,7 +580,6 @@ def encode_with(coder) coder["provisional"] = provisional? if provisional? coder["signature"] = signature? if signature? coder["sort-priority"] = __sort_priority || 0b11111111 - coder["extension-priorities"] = __extension_priorities unless __extension_priorities.empty? coder end @@ -604,7 +600,6 @@ def init_with(coder) self.signature = coder["signature"] self.xrefs = coder["xrefs"] || {} self.use_instead = coder["use-instead"] - self.extension_priorities = coder["extension-priorities"] friendly(coder["friendly"] || {}) @@ -662,22 +657,11 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/") end end - def __extension_priorities # :nodoc: - @extension_priorities ||= {} - end - private - def clear_extension_priority(ext) - __extension_priorities.delete(ext) if ext - end - def get_extension_priority(ext) - [[-20, (__extension_priorities[ext] || 0) + __priority_penalty].max, 20].min - end - - def set_preferred_extension_priority(ext) - __extension_priorities[ext] = -10 unless __extension_priorities.has_key?(ext) + __priority_penalty + # [[-20, (__extension_priorities[ext] || 0) + __priority_penalty].max, 20].min end def clear_sort_priority diff --git a/lib/mime/types.rb b/lib/mime/types.rb index 7082376..4f40a83 100644 --- a/lib/mime/types.rb +++ b/lib/mime/types.rb @@ -148,17 +148,13 @@ def [](type_id, complete: false, registered: false) # puts MIME::Types.type_for(%w(citydesk.xml citydesk.gif)) # => [application/xml, image/gif, text/xml] def type_for(filename) - Array(filename).flat_map { |fn| - @extension_index[fn.chomp.downcase[/\.?([^.]*?)\z/m, 1]] - }.compact.inject(Set.new, :+).sort { |a, b| - by_ext = a.extension_priority(*a.extensions) <=> b.extension_priority(*b.extensions) + wanted = Array(filename).map { |fn| fn.chomp.downcase[/\.?([^.]*?)\z/m, 1] } - if by_ext.zero? - a.priority_compare(b) - else - by_ext - end - } + wanted + .flat_map { |ext| @extension_index[ext] } + .compact + .reduce(Set.new, :+) + .sort { |a, b| a.__extension_priority_compare(b, wanted) } end alias_method :of, :type_for diff --git a/mime-types.gemspec b/mime-types.gemspec index f3c7198..a1ac470 100644 --- a/mime-types.gemspec +++ b/mime-types.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/main/CHANGELOG.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Austin Ziegler".freeze] - s.date = "2025-04-05" + s.date = "2025-04-06" s.description = "The mime-types library provides a library and registry for information about\nMIME content type definitions. It can be used to determine defined filename\nextensions for MIME types, or to use filename extensions to look up the likely\nMIME type definitions.\n\nVersion 3.0 is a major release that requires Ruby 2.0 compatibility and removes\ndeprecated functions. The columnar registry format introduced in 2.6 has been\nmade the primary format; the registry data has been extracted from this library\nand put into {mime-types-data}[https://github.com/mime-types/mime-types-data].\nAdditionally, mime-types is now licensed exclusively under the MIT licence and\nthere is a code of conduct in effect. There are a number of other smaller\nchanges described in the History file.".freeze s.email = ["halostatue@gmail.com".freeze] s.extra_rdoc_files = ["CHANGELOG.md".freeze, "CODE_OF_CONDUCT.md".freeze, "CONTRIBUTING.md".freeze, "CONTRIBUTORS.md".freeze, "LICENCE.md".freeze, "Manifest.txt".freeze, "README.md".freeze, "SECURITY.md".freeze] diff --git a/test/test_mime_types.rb b/test/test_mime_types.rb index 574be8a..922a12c 100644 --- a/test/test_mime_types.rb +++ b/test/test_mime_types.rb @@ -139,7 +139,7 @@ def mime_types end it "finds multiple extensions" do - assert_equal %w[image/jpeg text/plain], + assert_equal %w[text/plain image/jpeg], mime_types.type_for(%w[foo.txt foo.jpeg]) end diff --git a/test/test_mime_types_class.rb b/test/test_mime_types_class.rb index 2d1fd74..a99296a 100644 --- a/test/test_mime_types_class.rb +++ b/test/test_mime_types_class.rb @@ -86,10 +86,8 @@ def setup assert_equal %w[image/jpeg], MIME::Types.of(["foo.jpeg", "bar.jpeg"]) end - focus it "finds multiple extensions ordered by the filename list" do result = MIME::Types.type_for(%w[foo.txt foo.jpeg]) - p result.map { |e| [e.simplified, e.__sort_priority, e.extensions, e.extension_priority(*e.extensions)] } # assert_equal %w[text/plain image/jpeg], MIME::Types.type_for(%w[foo.txt foo.jpeg]) assert_equal %w[text/plain image/jpeg], result From 4a9b91d19b416e8dfa2ee80ac42e78de693cff20 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Sun, 6 Apr 2025 00:09:06 -0400 Subject: [PATCH 12/15] Remove priority penalty detritus --- lib/mime/type.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index 55c4eab..5e46de8 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -659,11 +659,6 @@ def simplify_matchdata(matchdata, remove_x = false, joiner: "/") private - def get_extension_priority(ext) - __priority_penalty - # [[-20, (__extension_priorities[ext] || 0) + __priority_penalty].max, 20].min - end - def clear_sort_priority @__sort_priority = nil end @@ -693,13 +688,6 @@ def update_sort_priority extension_count = [0, 16 - extension_count].max @__sort_priority = obsolete | registered | provisional | complete | extension_count - @__priority_penalty = ((instance_variable_defined?(:@obsolete) && @obsolete) ? 3 : 0) + - ((instance_variable_defined?(:@registered) && @registered) ? 0 : 2) - end - - def __priority_penalty - update_sort_priority if @__priority_penalty.nil? - @__priority_penalty end def content_type=(type_string) From 40173165d2ee56fdd0b3be60114055bab97c3144 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Sun, 6 Apr 2025 22:46:16 -0400 Subject: [PATCH 13/15] Test that sort priorities are stable --- lib/mime/types.rb | 15 +++++++++++---- mime-types.gemspec | 2 +- test/test_mime_types.rb | 13 +++++++++---- test/test_mime_types_class.rb | 4 ++++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/lib/mime/types.rb b/lib/mime/types.rb index 4f40a83..78a05d8 100644 --- a/lib/mime/types.rb +++ b/lib/mime/types.rb @@ -130,9 +130,8 @@ def [](type_id, complete: false, registered: false) @type_variants[MIME::Type.simplified(type_id)] end - prune_matches(matches, complete, registered).sort { |a, b| - a.priority_compare(b) - } + # prune_matches(matches, complete, registered).sort { |a, b| a.priority_compare(b) } + prune_matches(matches, complete, registered).sort end # Return the list of MIME::Types which belongs to the file based on its @@ -154,7 +153,9 @@ def type_for(filename) .flat_map { |ext| @extension_index[ext] } .compact .reduce(Set.new, :+) - .sort { |a, b| a.__extension_priority_compare(b, wanted) } + .sort { |a, b| + a.__extension_priority_compare(b, wanted) + } end alias_method :of, :type_for @@ -226,6 +227,12 @@ def match(pattern) k =~ pattern }.values.inject(Set.new, :+) end + + # def stable_sort(list) + # list.lazy.each_with_index.sort { |(a, ai), (b, bi)| + # a.priority_compare(b).nonzero? || ai <=> bi + # }.map(&:first) + # end end require "mime/types/cache" diff --git a/mime-types.gemspec b/mime-types.gemspec index a1ac470..aeb9642 100644 --- a/mime-types.gemspec +++ b/mime-types.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/main/CHANGELOG.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Austin Ziegler".freeze] - s.date = "2025-04-06" + s.date = "2025-04-07" s.description = "The mime-types library provides a library and registry for information about\nMIME content type definitions. It can be used to determine defined filename\nextensions for MIME types, or to use filename extensions to look up the likely\nMIME type definitions.\n\nVersion 3.0 is a major release that requires Ruby 2.0 compatibility and removes\ndeprecated functions. The columnar registry format introduced in 2.6 has been\nmade the primary format; the registry data has been extracted from this library\nand put into {mime-types-data}[https://github.com/mime-types/mime-types-data].\nAdditionally, mime-types is now licensed exclusively under the MIT licence and\nthere is a code of conduct in effect. There are a number of other smaller\nchanges described in the History file.".freeze s.email = ["halostatue@gmail.com".freeze] s.extra_rdoc_files = ["CHANGELOG.md".freeze, "CODE_OF_CONDUCT.md".freeze, "CONTRIBUTING.md".freeze, "CONTRIBUTORS.md".freeze, "LICENCE.md".freeze, "Manifest.txt".freeze, "README.md".freeze, "SECURITY.md".freeze] diff --git a/test/test_mime_types.rb b/test/test_mime_types.rb index 922a12c..c4c5af2 100644 --- a/test/test_mime_types.rb +++ b/test/test_mime_types.rb @@ -12,7 +12,8 @@ def mime_types MIME::Type.new("content-type" => "application/x-wordperfect6.1"), MIME::Type.new("content-type" => "application/x-www-form-urlencoded", "registered" => true), MIME::Type.new("content-type" => "application/x-gzip", "extensions" => %w[gz]), - MIME::Type.new("content-type" => "application/gzip", "extensions" => "gz", "registered" => true) + MIME::Type.new("content-type" => "application/gzip", "extensions" => "gz", "registered" => true), + *MIME::Types.type_for("foo.webm") ) } end @@ -33,8 +34,8 @@ def mime_types end it "is countable with an enumerator" do - assert_equal 6, mime_types.each.count - assert_equal 6, mime_types.lazy.count + assert_equal 8, mime_types.each.count + assert_equal 8, mime_types.lazy.count end end @@ -158,11 +159,15 @@ def mime_types it "handles newline characters correctly" do assert_includes mime_types.type_for("test.pdf\n.txt"), "text/plain" end + + it "returns a stable order for types with equal priority" do + assert_equal %w[text/x-vcalendar text/x-vcard], MIME::Types[/text\/x-vca/] + end end describe "#count" do it "can count the number of types inside" do - assert_equal 6, mime_types.count + assert_equal 8, mime_types.count end end end diff --git a/test/test_mime_types_class.rb b/test/test_mime_types_class.rb index a99296a..05494dd 100644 --- a/test/test_mime_types_class.rb +++ b/test/test_mime_types_class.rb @@ -107,6 +107,10 @@ def setup assert_includes MIME::Types.type_for("test.pdf\n.txt"), "text/plain" assert_includes MIME::Types.type_for("test.txt\n.pdf"), "application/pdf" end + + it "returns a stable order for types with equal priority" do + assert_equal %w[text/x-vcalendar text/x-vcard], MIME::Types[/text\/x-vca/] + end end describe ".count" do From e78bd76079c2af14156fb43e1eb75c449f538470 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Sun, 6 Apr 2025 23:10:54 -0400 Subject: [PATCH 14/15] Stable sort tests added to priority sorting Closes: #151 Resolves: #148 --- CHANGELOG.md | 11 ++++++++++- Rakefile | 2 +- lib/mime/types/version.rb | 2 +- mime-types.gemspec | 6 +++--- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aaa026..9844d59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 3.7.0.pre1 / YYYY-MM-DD +## 3.7.0.pre2 / YYYY-MM-DD - Deprecated `MIME::Type#priority_compare`. In a future release, this will be will be renamed to `MIME::Type#<=>`. This method is used in tight loops, so @@ -19,6 +19,14 @@ none, as explicit support for several special values (which should have caused errors in any case) have been removed. +- When sorting the result of `MIME::Types#type_for`, provided a priority boost + if one of the target extensions is the type's preferred extension. This means + that for the case in [#148][issue-148], when getting the type for `foo.webm`, + the type `video/webm` will be returned before the type `audio/webm`, because + `.webm` is the preferred extension for `video/webm` but not `audio/webm` + (which has a preferred extension of `.weba`). Added tests to ensure MIME types + are retrieved in a stable order (which is alphabetical). + ## 3.6.2 / 2025-03-25 - Updated the reference to the changelog in the README, fixing RubyGems metadata @@ -369,6 +377,7 @@ there are some validation changes and updated code with formatting. [issue-127]: https://github.com/mime-types/ruby-mime-types/issues/127 [issue-134]: https://github.com/mime-types/ruby-mime-types/issues/134 [issue-136]: https://github.com/mime-types/ruby-mime-types/issues/136 +[issue-148]: https://github.com/mime-types/ruby-mime-types/issues/148 [issue-166]: https://github.com/mime-types/ruby-mime-types/issues/166 [issue-177]: https://github.com/mime-types/ruby-mime-types/issues/177 [mime-types-data]: https://github.com/mime-types/mime-types-data diff --git a/Rakefile b/Rakefile index 66c5240..af0cc12 100644 --- a/Rakefile +++ b/Rakefile @@ -26,7 +26,7 @@ spec = Hoe.spec "mime-types" do val.merge!({"rubygems_mfa_required" => "true"}) } - extra_deps << ["mime-types-data", "~> 3.2025", ">= 3.2025.0506.pre1"] + extra_deps << ["mime-types-data", "~> 3.2025", ">= 3.2025.0506.pre2"] extra_deps << ["logger", ">= 0"] extra_dev_deps << ["hoe", "~> 4.0"] diff --git a/lib/mime/types/version.rb b/lib/mime/types/version.rb index ab10d6c..c5f21bd 100644 --- a/lib/mime/types/version.rb +++ b/lib/mime/types/version.rb @@ -4,7 +4,7 @@ module MIME class Types # The released version of the mime-types library. - VERSION = "3.7.0.pre1" + VERSION = "3.7.0.pre2" end class Type diff --git a/mime-types.gemspec b/mime-types.gemspec index aeb9642..cc15dc2 100644 --- a/mime-types.gemspec +++ b/mime-types.gemspec @@ -1,9 +1,9 @@ # -*- encoding: utf-8 -*- -# stub: mime-types 3.7.0.pre1 ruby lib +# stub: mime-types 3.7.0.pre2 ruby lib Gem::Specification.new do |s| s.name = "mime-types".freeze - s.version = "3.7.0.pre1".freeze + s.version = "3.7.0.pre2".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/mime-types/ruby-mime-types/issues", "changelog_uri" => "https://github.com/mime-types/ruby-mime-types/blob/main/CHANGELOG.md", "homepage_uri" => "https://github.com/mime-types/ruby-mime-types/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/mime-types/ruby-mime-types/" } if s.respond_to? :metadata= @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.specification_version = 4 - s.add_runtime_dependency(%q.freeze, ["~> 3.2025".freeze, ">= 3.2025.0506.pre1".freeze]) + s.add_runtime_dependency(%q.freeze, ["~> 3.2025".freeze, ">= 3.2025.0506.pre2".freeze]) s.add_runtime_dependency(%q.freeze, [">= 0".freeze]) s.add_development_dependency(%q.freeze, ["~> 4.0".freeze]) s.add_development_dependency(%q.freeze, ["~> 2.0".freeze]) From 084483b09d57e71de6ca638b41affd26d4b67fd4 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Sun, 6 Apr 2025 23:28:57 -0400 Subject: [PATCH 15/15] Attempt to fix truffleruby issue --- lib/mime/types/loader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mime/types/loader.rb b/lib/mime/types/loader.rb index 79c4f98..7336c25 100644 --- a/lib/mime/types/loader.rb +++ b/lib/mime/types/loader.rb @@ -79,7 +79,7 @@ def load_columnar # # This will load from columnar files (#load_columnar) if columnar: # true is provided in +options+ and there are columnar files in +path+. - def load(options = {columnar: false}) + def load(options = {columnar: true}) if options[:columnar] && !Dir[columnar_path].empty? load_columnar else