A common pattern in a Rails app is displaying a list of items:

<% @posts.each do |post| %>
  <h2><%= post.title %></h2>
  ...
<% end %>

You can speed up a little by caching the individual posts:

<% @posts.each do |post| %>
  <% cache post do %>
    <h2><%= post.title %></h2>
    ...
  <% end %>
<% end %>

This will cache the HTML in the cache block. When either the HTML or Post model changes, the cache will expire. This is called key-based cache expiration and is pretty sweet.

But with this scheme it’s still required to fetch all posts from the database. What we’d like is a way to also cache the entire structure. To achieve that, we have to add a cache_key to the Post model.

# concerns/cacheable.rb
module Cacheable
  extend ActiveSupport::Concern

  included do
    after_save :update_cache_key
  end

  def update_cache_key
    Redis.current.incr(self.class.redis_key)
  end

  module ClassMethods
    def cache_key
      version = Redis.current.get(redis_key)
      "#{name}/#{version}"
    end

    def redis_key
      "class-cache-key:#{name}"
    end
  end
end

# post.rb
class Post
  include Cacheable
end

Now we can easily cache the entire template.

<% cache Post do %>
  <% @posts.each do |post| %>
    <% cache post do %>
      <h2><%= post.title %></h2>
      ...
    <% end %>
  <% end %>
<% end %>