The following is a guest post by Li Ouyang and originally appeared on her blog. Li is currently a student a The Flatiron School. You can learn more about her here, or follow her on twitter here.
In order to speed up performance, sometimes you’ll want to cache a page. Once the data changes, the cached page should expire, in order to show the new information. Rails has some features built in to help us with this. One of which are Sweepers. The Rails API claims this:
Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change. They do this by being half-observers, half-filters and implementing callbacks for both roles.
However, much to my dismay, if an object was edited or created outside of the controller, the sweeper doesn’t seem to pick it up, despite the Rails documentation. This means cron jobs or objects created/edited in console doesn’t trigger the sweeper, and you’re left with stale cached pages. After digging through many stackoverflow threads, like this one, it seems like I wasn’t the only one to struggle with this.
However, I did find a workaround: fragment caching! Thanks to Ryan Bates for another great railscast.
Modify the configuration file and set perform_caching = true
.
To cache a fragment of any view, simply wrap the code with <% cache 'something' do %> <% end %>
. The fragment will be cached with whatever you want to call it. Let’s say you want automatic expiration. You can do this by replacing 'something'
with something like this.
Automatic expiration works here because of key-based expiration. Check it out by going to console and typing in song.cache_key
.
The cache_key is updated through the updated_at
timestamp. So the line <% cache song do %>
checks to see if there is a fragment that is the same as song.cache_key. If not, it generates a new one, and expires the old one. More information about cache keys from DHH himself here.
One thing to be careful of is the naming of fragments. If you’re caching on multiple templates, chances are you might accidently create a fragment with the same name. To get around that, prepend a string by using an array.
In our case, song belongs to a mixtape. So in the song.rb file I’ve added a touch: true
to the association, to ensure the associated model, mixtape, gets updated everytime the song gets updated.
While you’re testing caching, you may notice your changes not going through. Be sure to Rails.cache.clear
everytime you change the code regarding caching. Interesting? Read more about advanced caching here.