In this example we’re going to have an XML API
on FontsController#index
. A GET
to /fonts.xml
will produce a list of every
Font
in the database, along with all its information (name, thumbnail, a list
of ligatures, price, license, and so on).
This is a long list. Luckily it’s just for the API consumers. The normal HTML people just request /fonts
and this gives them a
paginated view of the lovely fonts on our system.
So to speed it up we do some simple caching. In FontsController
, at the top,
we add this:
caches_page :index, :if => Proc.new {|c|
c.request.format.xml?
}
And magically requests to /fonts.xml
are cached to public/fonts.xml
. Lovely!
Thanks, Rails!
The Problem
So what happens when someone requests /fonts
with an Accept: text/xml
header? You can try it like this:
curl -H 'Accept: text/xml' http://ihearthelvetica.local/fonts
FontsController#index
uses #respond_to
, so it sends back the XML as requested. However,
#caches_page
saves the XML to
public/fonts.html
! Now when a user requests /fonts
from their Web browser,
they’re getting back a mess of XML!
No good.
The Workaround
This is a problem deep in the Rails caching code. As a workaround, try this on for size:
class ApplicationController < ActionController::Base
before_filter :fix_caching_extension_for_xml
private
def fix_caching_extension_for_xml
if request.format.xml?
ActionController::Base.page_cache_extension = '.xml'
end
end
end
This manually sets the extension for XML requests to .xml
, so that it saves it to the right place.
The Fix
The other option is to fix this in Ruby on Rails itself. Download the patch attached to the ticket I’ve opened and apply it to an edge version of Rails. The patch has tests and is more generalized, so if the workaround fails to solve your problem the patch might.
Leave a comment on the Lighthouse ticket if the patch works for you, or if you’ve encountered this problem. Together, we can.