class IknowCache::CacheGroup

def delete_all(key, parent_path: nil)

It is an error to do this if this CacheGroup has an statically versioned child, as that child cannot be invalidated.
Clear this key in all Caches in this CacheGroup.
def delete_all(key, parent_path: nil)
  @caches.each do |cache|
    cache.delete(key, parent_path: parent_path)
  end
  @children.each do |child_group|
    child_group.invalidate_cache_group(key)
  end
end

def initialize(parent, name, key_name, default_options, static_version)

def initialize(parent, name, key_name, default_options, static_version)
  @parent          = parent
  @name            = name
  @key_name        = key_name
  @key             = Struct.new(*parent.try { |p| p.key.members }, key_name)
  @default_options = IknowCache.merge_options(parent&.default_options, default_options).try { |x| x.dup.freeze }
  @static_version  = static_version
  @caches          = []
  @children        = []
end

def invalidate_cache_group(parent_key = nil)

invalidating all caches in it and its children
Clear all keys in this cache group (for the given parent),
def invalidate_cache_group(parent_key = nil)
  parent_path = self.parent_path(parent_key)
  IknowCache.cache.increment(version_path_string(parent_path))
end

def parent_path(parent_key = nil)

def parent_path(parent_key = nil)
  if parent.nil?
    ROOT_PATH
  else
    parent.path(parent_key)
  end
end

def parent_path_multi(parent_keys = nil)

look up multiple parent paths at once, returns { key => parent_path }
def parent_path_multi(parent_keys = nil)
  if parent.nil?
    parent_keys.each_with_object({}) { |k, h| h[k] = ROOT_PATH }
  else
    parent.path_multi(parent_keys)
  end
end

def path(key, parent_path = nil)

to save hitting the cache multiple times for its version.
Fetch the path for this cache. We allow the parent_path to be precomputed
def path(key, parent_path = nil)
  if key.nil? || key[self.key_name].nil?
    raise ArgumentError.new("Missing required key '#{self.key_name}' for cache '#{self.name}'")
  end
  key_value     = key[self.key_name]
  parent_path ||= self.parent_path(key)
  version       = self.version(parent_path)
  path_string(parent_path, version, key_value)
end

def path_multi(keys)

compute multiple paths at once: returns { key => path }
def path_multi(keys)
  # compute parent path for each key
  parent_paths = self.parent_path_multi(keys)
  # and versions for each parent path
  versions = self.version_multi(parent_paths.values.uniq)
  # update parent_paths with our paths
  keys.each do |key|
    parent_path = parent_paths[key]
    version     = versions[parent_path]
    key_value   = key[self.key_name]
    unless key_value
      raise ArgumentError.new("Required cache key missing: #{self.key_name}")
    end
    parent_paths[key] = path_string(parent_path, version, key_value)
  end
  parent_paths
end

def path_string(parent_path, version, value)

def path_string(parent_path, version, value)
  "#{parent_path}/#{name}/#{@static_version}/#{version}/#{value}"
end

def register_cache(name, static_version: nil, cache_options: nil)

def register_cache(name, static_version: nil, cache_options: nil)
  c = Cache.new(self, name, static_version, cache_options)
  @caches << c
  c
end

def register_child_group(name, key_name, default_options: nil, static_version: 1)

def register_child_group(name, key_name, default_options: nil, static_version: 1)
  group = CacheGroup.new(self, name, key_name, default_options, static_version)
  @children << group
  yield group if block_given?
  group
end

def version(parent_path)

def version(parent_path)
  IknowCache.cache.fetch(version_path_string(parent_path), raw: true) { 1 }
end

def version_multi(parent_paths)

Look up multiple versions at once, returns { parent_path => version }
def version_multi(parent_paths)
  # compute version paths
  version_by_pp = parent_paths.each_with_object({}) { |pp, h| h[pp] = version_path_string(pp) }
  version_paths = version_by_pp.values
  # look up versions in cache
  versions = IknowCache.cache.read_multi(*version_paths, raw: true)
  version_paths.each do |vp|
    next if versions.has_key?(vp)
    versions[vp] = IknowCache.cache.fetch(vp, raw: true) { 1 }
  end
  # swap in the versions
  parent_paths.each do |pp|
    vp = version_by_pp[pp]
    version = versions[vp]
    version_by_pp[pp] = version
  end
  version_by_pp
end

def version_path_string(parent_path)

def version_path_string(parent_path)
  "#{parent_path}/#{name}/_version"
end