Skip to content

Use Accept header and updates Accept header #14

Merged
merged 8 commits into from Mar 12, 2012
View
43 lib/sinatra/respond_to.rb
@@ -15,7 +15,7 @@ class UnhandledFormat < Sinatra::NotFound; end
class MissingTemplate < Sinatra::NotFound
def code; 404 end
end
-
+
def self.registered(app)
app.helpers RespondTo::Helpers
@@ -39,14 +39,40 @@ def self.registered(app)
unless settings.static? && settings.public_folder? && (request.get? || request.head?) && static_file?(request.path_info)
if request.params.has_key? 'format'
format params['format']
+
+ # Rewrite the accept header with the determined format to allow
+ # downstream middleware to make use the the mime type
+ env['HTTP_ACCEPT'] = ::Sinatra::Base.mime_type(format)
+ request.accept.replace [env['HTTP_ACCEPT']]
else
+ # Consider first Accept type as default, otherwise
+ # fall back to settings.default_content
+ default_content = Rack::Mime::MIME_TYPES.invert[request.accept.first]
+ default_content = default_content ? default_content[1..-1] : settings.default_content
+
+ # Special case, as the specified default_content may use a different symbol than that
+ # found through lookup based on Content-Type
+ default_content = settings.default_content if
+ default_content != settings.default_content &&
+ ::Sinatra::Base.mime_type(default_content) == ::Sinatra::Base.mime_type(settings.default_content)
+
# Sinatra relies on a side-effect from path_info= to
# determine its routes. A direct string change (e.g., sub!)
# would bypass that -- and fail to have the effect we're looking
# for.
- request.path_info = request.path_info.sub %r{\.([^\./]+)$}, ''
-
- format $1 || (request.xhr? && settings.assume_xhr_is_js? ? :js : settings.default_content)
+ ext = $1 if request.path_info.match(%r{\.([^\./]+)$})
+ if ext
+ request.path_info = request.path_info[0..-(ext.length+2)]
+
+ format ext
+
+ # Rewrite the accept header with the determined format to allow
+ # downstream middleware to make use the the mime type
+ env['HTTP_ACCEPT'] = ::Sinatra::Base.mime_type(format)
+ request.accept.replace [env['HTTP_ACCEPT']]
+ else
+ format(request.xhr? && settings.assume_xhr_is_js? ? :js : default_content)
+ end
end
end
end
@@ -192,6 +218,15 @@ def wants.method_missing(type, *args, &handler)
yield wants
+ if wants[format].nil?
+ # Loop through request.accept in prioritized order looking for a Mime Type having a format
+ # that is recognized.
+ alt = nil
+ request.accept.each do |mime_type|
+ break if alt = wants.keys.detect {|k| ::Sinatra::Base.mime_type(k) == mime_type}
+ end
+ format alt if alt
+ end
raise UnhandledFormat if wants[format].nil?
wants[format].call
end
View
54 spec/extension_spec.rb
@@ -48,6 +48,8 @@ def mime_type(sym)
it "should use a format parameter before sniffing out the extension" do
get "/resource?format=xml"
last_response.body.should =~ %r{\s*<root>Some XML</root>\s*}
+ last_response.content_type.should include(mime_type(:xml))
+ last_request.env['HTTP_ACCEPT'].should == mime_type(:xml)
end
it "breaks routes expecting an extension" do
@@ -61,24 +63,31 @@ def mime_type(sym)
get "/resource"
last_response.body.should =~ %r{\s*<html>\s*<body>Hello from HTML</body>\s*</html>\s*}
+ last_response.content_type.should include(mime_type(:html))
end
it "should render for a template using builder" do
get "/resource.xml"
last_response.body.should =~ %r{\s*<root>Some XML</root>\s*}
+ last_response.content_type.should include(mime_type(:xml))
+ last_request.env['HTTP_ACCEPT'].should == mime_type(:xml)
end
it "should render for a template using erb" do
get "/resource.js"
last_response.body.should =~ %r{'Hiya from javascript'}
+ last_response.content_type.should include(mime_type(:js))
+ last_request.env['HTTP_ACCEPT'].should == mime_type(:js)
end
it "should return string literals in block" do
get "/resource.json"
last_response.body.should =~ %r{We got some json}
+ last_response.content_type.should include(mime_type(:json))
+ last_request.env['HTTP_ACCEPT'].should == mime_type(:json)
end
# This will fail if the above is failing
@@ -134,6 +143,51 @@ def mime_type(sym)
end
end
+ describe "accept routing" do
+ it "should use a format parameter before sniffing out the accept header" do
+ get "/resource?format=xml", {}, {'HTTP_ACCEPT' => "text/html"}
+ last_response.body.should =~ %r{\s*<root>Some XML</root>\s*}
+ last_response.content_type.should include(mime_type(:xml))
+ last_request.env['HTTP_ACCEPT'].should == mime_type(:xml)
+ end
+
+ it "should use an extension before sniffing out the accept header" do
+ get "/resource.xml", {}, {'HTTP_ACCEPT' => "text/html"}
+
+ last_response.body.should =~ %r{\s*<root>Some XML</root>\s*}
+ last_response.content_type.should include(mime_type(:xml))
+ last_request.env['HTTP_ACCEPT'].should == mime_type(:xml)
+ end
+
+ it "should render for a template using builder" do
+ get "/resource", {}, {'HTTP_ACCEPT' => "application/xml"}
+
+ last_response.body.should =~ %r{\s*<root>Some XML</root>\s*}
+ last_response.content_type.should include(mime_type(:xml))
+ end
+
+ it "should render for a template using haml" do
+ get "/resource", {}, {'HTTP_ACCEPT' => "text/html"}
+
+ last_response.body.should =~ %r{\s*<html>\s*<body>Hello from HTML</body>\s*</html>\s*}
+ last_response.content_type.should include(mime_type(:html))
+ end
+
+ it "should render for a template using json" do
+ get "/resource", {}, {'HTTP_ACCEPT' => "application/json"}
+
+ last_response.body.should =~ %r{We got some json}
+ last_response.content_type.should include(mime_type(:json))
+ end
+
+ it "should render for a template using erb" do
+ get "/resource", {}, {'HTTP_ACCEPT' => "application/javascript"}
+
+ last_response.body.should =~ %r{'Hiya from javascript'}
+ last_response.content_type.should include(mime_type(:js))
+ end
+ end
+
describe "routes not using respond_to" do
it "should set the default content type when no extension" do
get "/normal-no-respond_to"
Something went wrong with that request. Please try again.