I have a home page which has some partials rendered all over the page.And it also has session header as well(login). Partial contains set of books paginated. Now I want to cache this partial as it is getting updated once in a week.
In order to use page and action caching you will need to add actionpack-page_caching and actionpack-action_caching to your Gemfile . By default, caching is only enabled in your production environment. You can play around with caching locally by running rails dev:cache , or by setting config. action_controller.
To use Redis as a Rails cache store, use a dedicated cache instance that's set up as an LRU (Last Recently Used) cache instead of pointing the store at your existing Redis server, to make sure entries are dropped from the store when it reaches its maximum size.
Rails Guides describes partials this way: Partial templates - usually just called "partials" - are another device for breaking the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file.
View caching in Ruby on Rails is taking the HTML that a view generates and storing it for later.
You're looking for fragment caching here, which occurs on the view layer. Fragment caching and expiration of stored contents is surprisingly easy to do. You have a list of books, so let's say your view looks a bit like this:
<ul>
<% @books.each do |book| %>
<li><%= book.name %></li>
<% end %>
</ul>
To enable caching for just this bit, simply wrap it in cache
:
<% cache do %>
<ul>
<% @books.each do |book| %>
<li><%= book.name %></li>
<% end %>
</ul>
<% end %>
Of course, this doesn't name the cache or do anything really special with it... while Rails will auto-select a unique name for this cache fragment, it won't be really helpful. We can do better. Let's use DHH's key-based cache expiration technique and give the cache a name relating to its content.
<% cache ['book-list', *@books] do %>
<ul>
<% @books.each do |book| %>
<li><%= book.name %></li>
<% end %>
</ul>
<% end %>
Passing arguments into cache builds the cache key from the supplied arguments. Strings are passed in directly -- so, here, the cache will always be prefaced with 'book-list'. This is to prevent cache collisions with other places you might be caching the same content, but with a different view. For each member of the @books array, Rails will call cache_key
: for ActiveRecord objects, this yields a string composed of its model, ID, and crucially, the last time the object was updated.
This means that when you update the object, the cache key for this fragment will change. In other words, it's automatically getting expired -- when a book is updated, this cache statement will search for a nonexistent key, conclude it doesn't exist, and populate it with new content. Old, stale content will linger in your cache store until evicted by memory or age constraints (memcached does this automatically).
I use this technique in a number of production applications and it works wonderfully. For more information, check out that 37signals post, and for general caching information in Rails, see the Ruby on Rails caching guide.
"There are only two hard problems in Computer Science: cache invalidation and naming things."
-- Phil Karlton
Caching
The Rails Guide to caching is probably always a good entry point for the built in caching strategies rails has to offer. Anyway here comes my very easy approach to caching
# _partial.html.erb
<% cache(some_key, :expires_in => 1.week) do %>
<%# ... content %>
<% end %>
Now some some_key
can be any string as long as it is unique. But then again lets try to be a bit more clever about it and make the key somehow dependent on the list of books. Say you actually pass in the array of books
some query returned then rails calls cache_key
on each of its entries and eventually constructs a unique key for this collection. So when the collection changes the key changes. Thats because cache_key
is implemented in ActiveRecord::Base
and thus available on all Models. And further more it even uses the timestamps if available.
But then again this will hit the db every time a request is made. The same in code:
# controller_method
@weekly_books = Books.where 'condition'
# _partial.html.erb
<% cache(@weekly_books) do %>
<%# ... content %>
<% end %>
To avoid hitting the db to often you can also cache the query its self by wrapping the call:
# Book.rb
def self.weeklies
Rails.cache.fetch("book_weeklies", :expires_in => 1.day) do
Books.where 'condition'
end
end
# controller_method
@weekly_books = Books.weeklies
# _partial.html.erb
<% cache(@weekly_books) do %>
<%# ... content %>
<% end %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With