File: equality_map.rb

package info (click to toggle)
ruby-mustermann19 0.4.3%2Bgit20160621-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 756 kB
  • ctags: 445
  • sloc: ruby: 7,197; makefile: 3
file content (49 lines) | stat: -rw-r--r-- 1,551 bytes parent folder | download
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