# frozen_string_literal: true# :markup: markdownmoduleAbstractControllermoduleCaching# # Abstract Controller Caching Fragments## Fragment caching is used for caching various blocks within views without# caching the entire action as a whole. This is useful when certain elements of# an action change frequently or depend on complicated state while other parts# rarely change or can be shared amongst multiple parties. The caching is done# using the `cache` helper available in the Action View. See# ActionView::Helpers::CacheHelper for more information.## While it's strongly recommended that you use key-based cache expiration (see# links in CacheHelper for more information), it is also possible to manually# expire caches. For example:## expire_fragment('name_of_cache')moduleFragmentsextendActiveSupport::Concernincludeddoifrespond_to?(:class_attribute)class_attribute:fragment_cache_keyselsemattr_writer:fragment_cache_keysendself.fragment_cache_keys=[]ifrespond_to?(:helper_method)helper_method:combined_fragment_cache_keyendendmoduleClassMethods# Allows you to specify controller-wide key prefixes for cache fragments. Pass# either a constant `value`, or a block which computes a value each time a cache# key is generated.## For example, you may want to prefix all fragment cache keys with a global# version identifier, so you can easily invalidate all caches.## class ApplicationController# fragment_cache_key "v1"# end## When it's time to invalidate all fragments, simply change the string constant.# Or, progressively roll out the cache invalidation using a computed value:## class ApplicationController# fragment_cache_key do# @account.id.odd? ? "v1" : "v2"# end# enddeffragment_cache_key(value=nil,&key)self.fragment_cache_keys+=[key||->{value}]endend# Given a key (as described in `expire_fragment`), returns a key array suitable# for use in reading, writing, or expiring a cached fragment. All keys begin# with `:views`, followed by `ENV["RAILS_CACHE_ID"]` or# `ENV["RAILS_APP_VERSION"]` if set, followed by any controller-wide key prefix# values, ending with the specified `key` value.defcombined_fragment_cache_key(key)head=self.class.fragment_cache_keys.map{|k|instance_exec(&k)}tail=key.is_a?(Hash)?url_for(key).split("://").last:keycache_key=[:views,ENV["RAILS_CACHE_ID"]||ENV["RAILS_APP_VERSION"],head,tail]cache_key.flatten!(1)cache_key.compact!cache_keyend# Writes `content` to the location signified by `key` (see `expire_fragment` for# acceptable formats).defwrite_fragment(key,content,options=nil)returncontentunlesscache_configured?key=combined_fragment_cache_key(key)instrument_fragment_cache:write_fragment,keydocontent=content.to_strcache_store.write(key,content,options)endcontentend# Reads a cached fragment from the location signified by `key` (see# `expire_fragment` for acceptable formats).defread_fragment(key,options=nil)returnunlesscache_configured?key=combined_fragment_cache_key(key)instrument_fragment_cache:read_fragment,keydoresult=cache_store.read(key,options)result.respond_to?(:html_safe)?result.html_safe:resultendend# Check if a cached fragment from the location signified by `key` exists (see# `expire_fragment` for acceptable formats).deffragment_exist?(key,options=nil)returnunlesscache_configured?key=combined_fragment_cache_key(key)instrument_fragment_cache:exist_fragment?,keydocache_store.exist?(key,options)endend# Removes fragments from the cache.## `key` can take one of three forms:## * String - This would normally take the form of a path, like# `pages/45/notes`.# * Hash - Treated as an implicit call to `url_for`, like `{ controller:# 'pages', action: 'notes', id: 45}`# * Regexp - Will remove any fragment that matches, so `%r{pages/\d*/notes}`# might remove all notes. Make sure you don't use anchors in the regex (`^`# or `$`) because the actual filename matched looks like# `./cache/filename/path.cache`. Note: Regexp expiration is only supported# on caches that can iterate over all keys (unlike memcached).### `options` is passed through to the cache store's `delete` method (or# `delete_matched`, for Regexp keys).defexpire_fragment(key,options=nil)returnunlesscache_configured?key=combined_fragment_cache_key(key)unlesskey.is_a?(Regexp)instrument_fragment_cache:expire_fragment,keydoifkey.is_a?(Regexp)cache_store.delete_matched(key,options)elsecache_store.delete(key,options)endendenddefinstrument_fragment_cache(name,key,&block)# :nodoc:ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}",instrument_payload(key),&block)endendendend