# frozen_string_literal: true module Gitlab class JsonCache attr_reader :backend, :cache_key_with_version, :namespace def initialize(options = {}) @backend = options.fetch(:backend, Rails.cache) @namespace = options.fetch(:namespace, nil) @cache_key_with_version = options.fetch(:cache_key_with_version, true) end def active? if backend.respond_to?(:active?) backend.active? else true end end def cache_key(key) expanded_cache_key = [namespace, key].compact if cache_key_with_version expanded_cache_key << Rails.version end expanded_cache_key.join(':') end def expire(key) backend.delete(cache_key(key)) end def read(key, klass = nil) value = backend.read(cache_key(key)) value = parse_value(value, klass) if value value end def write(key, value, options = nil) backend.write(cache_key(key), value.to_json, options) end def fetch(key, options = {}, &block) klass = options.delete(:as) value = read(key, klass) return value unless value.nil? value = yield write(key, value, options) value end private def parse_value(raw, klass) value = ActiveSupport::JSON.decode(raw) case value when Hash then parse_entry(value, klass) when Array then parse_entries(value, klass) else value end rescue ActiveSupport::JSON.parse_error nil end def parse_entry(raw, klass) klass.new(raw) if valid_entry?(raw, klass) end def valid_entry?(raw, klass) return false unless klass && raw.is_a?(Hash) (raw.keys - klass.attribute_names).empty? end def parse_entries(values, klass) values.map { |value| parse_entry(value, klass) }.compact end end end