I ran into some issues with the response content type for action- cached pages. After digging through the code and reading up on some of the lighthouse tickets, I'm proposing a reset of the thinking for how cached content is matched up to requests. The biggest problem with the current implementation, in my hastily formed opinion, is that the cached content may or may not be tagged with a content type. This leads to futile attempts to match
a. The requested content type -which is intentionally vague (image/ *, ACCEPT header with q values, IE's infamous */*, etc.). b. The available content type(s) -which can be UNKNOWN in the common case of a cache key without an extension
There are lots of compromises that involve trying to make educated guesses (request.cache_format, AC::Base.page_cache_extension, params[:format], request.path). Here are some observations that jump out when looking at the "surprising" behavior of the current implementation:
1. Rails has a convention for tagging cached content with a Mime::Type- compatible extension. 2. At the moment the cache is created, Rails *knows* the content type of the cache -but ignores this information in favor of the guess made *before* the action is processed. 3. Rails *already* has a strong and well documented mechanism for matching requests with available content type -the Mime::Responder#respond_to.
Observations 1 & 2 lead me to the conclusion that the content type (and thus extension) of the cached content should be determined *after* the action has processed and explicitly tagged the content (usually via #respond_to or #render). It was a pretty simple change that resulted in a noticeable simplification of the actions.rb file, particularly the ActionCachePath class. The whole "infer_extension" mess is gone, replaced by specific information from the user-generated response. This leads to the immediate benefit that a request with an extension-less path still gets a content type:
Observation 3 lead me to graft the existing Mime::Responder#respond_to onto the action cache filter code. It was surprisingly easy. Now responses from cache work follow the principle of least surprise: just like regular responses, the content type is determined by respond_to, which considers the available content types (specified as a parameter to the caches_action method) and the normal cast of characters (:format parameter, path extension, HTTP_ACCEPT header, xhr logic, etc.).
Here is an example that should illustrate the impact of the changes.
Request to: /widgets/23 with a response content type of
BEFORE content_type of first response: application/xml (determined by a #render :xml call in the action, for example) BEFORE cache key: /widgets/23 BEFORE content_type of subsequent response served from cache: variable depending on request and static configuration values. Possibly a surprising HTML.
AFTER content_type of first response: application/xml AFTER cache key: /widgets/23.xml AFTER content_type of subsequent response served from cache: xml
Gist of commit: http://gist.github.com/620577
I'd like some advice on how to proceed from here. I've not submitted a patch to Rails in over a year and I'm not up-to-speed with the current process.