lib/garner/strategies/binding/key/binding_index.rb
module Garner
module Strategies
module Binding
module Key
class BindingIndex < Base
RANDOM_KEY_LENGTH = 12 # In bytes.
# Compute a cache key as follows:
# 1. Determine whether the binding is canonical.
# 2. If canonical, fetch the cache key stored for binding.class,
# binding.id
# 3. If not canonical, determine the canonical ID from a proxy
# binding. Proxy bindings must implement `proxy_binding` and
# `handle`.
#
# @param binding [Object] The object from which to compute a key.
# @return [String] A cache key string.
def self.apply(binding)
fetch_cache_key_for(binding)
end
# Fetch cache key, from cache, for the given binding. Generate a
# random key, if not already extant.
#
# @param binding [Object] The object from which to compute a key.
# @return [String] A cache key string.
def self.fetch_cache_key_for(binding)
canonical_binding = fetch_canonical_binding_for(binding)
return nil unless canonical_binding
key = index_key_for(canonical_binding)
Garner.config.cache.fetch(key) { new_cache_key_for(canonical_binding) }
end
# Overwrite cache key for the given binding.
#
# @param binding [Object] The object from which to compute a key.
# @return [String] A cache key string.
def self.write_cache_key_for(binding)
canonical_binding = fetch_canonical_binding_for(binding)
return nil unless canonical_binding
key = index_key_for(canonical_binding)
value = new_cache_key_for(canonical_binding)
value.tap { |v| Garner.config.cache.write(key, v) }
end
# Fetch canonical binding for the given binding.
#
# @param binding [Object] The object from which to compute a key.
# @return [Object] A class, ID pair.
def self.fetch_canonical_binding_for(binding)
return binding if canonical?(binding)
key = index_key_for(binding)
Garner.config.cache.fetch(key) { canonical_binding_for(binding) }
end
# Overwrite canonical binding for the given binding.
#
# @param binding [Object] The object from which to compute a key.
# @return [Object] A class, ID pair.
def self.write_canonical_binding_for(binding)
return binding if canonical?(binding)
key = index_key_for(binding)
value = canonical_binding_for(binding)
value.tap { |v| Garner.config.cache.write(key, v) }
end
# Return canonical binding for the given binding.
#
# @param binding [Object] The (possibly) non-canonical binding.
# @return binding [Object] The canonical binding.
def self.canonical_binding_for(binding)
if canonical?(binding)
binding
elsif binding.respond_to?(:proxy_binding)
canonical_binding_for(binding.proxy_binding)
else
nil
end
end
# Determine whether the given binding is canonical.
#
# @return [Boolean]
def self.canonical?(binding)
# TODO: Implement real logic for determining canonicity.
binding.is_a?(Mongoid::Document) ||
(binding.is_a?(Class) && binding.include?(Mongoid::Document))
end
def self.index_key_for(binding)
if binding.respond_to?(:identity_string)
binding_key = binding.identity_string
else
binding_key = binding.to_s
end
{
strategy: self,
proxied_binding: binding_key
}
end
def self.new_cache_key_for(_binding)
SecureRandom.hex(RANDOM_KEY_LENGTH)
end
end
end
end
end
end