Skip to content

Commit

Permalink
Support extension priorities
Browse files Browse the repository at this point in the history
  • Loading branch information
halostatue committed Feb 17, 2023
1 parent e0bc5e9 commit 8748d86
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 11 deletions.
52 changes: 47 additions & 5 deletions lib/mime/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def to_s
# :stopdoc:
# TODO verify mime-type character restrictions; I am pretty sure that this is
# too wide open.
MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}.freeze
MEDIA_TYPE_RE = %r{([a-zA-Z][-a-zA-Z0-9+_.]*)/([a-zA-Z0-9][-a-zA-Z0-9+_.]*)}.freeze
I18N_RE = /[^[:alnum:]]/.freeze
BINARY_ENCODINGS = %w[base64 8bit].freeze
ASCII_ENCODINGS = %w[7bit quoted-printable].freeze
Expand Down Expand Up @@ -131,6 +131,7 @@ def initialize(content_type) # :yields: self
@friendly = {}
@obsolete = @registered = @provisional = false
@preferred_extension = @docs = @use_instead = @__sort_priority = nil
self.__extension_priorities

self.extensions = []

Expand Down Expand Up @@ -302,7 +303,7 @@ def add_extensions(*extensions)
# exceptions defined, the first extension will be used.
#
# When setting #preferred_extensions, if #extensions does not contain this
# extension, this will be added to #xtensions.
# extension, this will be added to #extensions.
#
# :attr_accessor: preferred_extension

Expand All @@ -313,10 +314,31 @@ def preferred_extension

##
def preferred_extension=(value) # :nodoc:
add_extensions(value) if value
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+, <tt>quoted-printable</tt>, or +base64+)
# required to transport the data of this content type safely across a
Expand Down Expand Up @@ -537,7 +559,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

Expand All @@ -546,6 +569,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"]
Expand All @@ -557,9 +581,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:
Expand Down Expand Up @@ -615,6 +641,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
Expand Down
2 changes: 2 additions & 0 deletions lib/mime/type/columnar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down
14 changes: 11 additions & 3 deletions lib/mime/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,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[/\.?([^.]*?)$/, 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
Expand Down
35 changes: 32 additions & 3 deletions lib/mime/types/_columnar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 == "-"
[]
Expand Down

0 comments on commit 8748d86

Please sign in to comment.