Skip to content
A Ruby gem for caching HTTP responses.
Ruby Shell
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Gem Version Build Status Coverage Status

A gem for caching HTTP responses.

The gem was built with an idea to implement a class which will create options for ActionController::ConditionalGet#stale? method. It allows to cache any kind of object, not only record or collection (unlike of #stale?).


Add this line to your application's Gemfile:

gem 'stale_options'

And then execute:


Or install it yourself as:

gem install stale_options


There are three main classes, each class is designed to create options for the corresponding object class:

  • StaleOptions::ArrayOptions - For caching Arrays.
  • StaleOptions::RelationOptions - For caching relations ActiveRecord::Relation.
  • StaleOptions::ObjectOptions - For caching any other objects.

There is also base class StaleOptions::AbstractOptions. Constructor of these classes looks like:

def initialize(record, options = {})
  • record - Basically any Ruby object. Arrays and relations are "specials".
  • options - Hash. Caching options (see detailed description in corresponding section).

Here is the very basic example of usage:

[1] pry(main)> options =
[2] pry(main)> options.to_h
=> {:etag=>"39f08c583b023142dd64b0922dfaefd4", :last_modified=>2018-07-04 18:05:22 UTC}

In order to help create different classes of StaleOptions for different objects there is method StaleOptions.create, so above example can be rewritten like:

[3] pry(main)> StaleOptions.create(Item.all)
=> {:etag=>"39f08c583b023142dd64b0922dfaefd4", :last_modified=>2018-07-04 18:05:22 UTC}

And this is the way how you'll use it in most cases :)

Caching options

There are two options for caching:

  • :cache_by
    • String or Symbol. A name of method which returns unique identifier of object for caching.
    • For arrays and relations if value is itself, then it will be cached "as it is" (relations will be converted to arrays by calling #to_a), otherwise this method will be called on each element. Hint: To cache arrays of "simple" objects (e.g. String or Numeric) set it to itself.
    • Default: :updated_at.
  • :last_modified
    • String or Symbol. A name of method which returns an instance of ActiveSupport::TimeWithZone, DateTime, Time.
      • If record is a relation, then an attribute name.
      • If record is an Array or Object, then a method name.
    • ActiveSupport::TimeWithZone, DateTime, Time or nil to set :last_modified.
    • Default: :updated_at.


[1] pry(main)> StaleOptions.create(Task.all, last_modified: :done_at)
=> {:etag=>"ce8d2fbc9b815937b59e8815d8a85c21", :last_modified=>2018-06-30 18:25:07 UTC}

[2] pry(main)> StaleOptions.create([1, 2, 3], cache_by: :itself, last_modified: nil)
=> {:etag=>"73250e72da5d8950b6bbb16044353d26", :last_modified=>nil}

[3] pry(main)> StaleOptions.create({ a: 'a', b: 'b', c: 'c' }, cache_by: :itself, last_modified:
=> {:etag=>"fec76eca1192bc7371e44d517b56c93f", :last_modified=>2018-07-08 07:08:48 UTC}

Controller helpers

In your controller:

# To render a template:

class PostsController < ApplicationController
  include StaleOptions::Backend

  def index
    if_stale?(Post.all) do |posts|
      @posts = posts

# Or, to render json:

class PostsController < ApplicationController
  include StaleOptions::Backend

  def index
    if_stale?(Post.all) do |posts|
      render json: posts

Here we're using method #if_stale? (there is also #unless_stale? btw) which was added to controller by including StaleOptions::Backend module. The method accepts two arguments record and options (yeah, just like StaleOptions.create).

Under the hood it calls ActionController::ConditionalGet#stale? with options created by StaleOptions.create:

def if_stale?(record, options = {})
  yield(record) if stale?(StaleOptions.create(record, options))


Bug reports and pull requests are welcome on GitHub at


The gem is available as open source under the terms of the MIT License.

You can’t perform that action at this time.