Question

Since action and page caches and sweepers will be removed from Rails 4.0, I started to use cache_digests in my Rails 3.2 application, since I'm suffering from this whole manual expiration nightmare.

But even after reading some tutorials (How key-based cache expiration works, Cache Digests, #387 Cache Digests, ...) I couldn't find a good way to handle views where there is no parent object which could provide a time stamp or something similar.

For example this works flawlessly, if Document and Todolist use the touch option on the Project association.

# app/views/projects/show.html.erb
<% cache @project do %>
  <%= render @project.documents %>
  <%= render @project.todolists %>
<% end %>

But what about the index action?

# app/views/projects/index.html.erb
<% cache ??? do %>
  <% @projects.each do |project| %>
    ...
  <% end %>
<% end %>

Of course I could just use any arbitrary key like project_index and expire it on any change of a project model, but that would require a sweeper or an observer and getting rid of them including the explicit expiration is one of the major reasons for key based expiration.

What is the Rails 4.0 way to do this?

Was it helpful?

Solution 2

Just do this:

# app/views/projects/index.html.erb
<% cache @projects do %>
  <% @projects.each do |project| %>
    ...
  <% end %>
<% end %>

The projects collection will create the cache key, any changes in the collection in terms of order or objects will create a different key.

It does have some limitations if your views are showing data relating to projects from lower level associations, however this can be dealt with.

OTHER TIPS

# app/views/projects/index.html.erb
<% cache @projects.scoped.maximum(:updated_at) do %>
  <% @projects.each do |project| %>
    ...
  <% end %>
<% end %>

When a project is updated, a new cache page is created....

I didn't give it too much thought, and I never tried it myself, but you might do the following:

class TimestampAsCacheKey
  def initialize(prefix,timestamp)
    @key = prefix + timestamp.to_s
  end
  def cache_key # This is what Rails calls to get a key from object for caching
    @key
  end
end

...
# in your model:
ts = ... # somehow get the most recent timestamp of your projects, like
         # 1) either @projects.to_a.map(&:updated_at).max 
         #    or from `projects` table, like 
         #      Project.order("updated_at DESC").first.updated_at
         #      (you should further optimize the query, of course, but you get the idea)
         #    but probably not, since the whole point is to avoid hitting db
         # 2) or, better yet, from storing it in a global var, special DB table, etc
         #    (just update it there each time you save a project, 
         #    e.g. through after_save / after_commit)
@projects_last_updated_time = TimestampAsCacheKey.new("projects-timestamped-list-",ts)

And then in your view:

<% cache @projects_last_updated_time do %>
  <% @projects.each do |project| %>
    ...
  <% end %>
<% end %>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top