module ActionView::Helpers::CacheHelper

def cache(name = {}, options = {}, &block)

expire the cache.
This will include both records as part of the cache key and updating either of them will

<%= render partial: 'projects/project', collection: @projects, cached: -> project { [ project, current_user ] } %>

you can name all these dependencies as part of a block that returns an array:
If your collection cache depends on multiple sources (try to avoid this to keep things simple),

to read multiple templates at once.
Any collection renders will find those cached templates when attempting

<% end %>
<%# ... %>
<% cache project do %>
# projects/_project.html.erb

For instance if the template the collection renders is cached like:
Works great alongside individual template fragment caching.

Templates in the collection not already cached are written to cache.

from cache at once instead of one call per template.
The cached: true will make Action View's rendering read several templates

<%= render partial: 'projects/project', collection: @projects, cached: true %>

For collections rendered such:

option can be passed.
When rendering a collection of objects that each use the same partial, a :cached

=== Collection Caching

Now all you have to do is change that timestamp when the helper method changes.

<%= some_helper_method(person) %>
<%# Helper Dependency Updated: May 6, 2012 at 6pm %>

must change. One recommendation is to simply be explicit in a comment, like:
It doesn't really matter how you do it, but the MD5 of the template file
you then update that helper, you'll have to bump the cache as well.
If you use a helper method, for example, inside a cached block and

=== External dependencies

You can only declare one template dependency per line.
so it's important that you type it out just so.
The pattern used to match explicit dependencies is /# Template Dependency: (\S+)/,

This way the wildcard for app/views/recordings/events would be recordings/events/* etc.
otherwise added with +prepend_view_path+ or +append_view_path+.
templates, the wildcard path must be absolutely defined from app/views or paths
This marks every template in the directory as a dependency. To find those

<%= render_categorizable_events @person.events %>
<%# Template Dependency: events/* %>

you can use a wildcard to match any template in a directory:
a bunch of explicit dependencies. Instead of writing every template out,
In some cases, like a single table inheritance setup, you might have

<%= render_sortable_todolists @project.todolists %>
<%# Template Dependency: todolists/todolist %>

You'll need to use a special comment format to call those out:

<%= render_sortable_todolists @project.todolists %>

the case when you have template rendering that happens in helpers. Here's an example:
Sometimes you'll have template dependencies that can't be derived at all. This is typically

=== Explicit dependencies

render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
render partial: 'attachments/attachment', collection: group_of_attachments

You will have to rewrite those to the explicit form:

render @project.documents.where(published: true).order('created_at')
render group_of_attachments

Here are a few examples of things that can't be derived:
It's not possible to derive all render calls like that, though.

render(message.topics) translates to render("topics/topic")
render(topics) translates to render("topics/topic")
render(@topic) translates to render("topics/topic")

render "header" translates to render("comments/header")

render('comments/comments')
render 'comments/comments'
render "comments/comments"
render partial: "comments/comment", collection: commentable.comments

Here are some examples of render calls that Cache Digests knows how to decode:
Most template dependencies can be derived from calls to render in the template itself.

==== Implicit dependencies

<% end %>
<%= render project.topics %>
All the topics on this project
<% cache project, skip_digest: true do %>

The digestor can be bypassed by passing skip_digest: true as an option to the cache call:

explicit and implicit dependencies, and include those as part of the digest.
Additionally, the digestor will automatically look through your template file for

still expire the cache.
cache do/end call. So it's possible that changing something outside of that call will
Note that the MD5 is taken of the entire template file, not just what's within the

expire when you change the template file.
contents of the entire template file. This ensures that your caches will automatically
The template digest that's added to the cache key is computed by taking an MD5 of the

==== \Template digest

expire the cache.
This will include both records as part of the cache key and updating either of them will

<% end %>
<%= render project.topics %>
All the topics on this project
<% cache [ project, current_user ] do %>

you can name all these dependencies as part of an array:
If your template cache depends on multiple sources (try to avoid this to keep things simple),

record is updated.
approach, you won't be generating cache trash, unused keys, simply because the dependent
if the key stays stable. This means that unlike a traditional key-based cache expiration
record. When the project updated_at is touched, the #cache_version changes, even
This cache key is stable, but it's combined with a cache version derived from the project

^template path ^template tree digest ^class ^id
views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123

the project. The cache key generated from this call will be something like:
This approach will assume that when a new topic is added, you'll touch

<% end %>
<%= render project.topics %>
All the topics on this project
<% cache project do %>

When using this method, you list the cache dependency as the name of the cache, like so:

kick out old entries.
on top of a cache store like Memcached or Redis that'll automatically
The best way to use this is by doing recyclable key-based cache expiration

the content you wish to cache.
fragments, and so on. This method takes a block that contains
caching pieces like menus, lists of new topics, static HTML
rather than an entire action or page. This technique is useful
This helper exposes a method for caching fragments of a view
def cache(name = {}, options = {}, &block)
  if controller.respond_to?(:perform_caching) && controller.perform_caching
    CachingRegistry.track_caching do
      name_options = options.slice(:skip_digest)
      safe_concat(fragment_for(cache_fragment_name(name, **name_options), options, &block))
    end
  else
    yield
  end
  nil
end

def cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)

case when using memcached.
cannot be manually expired unless you know the exact key which is the
fragments can be manually bypassed. This is useful when cache fragments
call. By supplying skip_digest: true to cache, the digestion of cache
This helper returns the name of a cache key for a given fragment cache
def cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)
  if skip_digest
    name
  else
    fragment_name_with_digest(name, digest_path)
  end
end

def cache_if(condition, name = {}, options = {}, &block)

<% end %>
<%= render project.topics %>
All the topics on this project
<% cache_if admin?, project do %>

Cache fragments of a view if +condition+ is true
def cache_if(condition, name = {}, options = {}, &block)
  if condition
    cache(name, options, &block)
  else
    yield
  end
  nil
end

def cache_unless(condition, name = {}, options = {}, &block)

<% end %>
<%= render project.topics %>
All the topics on this project
<% cache_unless admin?, project do %>

Cache fragments of a view unless +condition+ is true
def cache_unless(condition, name = {}, options = {}, &block)
  cache_if !condition, name, options, &block
end

def caching?

<% end %>
<% raise StandardError, "Caching private data!" if caching? %>
<% cache project do %>

Useful when certain fragments aren't cacheable:

Returns whether the current view fragment is within a +cache+ block.
def caching?
  CachingRegistry.caching?
end

def digest_path_from_template(template) # :nodoc:

:nodoc:
def digest_path_from_template(template) # :nodoc:
  digest = Digestor.digest(name: template.virtual_path, format: template.format, finder: lookup_context, dependencies: view_cache_dependencies)
  if digest.present?
    "#{template.virtual_path}:#{digest}"
  else
    template.virtual_path
  end
end

def fragment_for(name = {}, options = nil, &block)

def fragment_for(name = {}, options = nil, &block)
  if content = read_fragment_for(name, options)
    @view_renderer.cache_hits[@current_template&.virtual_path] = :hit if defined?(@view_renderer)
    content
  else
    @view_renderer.cache_hits[@current_template&.virtual_path] = :miss if defined?(@view_renderer)
    write_fragment_for(name, options, &block)
  end
end

def fragment_name_with_digest(name, digest_path)

def fragment_name_with_digest(name, digest_path)
  name = controller.url_for(name).split("://").last if name.is_a?(Hash)
  if @current_template&.virtual_path || digest_path
    digest_path ||= digest_path_from_template(@current_template)
    [ digest_path, name ]
  else
    name
  end
end

def read_fragment_for(name, options)

def read_fragment_for(name, options)
  controller.read_fragment(name, options)
end

def uncacheable!

<% end %>
<%= project_name_with_time(project) %>
<% cache project do %>
# Which will then raise if used within a +cache+ block:

end
"#{project.name} - #{Time.now}"
uncacheable!
def project_name_with_time(project)

Useful to denote helper methods that can't participate in fragment caching:

Raises +UncacheableFragmentError+ when called from within a +cache+ block.
def uncacheable!
  raise UncacheableFragmentError, "can't be fragment cached" if caching?
end

def write_fragment_for(name, options)

def write_fragment_for(name, options)
  pos = output_buffer.length
  yield
  output_safe = output_buffer.html_safe?
  fragment = output_buffer.slice!(pos..-1)
  if output_safe
    self.output_buffer = output_buffer.class.new(output_buffer)
  end
  controller.write_fragment(name, fragment, options)
end