实例公共方法
cache(name = {}, options = {}, &block) Link
此助手提供了一种方法来缓存视图的片段,而不是整个动作或页面。这种技术对于缓存菜单、新主题列表、静态 HTML 片段等很有用。此方法接受一个包含您希望缓存的内容的块。
使用此方法的最佳方式是在 Memcached 或 Redis 等缓存存储上进行可回收的基于键的缓存过期,它将自动删除旧条目。
使用此方法时,将缓存依赖项列为缓存的名称,如下所示
<% cache project do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
此方法将假设当添加新主题时,您将触碰项目。此调用生成的缓存键将类似于
views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123
      ^template path  ^template tree digest            ^class   ^id
此缓存键是稳定的,但它与源自项目记录的缓存版本结合在一起。当项目 updated_at 被触碰时,即使键保持稳定,缓存版本也会更改。这意味着与传统的基于键的缓存过期方法不同,您不会因为依赖记录已更新而生成缓存垃圾、未使用的键。
如果您的模板缓存依赖于多个源(请尽量避免这样做以保持简单),您可以将所有这些依赖项命名为一个数组
<% cache [ project, current_user ] do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
这将把这两个记录都包含在缓存键中,更新其中任何一个都会使缓存失效。
模板摘要¶ ↑
添加到缓存键的模板摘要是通过获取整个模板文件的内容的 MD5 来计算的。这确保了在您更改模板文件时,您的缓存将自动过期。
请注意,MD5 是针对整个模板文件而不是仅在 cache do/end 调用内的内容进行的。因此,在调用外部的内容可能会导致缓存过期。
此外,摘要器将自动扫描您的模板文件中的显式和隐式依赖项,并将它们包含在摘要中。
可以通过将 skip_digest: true 作为选项传递给 cache 调用来绕过摘要器
<% cache project, skip_digest: true do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
隐式依赖项¶ ↑
大多数模板依赖项都可以从模板本身的 render 调用中派生出来。以下是一些 Cache Digests 知道如何解码的 render 调用示例
render partial: "comments/comment", collection: commentable.comments render "comments/comments" render 'comments/comments' render('comments/comments') render "header" # translates to render("comments/header") render(@topic) # translates to render("topics/topic") render(topics) # translates to render("topics/topic") render(message.topics) # translates to render("topics/topic")
然而,并非所有 render 调用都可以如此推导。以下是一些无法推导的示例
render group_of_attachments render @project.documents.where(published: true).order('created_at')
您将不得不将它们重写为显式形式
render partial: 'attachments/attachment', collection: group_of_attachments render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
最后一种依赖关系可以隐式确定
render "maintenance_tasks/runs/info/#{run.status}"
由于传递给 render 的值以插值为结尾,Action View 将把 "maintenance_tasks/runs/info" 文件夹内的所有部分标记为依赖项。
显式依赖项¶ ↑
有时您会遇到完全无法派生的模板依赖项。当在 helpers 中发生模板渲染时,通常就是这种情况。这是一个例子
<%= render_sortable_todolists @project.todolists %>
您需要使用特殊的注释格式来调用它们
<%# Template Dependency: todolists/todolist %> <%= render_sortable_todolists @project.todolists %>
在某些情况下,例如单一表继承设置,您可能有很多显式依赖项。您可以改用通配符来匹配目录中的任何模板,而不是编写每个模板
<%# Template Dependency: events/* %> <%= render_categorizable_events @person.events %>
这会将目录中的每个模板标记为依赖项。要找到这些模板,通配符路径必须从 app/views 完全定义,或者通过 prepend_view_path 或 append_view_path 添加的路径。这样,app/views/recordings/events 的通配符将是 recordings/events/* 等。
用于匹配显式依赖项的模式是 /# Template Dependency: (\S+)/,所以一定要这样输入。您每行只能声明一个模板依赖项。
外部依赖项¶ ↑
例如,如果您在缓存块中使用 helper 方法,然后更新该 helper,您还需要更新缓存。如何做到这一点并不重要,但模板文件的 MD5 必须改变。一种建议是简单地在注释中显式说明,例如
<%# Helper Dependency Updated: May 6, 2012 at 6pm %> <%= some_helper_method(person) %>
现在,当 helper 方法更改时,您只需更改此时间戳。
集合缓存¶ ↑
渲染每个使用相同部分的对象的集合时,可以传递 :cached 选项。
对于像这样渲染的集合
<%= render partial: 'projects/project', collection: @projects, cached: true %>
cached: true 将使 Action View 的渲染一次从缓存中读取多个模板,而不是为每个模板调用一次。
集合中尚未缓存的模板将被写入缓存。
与单个模板片段缓存结合使用效果很好。例如,如果集合渲染的模板被缓存,如下所示
# projects/_project.html.erb <% cache project do %> <%# ... %> <% end %>
任何集合渲染在尝试一次读取多个模板时都会找到这些缓存的模板。
如果您的集合缓存依赖于多个源(请尽量避免这样做以保持简单),您可以将所有这些依赖项命名为一个返回数组的块
<%= render partial: 'projects/project', collection: @projects, cached: -> project { [ project, current_user ] } %>
这将把这两个记录都包含在缓存键中,更新其中任何一个都会使缓存失效。
# File actionview/lib/action_view/helpers/cache_helper.rb, line 176 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
cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil) Link
此助手为给定的片段缓存调用返回缓存键的名称。通过向 cache 提供 skip_digest: true,可以手动绕过片段缓存的摘要。这在无法手动过期片段缓存的情况下很有用,除非您知道确切的键,这在使用 memcached 时就是这种情况。
cache_if(condition, name = {}, options = {}, &block) Link
如果 condition 为 true,则缓存视图的片段
<% cache_if admin?, project do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
cache_unless(condition, name = {}, options = {}, &block) Link
除非 condition 为 true,否则缓存视图的片段
<% cache_unless admin?, project do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
caching?() Link
返回当前视图片段是否在 cache 块内。
当某些片段不可缓存时很有用
<% cache project do %> <% raise StandardError, "Caching private data!" if caching? %> <% end %>
uncacheable!() Link
当从 cache 块内部调用时,引发 UncacheableFragmentError。
用于表示无法参与片段缓存的 helper 方法
def project_name_with_time(project)
  uncacheable!
  "#{project.name} - #{Time.now}"
end
# Which will then raise if used within a `cache` block:
<% cache project do %>
  <%= project_name_with_time(project) %>
<% end %>