Let's say we've got some view in a Ruby on Rails app that takes some SampleObject instance variable and renders out partials based on external_components, which is a method we define on SampleObject that performs some long-running task to return the data for the partials.

We might write our view like this:

<% @sample_object.external_components.each do |component| %>
    <%= render component %>
<% end %>

Fragment caching

Our first intuition might be to use fragment caching to speed up the partials a little. You can use fragment caching on the partial like so:

<% @sample_object.external_components.each do |component| %>
  <% cache component do %>
    <%= render component %>
  <% end %>
<% end %>

This will yield some benefits because it caches the render output of the component partial.

But if @sample_object.external_components takes some non-trivial amount of time to run its operations, we haven't quite solved all our caching problems. The call to that method happens outside of the cache. Every time we render this view, we'll invoke external_components and slow things down, even with the cached partials.

Low-level caching

The SampleObject class defines the external_components method like this:

class SampleObject < ApplicationRecord
    def external_components 
        # Some long running task that returns `components`
    end
end

This is a great candidate for low-level caching.

We can rewrite external_components like this:

class SampleObject < ApplicationRecord
   def external_components 
     Rails.cache.fetch("#{cache_key_with_version}/components", expires_in: 12.hours) do
     # Some long running task that returns `components`
    end
  end
end

Now any call to external_components will be cached for twelve hours, which is great because we can reuse it elsewhere across our application and also rest assured that the result will be cached as desired.

Caching in Development

When I was putting this together in my own application at work, I was having difficulty benchmarking the difference between my cached and non-cached responses.

I had forgotten that the Rails cache is off by default in development. So my caching wasn't doing anything.

If you want to see the difference in a development environment, you can turn on the Rails dev cache like so:

rails dev:cache