Issues using Russian Doll Caching with Template In

2019-04-09 19:09发布

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?

1条回答
趁早两清
2楼-- · 2019-04-09 19:36

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:

# layouts/application.html.erb
<%= render 'header' %>

# application/_header.html.erb 
<% cache 'application_header' do %>
  ...
<% end %>

# thing_ones/_header.html.erb
<% cache 'thing_ones_header' do %>
  ...
<% end %>

# and thing_twos/_header.html.erb
<% cache 'thing_twos_header' do %>
  ...
<% end %>

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 one ThingOnes and ThingTwos pages.

查看更多
登录 后发表回答