1# frozen_string_literal: true
2require "delegate"
3
4# Weak Reference class that allows a referenced object to be
5# garbage-collected.
6#
7# A WeakRef may be used exactly like the object it references.
8#
9# Usage:
10#
11#   foo = Object.new            # create a new object instance
12#   p foo.to_s                  # original's class
13#   foo = WeakRef.new(foo)      # reassign foo with WeakRef instance
14#   p foo.to_s                  # should be same class
15#   GC.start                    # start the garbage collector
16#   p foo.to_s                  # should raise exception (recycled)
17#
18
19class WeakRef < Delegator
20
21  ##
22  # RefError is raised when a referenced object has been recycled by the
23  # garbage collector
24
25  class RefError < StandardError
26  end
27
28  @@__map = ::ObjectSpace::WeakMap.new
29
30  ##
31  # Creates a weak reference to +orig+
32  #
33  # Raises an ArgumentError if the given +orig+ is immutable, such as Symbol,
34  # Integer, or Float.
35
36  def initialize(orig)
37    case orig
38    when true, false, nil
39      @delegate_sd_obj = orig
40    else
41      @@__map[self] = orig
42    end
43    super
44  end
45
46  def __getobj__ # :nodoc:
47    @@__map[self] or defined?(@delegate_sd_obj) ? @delegate_sd_obj :
48      Kernel::raise(RefError, "Invalid Reference - probably recycled", Kernel::caller(2))
49  end
50
51  def __setobj__(obj) # :nodoc:
52  end
53
54  ##
55  # Returns true if the referenced object is still alive.
56
57  def weakref_alive?
58    @@__map.key?(self) or defined?(@delegate_sd_obj)
59  end
60end
61