I have been using both Template Inheritance and Russian Doll Caching (using the cache_digests gem) independently of one another in select portions of a fairly complex Rails application, with much success.
I am having difficulty using the two technologies together, in a sane way, which makes me suspect that I may be doing something wrong...
For an extremely simple example, consider an application consisting of two controllers, ThingOnes and ThingTwos. This application has one layout (layouts/application.html.erb
) which simply renders a header file with: <%= render 'header' %>
.
By default, Rails will look in a bunch of locations for this partial, including the view directory for the layout ( views/application/_header.html.erb
) as well as any specific to the current controller ( like views/thing_ones/_header.html.erb
or views/thing_twos/_header.html.erb
). This means, for caching purposes, I basically have a template dependency list ( not counting engines, or anything else ) like this:
[
"application/header",
"thing_ones/header",
"thing_twos/header"
]
Now, we wrap that render call with caching, like so:
<% cache 'header' do %>
<%= render 'header' %>
<% end %>
Unfortunately, running rake cache_digests:nested_dependencies TEMPLATE=layouts/application
results in the following dependency list.
[
"layouts/header"
]
It doesn't seem to care about template inheritance at all. Modifying the files not included in the list has the expected effect of modifying files not included in the list - the cache is not expired properly and the stale header is displayed.
This can be easily remidied by specifying the relevant template paths, like so:
<% cache 'header' do %>
<%# Template Dependency: application/header %>
<%# Template Dependency: thing_ones/header %>
<%# Template Dependency: thing_twos/header %>
<%= render 'header' %>
<% end %>
This seems like a very poor solution indeed, as it doesn't grow well, and requires a lot of frivolous decoration to cache calls in order to retain the existing template inheritance behavior.
Similarly, one can more explicitly specify the header location, like so:
<% cache 'header' do %>
<%= render 'application/header' %>
<% end %>
This also fails to retain the existing template inheritance behavior, rendering it unsuitable for our needs.
A final option lies in moving the cache
call into the header partials themselves. This is not only inefficient, as it leaves the render
call out of the cache. It is also more WET (write everything twice) than DRY, which is a big turn off.
So, to get to my actual question(s)... Am I doing this correctly? This seems like a fairly major shortcoming that would be affecting a wide variety of implementations, but I can't really find a lot of discussion related to this specific issue, so I wonder if others are doing it in a way that works better. Is there a better way to do this, or at least automatically specify the entire template dependency heirarchy for partial renders?
The problem here is actually what you suggest yourself in the end: you have to move the
cache
method into your rendered templates.This may seem like more code, but this is neccesarry for cache digest to work properly. Furthermore, if your partials (
_header.html.erb
in application, thing_ones and thing_twos) are different, should the cache key be different as well. This means that you should end up with something like this:Without different cache keys would these caches override each other meaning that if eg.
application/_header.html.erb
was cached first, then that would be the one that is rendered oneThingOnes
andThingTwos
pages.