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

map[R]

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/mustermann/equality_map.rb, line 21
def self.new
  defined?(ObjectSpace::WeakMap) ? super : {}
end
new() click to toggle source
# File lib/mustermann/equality_map.rb, line 25
def initialize
  @keys = {}
  @map  = ObjectSpace::WeakMap.new
end

Public Instance Methods

fetch(key) { || ... } click to toggle source

@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(hash) click to toggle source

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
track(key, object) click to toggle source

@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