From 6ecf0297b6bb8a21bb2f1d1af3f23b6616b6b54a Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Fri, 7 Jan 2022 11:48:24 -0500 Subject: [PATCH] WIP --- lib/mime/type.rb | 26 +++++++++-------- lib/mime/type/columnar.rb | 6 ++-- lib/mime/types/_columnar.rb | 7 ++--- test/test_mime_type.rb | 53 ++++++++++++++++++++--------------- test/test_mime_types_class.rb | 5 ++-- 5 files changed, 54 insertions(+), 43 deletions(-) diff --git a/lib/mime/type.rb b/lib/mime/type.rb index 1ba085d..ec4fb67 100644 --- a/lib/mime/type.rb +++ b/lib/mime/type.rb @@ -131,7 +131,7 @@ def initialize(content_type) # :yields: self @friendly = {} @obsolete = @registered = @provisional = false @preferred_extension = @docs = @use_instead = @__sort_priority = nil - self.__extension_priorities + __extension_priorities self.extensions = [] @@ -176,6 +176,8 @@ def like?(other) # # 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 @@ -230,7 +232,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. @@ -317,7 +320,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 @@ -566,7 +569,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 @@ -646,12 +649,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 @@ -686,14 +689,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/_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 3864772..43a91d3 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).split(//).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,21 +347,27 @@ 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 text_1.registered = text_1p.registered = true text_1b = mime_type(text_1) { |t| t.registered = false } @@ -377,12 +382,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 @@ -395,6 +394,14 @@ 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 +509,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..b42d643 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 @@ -87,8 +87,7 @@ def setup end it "finds multiple extensions" do - assert_equal %w[image/jpeg text/plain], - MIME::Types.type_for(%w[foo.txt foo.jpeg]) + assert_equal %w[text/plain image/jpeg], MIME::Types.type_for(%w[foo.txt foo.jpeg]) end it "does not find unknown extensions" do