class Mustermann::EqualityMap
A simple wrapper around ObjectSpace::WeakMap that allows matching keys by equality rather than identity. Used for caching. Note that `fetch` is not guaranteed to return the object, even if it has not been garbage collected yet, especially when used concurrently. Therefore, the block passed to `fetch` has to be idempotent.
@example
class ExpensiveComputation @map = Mustermann::EqualityMap.new def self.new(*args) @map.fetch(args) { super } end end
@see fetch
Attributes
Public Class Methods
# File lib/mustermann/equality_map.rb, line 21 def self.new defined?(ObjectSpace::WeakMap) ? super : {} end
# File lib/mustermann/equality_map.rb, line 25 def initialize @keys = {} @map = ObjectSpace::WeakMap.new end
Public Instance Methods
@param [#hash] key for caching @yield block that will be called to populate entry if missing (has to be idempotent) @return value stored in map or result of block
# File lib/mustermann/equality_map.rb, line 33 def fetch(key) identity = @keys[key.hash] if identity == key key = identity elsif key.frozen? key = key.dup end # 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
Private Instance Methods
Finalizer proc needs to be generated in different scope so it doesn't keep a reference to the object.
@param [Integer] hash for key @return [Proc] finalizer callback
# File lib/mustermann/equality_map.rb, line 60 def finalizer(hash) proc { @keys.delete(hash) } end
@param [#hash] key for identifying the object @param [Object] object to be stored @return [Object] same as the second parameter
# File lib/mustermann/equality_map.rb, line 49 def track(key, object) object = object.dup if object.frozen? ObjectSpace.define_finalizer(object, finalizer(key.hash)) @keys[key.hash] = key object end