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
|
module Mustermann
# A simple wrapper around ObjectSpace::WeakMap that allows matching keys by equality rather than identity.
# Used for caching.
#
# @see #fetch
# @!visibility private
class EqualityMap
major, minor, _ = RUBY_VERSION.split(?.).map(&:to_i)
MAP_CLASS = major > 1 && minor >= 1 && defined?(ObjectSpace::WeakMap) ? ObjectSpace::WeakMap : Hash
# @!visibility private
def initialize
@keys = {}
@map = MAP_CLASS.new
end
# @param [Array<#hash>] key for caching
# @yield block that will be called to populate entry if missing
# @return value stored in map or result of block
# @!visibility private
def fetch(*key)
identity = @keys[key.hash]
key = identity == key ? identity : key
# it is ok that this is not thread-safe, worst case it has double cost in
# generating, object equality is not guaranteed anyways
@map[key] ||= track(key, yield)
end
# @param [#hash] key for identifying the object
# @param [Object] object to be stored
# @return [Object] same as the second parameter
def track(key, object)
ObjectSpace.define_finalizer(object, finalizer(key.hash))
@keys[key.hash] = key
object
end
# Finalizer proc needs to be generated in different scope so it doesn't keep a reference to the object.
#
# @param [Fixnum] hash for key
# @return [Proc] finalizer callback
def finalizer(hash)
proc { @keys.delete(hash) }
end
private :track, :finalizer
end
end
|