diff --git a/lib/mime/types.rb b/lib/mime/types.rb index 0aba5b19..e8a92e32 100644 --- a/lib/mime/types.rb +++ b/lib/mime/types.rb @@ -130,9 +130,7 @@ 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) - } + stable_sort(prune_matches(matches, complete, registered)) end # Return the list of MIME::Types which belongs to the file based on its @@ -148,11 +146,12 @@ 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| - a.priority_compare(b) - } + results = + Array(filename).flat_map { |fn| + @extension_index[fn.chomp.downcase[/\.?([^.]*?)\z/m, 1]] + }.compact.inject(Set.new, :+) + + stable_sort(results) end alias_method :of, :type_for @@ -220,6 +219,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/test/test_mime_types.rb b/test/test_mime_types.rb index 574be8a2..99bde3d1 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[audio/webm video/webm], mime_types.type_for("foo.webm") + 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 c4b8a33c..d438d37f 100644 --- a/test/test_mime_types_class.rb +++ b/test/test_mime_types_class.rb @@ -105,6 +105,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[audio/webm video/webm], MIME::Types.type_for("foo.webm") + end end describe ".count" do