Backstory
So, I had a respond_to block handling PDF and HTML. Without an explicit format, the RESTful route returned HTML. With an explicit PDF format, it returned PDF (using Prince XML and Princely to keep DRY). Straightforward stuff.
Then the client requested that the documents also be downloadable in MS Word format. So, after building a failing Cucumber spec, I added a Doc format block and returned some paired down HTML with inline CSS as an application/msword attachment. This gave the following respond_to block:
def show
respond_to do |format|
format.html { render(:show) }
format.pdf do
render(:pdf => @document.title,
:template => 'documents/show.html.haml',
:stylesheets => ['main', 'print', 'prince'])
end
format.doc do
@format = :doc
response.headers['Content-Disposition'] = "attachment; filename=\"#{@document.title}.doc\""
render(:layout => 'document.doc.haml', :template => 'documents/show.doc.haml')
end
end
end
Tests went green. The Doc format worked on Ubuntu with OpenOffice and on OSX with MS Office 2004 for Mac. It worked with Windows and Word, too…only problem is, it worked “too well”.
After pushing the “download as Doc” update into production, I received a panicked call from the client saying he couldn’t reach the documents…every time he tried to visit a document from the index, it just downloaded a Word document. Say what!? I tried it out in IE7 on WinXP and IE8 on Vista. No problems on my end. Visiting documents from the index worked as expected, resulting in the HTML version, from which I could access the Doc formatted link and download the Word version.
Solution
It turns out that if MS Word is installed, then Internet Explorer prefers Word format over HTML. So the .doc format in the respond_to block is the one that is executed even when no explicit format is given and the .html block precedes the .doc block. That is, given the above #show method, http://example.com/document/1234 —when called by IE on Windows with Word installed—caused a Word document to be downloaded every time.
The first post in this thread explains this situation (it has to do with the HTTP_ACCEPT header sent by IE). I don’t have MS Word or MS Office installed on any of my Windows machines. I prefer OpenOffice, and in any case, I only use Windows for testing, or when I need to use an app for which I only have a Windows license, such as Photoshop or Illustrator.
The solution I’ve settled on is to use an outer case statement on the explicit :format param to isolate explicit Microsoft formats from the other, saner formats, and then to execute the respond_to block within each of the cases, giving:
def show
case params[:format]
when 'doc'
respond_to do |format|
format.doc {
response.headers['Content-Disposition'] = "attachment; filename=\"#{@document.title}.doc\""
render(:layout => 'documentdoc.haml', :template => 'documents/show.doc.haml')
}
end
else
respond_to do |format|
format.html { render(:show) }
format.pdf {
render(:pdf => @document.title,
:template => 'documents/show.html.haml',
:stylesheets => ['main', 'print', 'prince'])
}
end
end
end