module ActionView::Helpers::CacheHelper
Вспомогательные методы кэширования Action View
Общедоступные методы экземпляра
# 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 Этот вспомогательный метод позволяет кэшировать фрагменты представления, а не целое действие или страницу. Этот способ полезен для кэширования отдельных элементов, например меню, списков новых тем, статических фрагментов 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 Этот ключ кэша стабилен, но объединяется с версией кэша, полученной из записи проекта. При обновлении project updated_at меняется cache_version, даже если сам ключ остаётся прежним. Это означает, что, в отличие от традиционного подхода к истечению срока действия кэша на основе ключей, обновление зависимой записи не приводит к накоплению мусора в кэше — неиспользуемых ключей.
Если шаблон зависит от нескольких источников (старайтесь этого избегать, чтобы не усложнять систему), можно перечислить все эти зависимости в массиве:
<% 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 в самом шаблоне. Вот примеры вызовов render, которые Cache Digests умеет распознавать:
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” как зависимости.
Явные зависимости
Иногда шаблон имеет зависимости, которые невозможно определить автоматически. Обычно это происходит, когда шаблоны отображаются из вспомогательных методов. Например:
<%= 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+)/, поэтому важно в точности его соблюдать. В каждой строке можно объявить только одну зависимость шаблона.
Внешние зависимости
Если внутри кэшируемого блока используется вспомогательный метод и вы затем изменяете этот метод, необходимо также обновить кэш. Способ не имеет особого значения, но MD5 файла шаблона должен измениться. Например, можно явно указать это в комментарии:
<%# Helper Dependency Updated: May 6, 2012 at 6pm %> <%= some_helper_method(person) %>
Теперь при изменении вспомогательного метода достаточно обновить временную метку.
Кэширование коллекций
При отображении коллекции объектов, для каждого из которых используется один и тот же частичный шаблон, можно передать параметр :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 248
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 Этот вспомогательный метод возвращает имя ключа кэша для заданного вызова кэширования фрагмента. Передав skip_digest: true в cache, можно вручную отключить вычисление дайджеста фрагментов кэша. Это полезно, когда фрагменты кэша нельзя обновить вручную, не зная точного ключа, например при использовании memcached.
# File actionview/lib/action_view/helpers/cache_helper.rb, line 223
def cache_if(condition, name = {}, options = {}, &block)
if condition
cache(name, options, &block)
else
yield
end
nil
end Кэширует фрагменты представления, если condition имеет значение true
<% cache_if admin?, project do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 239
def cache_unless(condition, name = {}, options = {}, &block)
cache_if !condition, name, options, &block
end Кэширует фрагменты представления, если condition не имеет значения true
<% cache_unless admin?, project do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 196 def caching? CachingRegistry.caching? end
Возвращает, находится ли текущий фрагмент представления внутри блока cache.
Полезно, когда некоторые фрагменты нельзя кэшировать:
<% cache project do %> <% raise StandardError, "Caching private data!" if caching? %> <% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 213 def uncacheable! raise UncacheableFragmentError, "can't be fragment cached" if caching? end
Вызывает исключение UncacheableFragmentError при вызове из блока cache.
Полезно для обозначения вспомогательных методов, которые нельзя использовать при кэшировании фрагментов:
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 %>
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.