Rails Action Caching with Query Strings and Formats

By Hugh Evans10 Apr 2013

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' })

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.

Work with us, we’re good peopleGet in touch