Rails Action Caching with Query Strings and Formats
There are two pretty common use-case problems with the standard Rails action caching:
class ChaptersController < ApplicationController
respond_to :html, :json, :csv
caches_action :show
The first is that query strings are totally ignored when writing the cache paths:
$ wget http://localhost:3000/chapters/1.json
Write fragment views/localhost:3000/chapters/1.json
$ wget http://localhost:3000/chapters/1.json?include_body=true
Read fragment views/localhost:3000/chapters/1.json
The second is that the format of the request is not included in the cache path when using Accept:
header:
$ wget http://localhost:3000/chapters/1 --header 'Accept: text/html'
Write fragment views/localhost:3000/chapters/1
$ wget http://localhost:3000/chapters/1 --header 'Accept: application/json'
Read fragment views/localhost:3000/chapters/1
So what we need is to include both the query string params and the format, even if it was set by an accepts header, in the cache key path. This is what we’ve come up with:
caches_action :show,
cache_path: :updated_request_params_to_include_format_for_cache_key.to_proc
def updated_request_params_to_include_format_for_cache_key
params.merge({ format: request.format.symbol || 'html' })
end
We take the params
hash and we merge in the definitive format of the request from request.format
. Rails then turns this into a string for the cache path. It orders the params too, so ?foo=baz&boo=far
and ?boo=far&foo=baz
get written to the same path.
$ wget http://localhost:3000/chapters/1.json
Write fragment views/localhost:3000/chapters/1.json
$ wget http://localhost:3000/chapters/1 --header 'Accept: application/json'
$ wget http://localhost:3000/chapters/1.json?include_body=true
Write fragment views/localhost:3000/chapters/1.json?include_body=true
$ wget http://localhost:3000/chapters/1?include_body=true --header 'Accept: application/json'
Read fragment views/localhost:3000/chapters/1.json?include_body=true
$ wget http://localhost:3000/chapters/1?include_body=true --header 'Accept: text/html'
Write fragment views/localhost:3000/chapters/1.html?include_body=true
$ wget http://localhost:3000/chapters/1?include_body=true
Read fragment views/localhost:3000/chapters/1.html?include_body=true
Either pop updated_request_params_to_include_format_for_cache_key
in your ApplicationController
or wrap the whole caches_action
method in a new method caches_action_with_params
.