модуль ActionView::Helpers::CacheHelper
Справочник по кэшированию Action View
Общедоступные методы экземпляра
# File actionview/lib/action_view/helpers/cache_helper.rb, line 168
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_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 240
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 215
def cache_if(condition, name = {}, options = {}, &block)
if condition
cache(name, options, &block)
else
yield
end
nil
end Кэширует фрагменты представления, если condition истинно
<% 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 231
def cache_unless(condition, name = {}, options = {}, &block)
cache_if !condition, name, options, &block
end Кэширует фрагменты представления, если condition ложно
<% 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 188 def caching? CachingRegistry.caching? end
Возвращает значение true, если текущий фрагмент представления находится внутри блока cache.
Полезно, когда некоторые фрагменты не кэшируются:
<% cache project do %> <% raise StandardError, "Caching private data!" if caching? %> <% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 205 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.