method

default_serializer

Importance_0
v7.1.3.2 - Show latest stable - 0 notes - Class: ActiveSupport::Cache::Store
  • 1.0.0
  • 1.1.6
  • 1.2.6
  • 2.0.3
  • 2.1.0
  • 2.2.1
  • 2.3.8
  • 3.0.0
  • 3.0.9
  • 3.1.0
  • 3.2.1
  • 3.2.8
  • 3.2.13
  • 4.0.2
  • 4.1.8
  • 4.2.1
  • 4.2.7
  • 4.2.9
  • 5.0.0.1
  • 5.1.7
  • 5.2.3
  • 6.0.0
  • 6.1.3.1
  • 6.1.7.7
  • 7.0.0
  • 7.1.3.2 (0)
  • 7.1.3.4 (0)
  • What's this?
default_serializer() private

No documentation

This method has no description. You can help the Ruby on Rails community by adding new notes.

Hide source
# 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
Register or log in to add new notes.