method
default_serializer
v7.1.3.4 -
Show latest stable
- Class:
ActiveSupport::Cache::Store
default_serializer()private
No documentation available.
# File activesupport/lib/active_support/cache.rb, line 762
def default_serializer
case Cache.format_version
when 6.1
ActiveSupport.deprecator.warn <<~EOM
Support for `config.active_support.cache_format_version = 6.1` has been deprecated and will be removed in Rails 7.2.
Check the Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
for more information on how to upgrade.
EOM
Cache::SerializerWithFallback[:marshal_6_1]
when 7.0
Cache::SerializerWithFallback[:marshal_7_0]
when 7.1
Cache::SerializerWithFallback[:marshal_7_1]
else
raise ArgumentError, "Unrecognized ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}"
end
end
# Adds the namespace defined in the options to a pattern designed to
# match keys. Implementations that support delete_matched should call
# this method to translate a pattern that matches names into one that
# matches namespaced keys.
def key_matcher(pattern, options) # :doc:
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
if prefix
source = pattern.source
if source.start_with?("^")
source = source[1, source.length]
else
source = ".*#{source[0, source.length]}"
end
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
else
pattern
end
end
# Reads an entry from the cache implementation. Subclasses must implement
# this method.
def read_entry(key, **options)
raise NotImplementedError.new
end
# Writes an entry to the cache implementation. Subclasses must implement
# this method.
def write_entry(key, entry, **options)
raise NotImplementedError.new
end
def serialize_entry(entry, **options)
options = merged_options(options)
if @coder_supports_compression && options[:compress]
@coder.dump_compressed(entry, options[:compress_threshold])
else
@coder.dump(entry)
end
end
def deserialize_entry(payload, **)
payload.nil? ? nil : @coder.load(payload)
rescue DeserializationError
nil
end
# Reads multiple entries from the cache implementation. Subclasses MAY
# implement this method.
def read_multi_entries(names, **options)
names.each_with_object({}) do |name, results|
key = normalize_key(name, options)
entry = read_entry(key, **options)
next unless entry
version = normalize_version(name, options)
if entry.expired?
delete_entry(key, **options)
elsif !entry.mismatched?(version)
results[name] = entry.value
end
end
end
# Writes multiple entries to the cache implementation. Subclasses MAY
# implement this method.
def write_multi_entries(hash, **options)
hash.each do |key, entry|
write_entry key, entry, **options
end
end
# Deletes an entry from the cache implementation. Subclasses must
# implement this method.
def delete_entry(key, **options)
raise NotImplementedError.new
end
# Deletes multiples entries in the cache implementation. Subclasses MAY
# implement this method.
def delete_multi_entries(entries, **options)
entries.count { |key| delete_entry(key, **options) }
end
# Merges the default options with ones specific to a method call.
def merged_options(call_options)
if call_options
call_options = normalize_options(call_options)
if call_options.key?(:expires_in) && call_options.key?(:expires_at)
raise ArgumentError, "Either :expires_in or :expires_at can be supplied, but not both"
end
expires_at = call_options.delete(:expires_at)
call_options[:expires_in] = (expires_at - Time.now) if expires_at
if call_options[:expires_in].is_a?(Time)
expires_in = call_options[:expires_in]
raise ArgumentError.new("expires_in parameter should not be a Time. Did you mean to use expires_at? Got: #{expires_in}")
end
if call_options[:expires_in]&.negative?
expires_in = call_options.delete(:expires_in)
handle_invalid_expires_in("Cache expiration time is invalid, cannot be negative: #{expires_in}")
end
if options.empty?
call_options
else
options.merge(call_options)
end
else
options
end
end
def handle_invalid_expires_in(message)
error = ArgumentError.new(message)
if ActiveSupport::Cache::Store.raise_on_invalid_cache_expiration_time
raise error
else
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
logger.error("#{error.class}: #{error.message}") if logger
end
end
# Normalize aliased options to their canonical form
def normalize_options(options)
options = options.dup
OPTION_ALIASES.each do |canonical_name, aliases|
alias_key = aliases.detect { |key| options.key?(key) }
options[canonical_name] ||= options[alias_key] if alias_key
options.except!(*aliases)
end
options
end
def validate_options(options)
if options.key?(:coder) && options[:serializer]
raise ArgumentError, "Cannot specify :serializer and :coder options together"
end
if options.key?(:coder) && options[:compressor]
raise ArgumentError, "Cannot specify :compressor and :coder options together"
end
if Cache.format_version < 7.1 && !options[:serializer] && options[:compressor]
raise ArgumentError, "Cannot specify :compressor option when using" " default serializer and cache format version is < 7.1"
end
options
end
# Expands and namespaces the cache key.
# Raises an exception when the key is +nil+ or an empty string.
# May be overridden by cache stores to do additional normalization.
def normalize_key(key, options = nil)
str_key = expanded_key(key)
raise(ArgumentError, "key cannot be blank") if !str_key || str_key.empty?
namespace_key str_key, options
end
# Prefix the key with a namespace string:
#
# namespace_key 'foo', namespace: 'cache'
# # => 'cache:foo'
#
# With a namespace block:
#
# namespace_key 'foo', namespace: -> { 'cache' }
# # => 'cache:foo'
def namespace_key(key, options = nil)
options = merged_options(options)
namespace = options[:namespace]
if namespace.respond_to?(:call)
namespace = namespace.call
end
if key && key.encoding != Encoding::UTF_8
key = key.dup.force_encoding(Encoding::UTF_8)
end
if namespace
"#{namespace}:#{key}"
else
key
end
end
# Expands key to be a consistent string value. Invokes +cache_key+ if
# object responds to +cache_key+. Otherwise, +to_param+ method will be
# called. If the key is a Hash, then keys will be sorted alphabetically.
def expanded_key(key)
return key.cache_key.to_s if key.respond_to?(:cache_key)
case key
when Array
if key.size > 1
key.collect { |element| expanded_key(element) }
else
expanded_key(key.first)
end
when Hash
key.collect { |k, v| "#{k}=#{v}" }.sort!
else
key
end.to_param
end
def normalize_version(key, options = nil)
(options && options[:version].try(:to_param)) || expanded_version(key)
end
def expanded_version(key)
case
when key.respond_to?(:cache_version) then key.cache_version.to_param
when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
when key.respond_to?(:to_a) then expanded_version(key.to_a)
end
end
def instrument(operation, key, options = nil, &block)
_instrument(operation, key: key, options: options, &block)
end
def instrument_multi(operation, keys, options = nil, &block)
_instrument(operation, multi: true, key: keys, options: options, &block)
end
def _instrument(operation, multi: false, options: nil, **payload, &block)
if logger && logger.debug? && !silence?
debug_key =
if multi
": #{payload[:key].size} key(s) specified"
elsif payload[:key]
": #{normalize_key(payload[:key], options)}"
end
debug_options = " (#{options.inspect})" unless options.blank?
logger.debug "Cache #{operation}#{debug_key}#{debug_options}"
end
payload[:store] = self.class.name
payload.merge!(options) if options.is_a?(Hash)
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
block&.call(payload)
end
end
def handle_expired_entry(entry, key, options)
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
# for a brief period while the entry is being recalculated.
entry.expires_at = Time.now.to_f + race_ttl
write_entry(key, entry, expires_in: race_ttl * 2)
else
delete_entry(key, **options)
end
entry = nil
end
entry
end
def get_entry_value(entry, name, options)
instrument(:fetch_hit, name, options)
entry.value
end
def save_block_result_to_cache(name, options)
options = options.dup
result = instrument(:generate, name, options) do
yield(name, WriteOptions.new(options))
end
write(name, result, options) unless result.nil? && options[:skip_nil]
result
end
end
# Enables the dynamic configuration of Cache entry options while ensuring
# that conflicting options are not both set. When a block is given to
# ActiveSupport::Cache::Store#fetch, the second argument will be an
# instance of +WriteOptions+.
class WriteOptions
def initialize(options) # :nodoc:
@options = options
end
def version
@options[:version]
end
def version=(version)
@options[:version] = version
end
def expires_in
@options[:expires_in]
end
# Sets the Cache entry's +expires_in+ value. If an +expires_at+ option was
# previously set, this will unset it since +expires_in+ and +expires_at+
# cannot both be set.
def expires_in=(expires_in)
@options.delete(:expires_at)
@options[:expires_in] = expires_in
end
def expires_at
@options[:expires_at]
end
# Sets the Cache entry's +expires_at+ value. If an +expires_in+ option was
# previously set, this will unset it since +expires_at+ and +expires_in+
# cannot both be set.
def expires_at=(expires_at)
@options.delete(:expires_in)
@options[:expires_at] = expires_at
end
end
end