accept.rb
148 lines
| 3.8 KiB
| text/x-ruby
|
RubyLexer
|
r2376 | module OpenID | ||
module Yadis | ||||
# Generate an accept header value | ||||
# | ||||
# [str or (str, float)] -> str | ||||
def self.generate_accept_header(*elements) | ||||
parts = [] | ||||
elements.each { |element| | ||||
if element.is_a?(String) | ||||
qs = "1.0" | ||||
mtype = element | ||||
else | ||||
mtype, q = element | ||||
q = q.to_f | ||||
if q > 1 or q <= 0 | ||||
raise ArgumentError.new("Invalid preference factor: #{q}") | ||||
end | ||||
qs = sprintf("%0.1f", q) | ||||
end | ||||
parts << [qs, mtype] | ||||
} | ||||
parts.sort! | ||||
chunks = [] | ||||
parts.each { |q, mtype| | ||||
if q == '1.0' | ||||
chunks << mtype | ||||
else | ||||
chunks << sprintf("%s; q=%s", mtype, q) | ||||
end | ||||
} | ||||
return chunks.join(', ') | ||||
end | ||||
def self.parse_accept_header(value) | ||||
# Parse an accept header, ignoring any accept-extensions | ||||
# | ||||
# returns a list of tuples containing main MIME type, MIME | ||||
# subtype, and quality markdown. | ||||
# | ||||
# str -> [(str, str, float)] | ||||
chunks = value.split(',', -1).collect { |v| v.strip } | ||||
accept = [] | ||||
chunks.each { |chunk| | ||||
parts = chunk.split(";", -1).collect { |s| s.strip } | ||||
mtype = parts.shift | ||||
if mtype.index('/').nil? | ||||
# This is not a MIME type, so ignore the bad data | ||||
next | ||||
end | ||||
main, sub = mtype.split('/', 2) | ||||
q = nil | ||||
parts.each { |ext| | ||||
if !ext.index('=').nil? | ||||
k, v = ext.split('=', 2) | ||||
if k == 'q' | ||||
q = v.to_f | ||||
end | ||||
end | ||||
} | ||||
q = 1.0 if q.nil? | ||||
accept << [q, main, sub] | ||||
} | ||||
accept.sort! | ||||
accept.reverse! | ||||
return accept.collect { |q, main, sub| [main, sub, q] } | ||||
end | ||||
def self.match_types(accept_types, have_types) | ||||
# Given the result of parsing an Accept: header, and the | ||||
# available MIME types, return the acceptable types with their | ||||
# quality markdowns. | ||||
# | ||||
# For example: | ||||
# | ||||
# >>> acceptable = parse_accept_header('text/html, text/plain; q=0.5') | ||||
# >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg']) | ||||
# [('text/html', 1.0), ('text/plain', 0.5)] | ||||
# | ||||
# Type signature: ([(str, str, float)], [str]) -> [(str, float)] | ||||
if accept_types.nil? or accept_types == [] | ||||
# Accept all of them | ||||
default = 1 | ||||
else | ||||
default = 0 | ||||
end | ||||
match_main = {} | ||||
match_sub = {} | ||||
accept_types.each { |main, sub, q| | ||||
if main == '*' | ||||
default = [default, q].max | ||||
next | ||||
elsif sub == '*' | ||||
match_main[main] = [match_main.fetch(main, 0), q].max | ||||
else | ||||
match_sub[[main, sub]] = [match_sub.fetch([main, sub], 0), q].max | ||||
end | ||||
} | ||||
accepted_list = [] | ||||
order_maintainer = 0 | ||||
have_types.each { |mtype| | ||||
main, sub = mtype.split('/', 2) | ||||
if match_sub.member?([main, sub]) | ||||
q = match_sub[[main, sub]] | ||||
else | ||||
q = match_main.fetch(main, default) | ||||
end | ||||
if q != 0 | ||||
accepted_list << [1 - q, order_maintainer, q, mtype] | ||||
order_maintainer += 1 | ||||
end | ||||
} | ||||
accepted_list.sort! | ||||
return accepted_list.collect { |_, _, q, mtype| [mtype, q] } | ||||
end | ||||
def self.get_acceptable(accept_header, have_types) | ||||
# Parse the accept header and return a list of available types | ||||
# in preferred order. If a type is unacceptable, it will not be | ||||
# in the resulting list. | ||||
# | ||||
# This is a convenience wrapper around matchTypes and | ||||
# parse_accept_header | ||||
# | ||||
# (str, [str]) -> [str] | ||||
accepted = self.parse_accept_header(accept_header) | ||||
preferred = self.match_types(accepted, have_types) | ||||
return preferred.collect { |mtype, _| mtype } | ||||
end | ||||
end | ||||
end | ||||