модуль 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 Этот ключ кэша стабилен, но он комбинируется с версией кэша, полученной из записи проекта. При обновлении поля updated_at проекта версия кэша изменяется, даже если ключ остается стабильным. Это означает, что в отличие от традиционного подхода к кэшированию на основе ключей, вы не будете генерировать мусор кэша, неиспользуемые ключи, просто потому, что зависимая запись обновлена.
Если кэш вашего шаблона зависит от нескольких источников (постарайтесь этого избегать для простоты), вы можете указать все эти зависимости в качестве элементов массива:
<% cache [ project, current_user ] do %> <b>All the topics on this project</b> <%= render project.topics %> <% end %>
Это включит обе записи в ключ кэша, и обновление любой из них приведёт к истечению срока действия кэша.
Хеш шаблона
Хеш шаблона, добавляемый в ключ кэша, вычисляется путём взятия MD5 от содержимого всего файла шаблона. Это гарантирует, что ваши кэши будут автоматически истекать при изменении файла шаблона.
Обратите внимание, что MD5 вычисляется для всего файла шаблона, а не только для содержимого внутри вызова 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, которые модуль анализа хешей шаблонов умеет декодировать:
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.