Action Caching with Mime::Responder logic

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

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

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,

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:

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.