Module: Weak::Set::WeakKeys

Defined in:
lib/weak/set/weak_keys.rb

Overview

This Weak::Set strategy targets Ruby < 3.3.0.

Its ObjectSpace::WeakMap uses weak keys and weak values so that either the key or the value can be independently garbage collected. If either of them vanishes, the entry is removed.

The ObjectSpace::WeakMap does not allow to explicitly delete entries. We emulate this by setting the garbage-collectible value of a deleted entry to a simple new object. This value will be garbage collected on the next GC run which will then remove the entry. When accessing elements, we delete and filter out these recently deleted entries.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.usable?Bool

Checks if this strategy is usable for the current Ruby version.



29
30
31
# File 'lib/weak/set/weak_keys.rb', line 29

def self.usable?
  RUBY_ENGINE == "ruby"
end

Instance Method Details

#add(obj) ⇒ self

Adds the given object to the weak set and return self. Use Weak::Set#merge to add many elements at once.

In contrast to other “regular” objects, we will not retain a strong reference to the added object. Unless some other live objects still references the object, it will eventually be garbage-collected.

Examples:

Weak::Set[1, 2].add(3)                #=> #<Weak::Set {1, 2, 3}>
Weak::Set[1, 2].add([3, 4])           #=> #<Weak::Set {1, 2, [3, 4]}>
Weak::Set[1, 2].add(2)                #=> #<Weak::Set {1, 2}>


34
35
36
37
# File 'lib/weak/set/weak_keys.rb', line 34

def add(obj)
  @map[obj] = obj
  self
end

#clearself

Removes all elements and returns self



40
41
42
43
# File 'lib/weak/set/weak_keys.rb', line 40

def clear
  @map = ObjectSpace::WeakMap.new
  self
end

#delete?(obj) ⇒ self?

Note:

Weak::Set does not test member equality with ‘==` or eql?. Instead, it always checks strict object equality, so that, e.g., different strings are not considered equal, even if they may contain the same string content.

Deletes the given object from self and returns self if it was present in the set. If the object was not in the set, returns nil.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/weak/set/weak_keys.rb', line 46

def delete?(obj)
  return unless include?(obj)

  # If there is a valid entry in the `ObjectSpace::WeakMap`, we replace
  # the value for the `obj` with a temporary DeletedEntry object. As we do
  # not keep any strong reference to this object, this will cause the
  # key/value entry to vanish from the `ObjectSpace::WeakMap` when the
  # `DeletedEntry` object is eventually garbage collected.
  #
  # This ensures that we don't retain unnecessary entries in the map which
  # we would have to skip over.
  @map[obj] = DeletedEntry.new
  self
end

#each {|element| ... } ⇒ self, Enumerator

Calls the given block once for each live element in self, passing that element as a parameter. Returns the weak set itself.

If no block is given, an Enumerator is returned instead.

Yields:

  • (element)

    calls the given block once for each element in self

Yield Parameters:

  • element (Object)

    the yielded value



62
63
64
65
66
67
68
69
# File 'lib/weak/set/weak_keys.rb', line 62

def each
  return enum_for(__method__) { size } unless block_given?

  @map.values.each do |obj|
    yield(obj) unless DeletedEntry === obj
  end
  self
end

#include?(obj) ⇒ Bool

Note:

Weak::Set does not test member equality with ‘==` or eql?. Instead, it always checks strict object equality, so that, e.g., different strings are not considered equal, even if they may contain the same string content.

Returns true if the given object is included in self, false otherwise.



72
73
74
# File 'lib/weak/set/weak_keys.rb', line 72

def include?(obj)
  !!(@map.key?(obj) && @map[obj].equal?(obj))
end

#pruneself

Cleanup data structures from the set to remove data associated with deleted or garbage collected elements. This method may be called automatically for some Weak::Set operations.



77
78
79
# File 'lib/weak/set/weak_keys.rb', line 77

def prune
  self
end

#replace(enum) ⇒ self

Replaces the contents of self with the contents of the given enumerable object and returns self.

Examples:

set = Weak::Set[1, :c, :s]        #=> #<Weak::Set {1, :c, :s}>
set.replace([1, 2])               #=> #<Weak::Set {1, 2}>
set                               #=> #<Weak::Set {1, 2}>


82
83
84
85
86
87
88
89
90
# File 'lib/weak/set/weak_keys.rb', line 82

def replace(enum)
  map = ObjectSpace::WeakMap.new
  do_with_enum(enum) do |obj|
    map[obj] = obj
  end
  @map = map

  self
end

#sizeInteger



93
94
95
96
97
98
99
# File 'lib/weak/set/weak_keys.rb', line 93

def size
  count = 0
  @map.each_value do |obj|
    count = count.succ unless DeletedEntry === obj
  end
  count
end

#to_aArray

Note:

The order of elements on the returned Array is non-deterministic. We do not preserve preserve insertion order.

Returns the live elements contained in self as an Array.



102
103
104
# File 'lib/weak/set/weak_keys.rb', line 102

def to_a
  @map.values.delete_if { |obj| DeletedEntry === obj }
end