модуль 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, которые 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_sortable_todolists @project.todolists %>
Вам нужно будет использовать специальный формат комментариев для их вызова:
<%# Template Dependency: todolists/todolist %> <%= render_sortable_todolists @project.todolists %>
В некоторых случаях, например, при настройке single table inheritance, у вас может быть множество явных зависимостей. Вместо того, чтобы писать каждый шаблон, вы можете использовать подстановочный символ для соответствия любому шаблону в каталоге:
<%# 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 имеет значение 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 231
def cache_unless(condition, name = {}, options = {}, &block)
cache_if !condition, name, options, &block
end Кэширует фрагменты представления, если condition имеет значение false
<% 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.