class ViewModel::ActiveRecord::Cache
Cache for ViewModels that wrap ActiveRecord models.
def cache_for(migration_version)
def cache_for(migration_version) if migration_version @migrated_cache else @cache end end
def cache_name
versions of the constituent viewmodels, so that viewmodel changes force
Statically version the cache name based on the (current) deep schema
def cache_name "vmcache_#{cache_version}" end
def cache_version
def cache_version @cache_version ||= begin versions = @viewmodel_class.deep_schema_version(include_referenced: false) ViewModel.schema_hash(versions) end end
def clear
def clear @cache_group.invalidate_cache_group end
def create_default_cache_group
def create_default_cache_group IknowCache.register_group(@viewmodel_class.name, :id) end
def delete(*ids)
def delete(*ids) ids.each do |id| id = id.id if id.respond_to?(:id) raise ArgumentError.new unless id.is_a?(Numeric) || id.is_a?(String) @cache_group.delete_all(@cache.key.new(id)) end end
def fetch(ids, initial_viewmodels: nil, migration_versions: {}, locked: false, serialize_context: @viewmodel_class.new_serialize_context)
- Replaced by class methods
def fetch(ids, initial_viewmodels: nil, migration_versions: {}, locked: false, serialize_context: @viewmodel_class.new_serialize_context) self.class.render_from_cache(@viewmodel_class, ids, initial_viewmodels: initial_viewmodels, locked: locked, migration_versions: migration_versions, serialize_context: serialize_context) end
def fetch_by_viewmodel(viewmodels, migration_versions: {}, locked: false, serialize_context: @viewmodel_class.new_serialize_context)
- Replaced by class methods
def fetch_by_viewmodel(viewmodels, migration_versions: {}, locked: false, serialize_context: @viewmodel_class.new_serialize_context) ids = viewmodels.map(&:id) fetch(ids, initial_viewmodels: viewmodels, migration_versions: migration_versions, locked: locked, serialize_context: serialize_context) end
def id_for(key)
def id_for(key) key[:id] end
def initialize(viewmodel_class, cache_group: nil)
def initialize(viewmodel_class, cache_group: nil) @viewmodel_class = viewmodel_class @cache_group = cache_group || create_default_cache_group @migrated_cache_group = @cache_group.register_child_group(:migrated, :version) # /viewname/:id/viewname-currentversion @cache = @cache_group.register_cache(cache_name) # /viewname/:id/migrated/:oldversion/viewname-currentversion @migrated_cache = @migrated_cache_group.register_cache(cache_name) end
def key_for(id, migration_version)
def key_for(id, migration_version) if migration_version @migrated_cache.key.new(id, migration_version) else @cache.key.new(id) end end
def load(ids, migration_version)
def load(ids, migration_version) keys = ids.map { |id| key_for(id, migration_version) } results = cache_for(migration_version).read_multi(keys) results.transform_keys! { |key| id_for(key) } end
def migrated_cache_version(migration_versions)
def migrated_cache_version(migration_versions) versions = ViewModel::Migrator.migrated_deep_schema_version(viewmodel_class, migration_versions, include_referenced: false) version_hash = ViewModel.schema_hash(versions) if version_hash == cache_version # no migrations affect this view nil else version_hash end end
def render_from_cache(viewmodel_class, ids, initial_viewmodels: nil, migration_versions: {}, locked: false, serialize_context: viewmodel_class.new_serialize_context)
def render_from_cache(viewmodel_class, ids, initial_viewmodels: nil, migration_versions: {}, locked: false, serialize_context: viewmodel_class.new_serialize_context) ignore_existing = false begin worker = CacheWorker.new(migration_versions: migration_versions, serialize_context: serialize_context, ignore_existing: ignore_existing) worker.render_from_cache(viewmodel_class, ids, initial_viewmodels: initial_viewmodels, locked: locked) rescue StaleCachedReference # If the cache contents contained a unresolvable stale reference, retry # while ignoring existing cached values (thereby updating the cache). This # will have the side-effect of duplicate Before/AfterVisit callbacks. if ignore_existing raise else ignore_existing = true retry end end end
def render_viewmodels_from_cache(viewmodels, migration_versions: {}, locked: false, serialize_context: nil)
def render_viewmodels_from_cache(viewmodels, migration_versions: {}, locked: false, serialize_context: nil) if viewmodels.empty? return [], {} end ids = viewmodels.map(&:id) # ideally the roots wouldn't have to all be the same type viewmodel_class = viewmodels.first.class serialize_context ||= viewmodel_class.new_serialize_context render_from_cache(viewmodel_class, ids, initial_viewmodels: viewmodels, migration_versions: migration_versions, locked: locked, serialize_context: serialize_context) end
def store(id, migration_version, data_serialization, ref_cache)
def store(id, migration_version, data_serialization, ref_cache) key = key_for(id, migration_version) cache_for(migration_version).write(key, { data: data_serialization, ref_cache: ref_cache }) end