1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
# frozen_string_literal: true
require "concurrent/map"
module Dry
module Core
# Allows you to cache call results that are solely determined by arguments.
#
# @example
# require 'dry/core/cache'
#
# class Foo
# extend Dry::Core::Cache
#
# def heavy_computation(arg1, arg2)
# fetch_or_store(arg1, arg2) { arg1 ^ arg2 }
# end
# end
#
# @api public
module Cache
# @api private
def self.extended(klass)
super
klass.include(Methods)
klass.instance_variable_set(:@__cache__, Concurrent::Map.new)
end
# @api private
def inherited(klass)
super
klass.instance_variable_set(:@__cache__, cache)
end
# @api private
def cache
@__cache__
end
# Caches a result of the block evaluation
#
# @param [Array<Object>] args List of hashable objects
# @yield An arbitrary block
#
# @note beware Proc instance hashes are not equal, i.e. -> { 1 }.hash != -> { 1 }.hash,
# this means you shouldn't pass Procs in args unless you're sure
# they are always the same instances, otherwise you introduce a memory leak
#
# @return [Object] block's return value (cached for subsequent calls with
# the same argument values)
def fetch_or_store(*args, &block)
cache.fetch_or_store(args.hash, &block)
end
# Instance methods
module Methods
# Delegates call to the class-level method
#
# @param [Array<Object>] args List of hashable objects
# @yield An arbitrary block
#
# @return [Object] block's return value
def fetch_or_store(*args, &block)
self.class.fetch_or_store(*args, &block)
end
end
end
end
end
|