module JSONAPI class CachedResponseFragment Lookup = Struct.new(:resource_klass, :serializer_config_key, :context, :context_key, :cache_ids) do def type resource_klass._type end def keys cache_ids.map do |(id, cache_key)| [type, id, cache_key, serializer_config_key, context_key] end end end Write = Struct.new(:resource_klass, :resource, :serializer, :serializer_config_key, :context, :context_key, :relationship_data) do def to_key_value (id, cache_key) = resource.cache_id json = serializer.object_hash(resource, relationship_data) cr = CachedResponseFragment.new( resource_klass, id, json['type'], context, resource.fetchable_fields, json['relationships'], json['links'], json['attributes'], json['meta'] ) key = [resource_klass._type, id, cache_key, serializer_config_key, context_key] [key, cr] end end attr_reader :resource_klass, :id, :type, :context, :fetchable_fields, :relationships, :links_json, :attributes_json, :meta_json def initialize(resource_klass, id, type, context, fetchable_fields, relationships, links_json, attributes_json, meta_json) @resource_klass = resource_klass @id = id @type = type @context = context @fetchable_fields = Set.new(fetchable_fields) # Relationships left uncompiled because we'll often want to insert included ids on retrieval @relationships = relationships @links_json = CompiledJson.of(links_json) @attributes_json = CompiledJson.of(attributes_json) @meta_json = CompiledJson.of(meta_json) end def to_cache_value { id: id, type: type, fetchable: fetchable_fields, rels: relationships, links: links_json.try(:to_s), attrs: attributes_json.try(:to_s), meta: meta_json.try(:to_s) } end # @param [Lookup[]] lookups # @return [Hash, Hash>] def self.lookup(lookups, context) type_to_klass = lookups.map {|l| [l.type, l.resource_klass]}.to_h keys = lookups.map(&:keys).flatten(1) hits = JSONAPI.configuration.resource_cache.read_multi(*keys).reject {|_, v| v.nil?} return keys.inject({}) do |hash, key| (type, id, _, _) = key resource_klass = type_to_klass[type] hash[resource_klass] ||= {} if hits.has_key?(key) hash[resource_klass][id] = self.from_cache_value(resource_klass, context, hits[key]) else hash[resource_klass][id] = nil end hash end end # @param [Write[]] lookups def self.write(writes) key_values = writes.map(&:to_key_value) to_write = key_values.map {|(k, v)| [k, v.to_cache_value]}.to_h if JSONAPI.configuration.resource_cache.respond_to? :write_multi JSONAPI.configuration.resource_cache.write_multi(to_write) else to_write.each do |key, value| JSONAPI.configuration.resource_cache.write(key, value) end end end def self.from_cache_value(resource_klass, context, h) new( resource_klass, h.fetch(:id), h.fetch(:type), context, h.fetch(:fetchable), h.fetch(:rels, nil), h.fetch(:links, nil), h.fetch(:attrs, nil), h.fetch(:meta, nil) ) end end end