Skip to content

Commit

Permalink
Make Mime::Type.parse consider q values (if any)
Browse files Browse the repository at this point in the history
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3917 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
jamis committed Mar 18, 2006
1 parent 263479b commit 4e0028f
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 13 deletions.
2 changes: 2 additions & 0 deletions actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*

* Make Mime::Type.parse consider q values (if any) [Jamis Buck]

* XML-formatted requests are typecast according to "type" attributes for :xml_simple [Jamis Buck]

* Added protection against proxy setups treating requests as local even when they're not #3898 [stephen_purcell@yahoo.com]
Expand Down
80 changes: 67 additions & 13 deletions actionpack/lib/action_controller/mime_type.rb
@@ -1,31 +1,85 @@
module Mime
class Type

# A simple helper class used in parsing the accept header
class AcceptItem #:nodoc:
attr_accessor :order, :name, :q

def initialize(order, name, q=nil)
@order = order
@name = name.strip
q ||= 0.0 if @name == "*/*" # default "*/*" to end of list
@q = ((q || 1.0).to_f * 100).to_i
end

def to_s
@name
end

def <=>(item)
result = item.q <=> q
result = order <=> item.order if result == 0
result
end

def ==(item)
name == (item.respond_to?(:name) ? item.name : item)
end
end

class << self
def lookup(string)
LOOKUP[string]
end

def parse(accept_header)
mime_types = accept_header.split(",").collect! do |mime_type|
mime_type.split(";").first.strip
# keep track of creation order to keep the subsequent sort stable
index = 0
list = accept_header.split(/,/).
map! { |i| AcceptItem.new(index += 1, *i.split(/;\s*q=/)) }.sort!

# Take care of the broken text/xml entry by renaming or deleting it

text_xml = list.index("text/xml")
app_xml = list.index("application/xml")

if text_xml && app_xml
# set the q value to the max of the two
list[app_xml].q = [list[text_xml].q, list[app_xml].q].max

# make sure app_xml is ahead of text_xml in the list
if app_xml > text_xml
list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
app_xml, text_xml = text_xml, app_xml
end

# delete text_xml from the list
list.delete_at(text_xml)

elsif text_xml
list[text_xml].name = "application/xml"
end

reorder_xml_types!(mime_types)
mime_types.collect! { |mime_type| Mime::Type.lookup(mime_type) }
end

private
def reorder_xml_types!(mime_types)
mime_types.delete("text/xml") if mime_types.include?("application/xml")
# Look for more specific xml-based types and sort them ahead of app/xml

if index_for_generic_xml = mime_types.index("application/xml")
specific_xml_types = mime_types[index_for_generic_xml..-1].grep(/application\/[a-z]*\+xml/)
if app_xml
idx = app_xml
app_xml_type = list[app_xml]

for specific_xml_type in specific_xml_types.reverse
mime_types.insert(index_for_generic_xml, mime_types.delete(specific_xml_type))
while(idx < list.length)
type = list[idx]
break if type.q < app_xml_type.q
if type.name =~ /\+xml$/
list[app_xml], list[idx] = list[idx], list[app_xml]
app_xml = idx
end
idx += 1
end
end

list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
list
end
end

def initialize(string, symbol = nil, synonyms = [])
Expand Down
24 changes: 24 additions & 0 deletions actionpack/test/controller/mime_type_test.rb
@@ -0,0 +1,24 @@
require File.dirname(__FILE__) + '/../abstract_unit'

class MimeTypeTest < Test::Unit::TestCase
Mime::PNG = Mime::Type.new("image/png")
Mime::PLAIN = Mime::Type.new("text/plain")

def test_parse_single
Mime::LOOKUP.keys.each do |mime_type|
assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type)
end
end

def test_parse_without_q
accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,*/*"
expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::PLAIN, Mime::ALL]
assert_equal expect, Mime::Type.parse(accept)
end

def test_parse_with_q
accept = "text/xml,application/xhtml+xml,text/yaml; q=0.3,application/xml,text/html; q=0.8,image/png,text/plain; q=0.5,*/*; q=0.2"
expect = [Mime::HTML, Mime::XML, Mime::PNG, Mime::PLAIN, Mime::YAML, Mime::ALL]
assert_equal expect, Mime::Type.parse(accept)
end
end

0 comments on commit 4e0028f

Please sign in to comment.