Class: Farce::Abstract::BoundedMap Abstract

Inherits:
Map
  • Object
show all
Includes:
DuplicableMap
Defined in:
lib/farce/abstract/bounded_map.rb,
lib/farce/integrations/psych.rb,
lib/farce/integrations/active_support/map.rb

Overview

This class is abstract.

Superclass for maps that retain at most a configured number of entries.

Successful individual value reads and writes update the map's eviction policy. Observational operations such as iteration, #key?, and #getkey do not. Copies preserve capacity and eviction history without counting copying as an access.

Direct Known Subclasses

LFUMap, LRUMap

Instance Method Summary collapse

Methods included from DuplicableMap

#compact, #except, #flatten, #invert, #merge, #reject, #select, #slice, #to_proc, #transform_keys, #transform_values

Methods inherited from Map

#as_extended_json, #assoc, #bson_type, #compare_by_identity?, #deconstruct_keys, #dig, #empty?, #fetch_values, #has_key?, #key, #rassoc, #shareable_keys?, #shareable_values?, #store, #to_a, #to_bson, #to_bson_normalized_value, #to_cbor, #to_h, #to_hash, #to_json, #to_s, #value?, #values_at, #weak_keys?, #weak_values?

Constructor Details

#initialize(entries = nil, max_size:, normalize_keys: nil, compare_by_identity: false, compare_keys_by_identity: compare_by_identity, compare_values_by_identity: compare_by_identity) ⇒ BoundedMap

Returns a new instance of BoundedMap.

Parameters:

  • entries (Hash, Array<Array(BasicObject, BasicObject)>, Map, #each, nil) (defaults to: nil) —

    Optional initial entries. Entries are stored sequentially and may be evicted.

  • max_size (Integer) —

    Maximum number of retained entries.

  • normalize_keys (Symbol, Proc, Hash, Farce::Abstract::Map, nil) (defaults to: nil) —

    Converts incoming keys to their canonical stored form:

    • If a Symbol is provided, it will be used as a method name to call on each key.
    • If a Proc is provided, it will be called with each key and should return the normalized key.
    • If a Hash or Map is provided, it will be used to look up the normalized key for each incoming key.
  • compare_by_identity (Boolean) (defaults to: false) —

    Whether keys and values are compared by identity.

  • compare_keys_by_identity (Boolean) (defaults to: compare_by_identity) —

    Whether keys are compared by identity.

  • compare_values_by_identity (Boolean) (defaults to: compare_by_identity) —

    Whether values are compared by identity.



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
# File 'lib/farce/abstract/bounded_map.rb', line 22

def initialize(
  entries = nil,
  max_size:,
  normalize_keys: nil,
  compare_by_identity: false,
  compare_keys_by_identity: compare_by_identity,
  compare_values_by_identity: compare_by_identity
)
  entries = convert_entries(entries)
  @map    = new_bounded_map(
    max_size:,
    compare_by_identity:,
    compare_keys_by_identity:,
    compare_values_by_identity:,
  )
  @key_locks = new_key_locks(compare_keys_by_identity:)
  restoring  = Internal::KeyNormalizer.restoration?(normalize_keys)
  normalizer = Internal::KeyNormalizer.build(
    normalize_keys,
    shareable: normalize_keys && Internal::KeyNormalizer.shareable_target?(self),
  )
  Internal::KeyNormalizer.install(self, normalizer, Internal::KeyNormalizer::BoundedOperations) unless restoring
  entries&.each { self[_1] = _2 }
  Internal::KeyNormalizer.install(self, normalizer, Internal::KeyNormalizer::BoundedOperations) if restoring
  super()
end

Instance Method Details

#[](key) ⇒ BasicObject?

This method is abstract.

Look up a key without waiting for atomic-update access.

Parameters:

  • key (BasicObject) —

    The key to look up.

Returns:

  • (BasicObject, nil) —

    The associated value, or nil if the key is absent.



50
# File 'lib/farce/abstract/bounded_map.rb', line 50

def [](key) = unwrap_value(internal_map[prepare_key(key)])

#[]=(key, value) ⇒ BasicObject

This method is abstract.

Associate a value with a key without a timeout.

Parameters:

  • key (BasicObject) —

    The key to store.

  • value (BasicObject) —

    The value to store.

Returns:

  • (BasicObject) —

    value.



53
54
55
56
57
# File 'lib/farce/abstract/bounded_map.rb', line 53

def []=(key, value)
  key = prepare_store_key(key)
  with_key_lock(key) { internal_map[key] = wrap_value(value) }
  value
end

#clear ⇒ self

This method is abstract.

Remove all entries from the map.

Returns:

  • (self)


93
94
95
96
# File 'lib/farce/abstract/bounded_map.rb', line 93

def clear
  internal_map.clear
  self
end

#compare_keys_by_identity? ⇒ Boolean

This method is abstract.

Returns Whether keys are compared by identity instead of hash and eql?.

Returns:

  • (Boolean) —

    Whether keys are compared by identity instead of hash and eql?.



99
# File 'lib/farce/abstract/bounded_map.rb', line 99

def compare_keys_by_identity? = internal_map.compare_keys_by_identity?

#compare_values_by_identity? ⇒ Boolean

This method is abstract.

Returns Whether values are compared by identity instead of equality.

Returns:

  • (Boolean) —

    Whether values are compared by identity instead of equality.



102
# File 'lib/farce/abstract/bounded_map.rb', line 102

def compare_values_by_identity? = internal_map.compare_values_by_identity?

#delete(key) ⇒ BasicObject?

This method is abstract.

Remove a key and its associated value.

Parameters:

  • key (BasicObject) —

    The key to remove.

Returns:

  • (BasicObject, nil) —

    The removed value, or nil if the key was absent.



105
# File 'lib/farce/abstract/bounded_map.rb', line 105

def delete(key) = unwrap_value(internal_map.delete(prepare_key(key)))

#each {|pair| ... } ⇒ self #each ⇒ Enumerator Also known as: each_pair

Iterate over a snapshot without updating eviction history.

Overloads:

  • #each {|pair| ... } ⇒ self

    Yields:

    • (pair) —

      Called once for each entry.

    Yield Parameters:

    • pair (Array<BasicObject>) —

      A two-element [key, value] pair.

    Returns:

    • (self)
  • #each ⇒ Enumerator

    Returns:

    • (Enumerator)


165
166
167
168
169
# File 'lib/farce/abstract/bounded_map.rb', line 165

def each
  return enum_for(__method__) unless block_given?
  internal_map.each { |key, value| yield [key, unwrap_value(value)] }
  self
end

#each_key ⇒ self, Enumerator

Iterate over a snapshot of stored keys without updating eviction history.

Returns:

  • (self, Enumerator)


174
175
176
177
178
# File 'lib/farce/abstract/bounded_map.rb', line 174

def each_key
  return enum_for(__method__) unless block_given?
  internal_map.each_key { yield it }
  self
end

#each_value ⇒ self, Enumerator

Iterate over a snapshot of stored values without updating eviction history.

Returns:

  • (self, Enumerator)


182
183
184
185
186
# File 'lib/farce/abstract/bounded_map.rb', line 182

def each_value
  return enum_for(__method__) unless block_given?
  internal_map.each_value { yield unwrap_value(it) }
  self
end

#fetch(key) ⇒ BasicObject #fetch(key, default) ⇒ BasicObject #fetch(key) {|key| ... } ⇒ BasicObject

This method is abstract.

Fetch the value associated with a key, using the same missing-key behavior as Hash#fetch. If both a default and a block are provided, the block takes precedence and a warning is emitted.

Overloads:

  • #fetch(key) ⇒ BasicObject

    Returns The associated value.

    Parameters:

    • key (BasicObject) —

      The key to look up.

    Returns:

    • (BasicObject) —

      The associated value.

    Raises:

    • (KeyError) —

      If the key is absent.

  • #fetch(key, default) ⇒ BasicObject

    Returns The associated value or default.

    Parameters:

    • key (BasicObject) —

      The key to look up.

    • default (BasicObject) —

      The value to return if the key is absent.

    Returns:

    • (BasicObject) —

      The associated value or default.

  • #fetch(key) {|key| ... } ⇒ BasicObject

    Returns The associated value or the block result.

    Parameters:

    • key (BasicObject) —

      The key to look up.

    Yields:

    • (key) —

      Called if the key is absent.

    Yield Parameters:

    • key (BasicObject) —

      The missing key.

    Yield Returns:

    • (BasicObject) —

      The value to return.

    Returns:

    • (BasicObject) —

      The associated value or the block result.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/farce/abstract/bounded_map.rb', line 108

def fetch(*arguments)
  unless arguments.length.between?(1, 2)
    raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
  end

  key, default = arguments
  prepared_key = prepare_key(key)
  warn "block supersedes default value argument", uplevel: 1 if block_given? && arguments.length == 2
  value = internal_map.fetch(prepared_key) do
    return yield(key) if block_given?
    return default if arguments.length == 2
    raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
  end
  unwrap_value(value)
end

#getkey(key) ⇒ BasicObject?

This method is abstract.

Return the stored key that matches a lookup key.

Parameters:

  • key (BasicObject) —

    The key to match.

Returns:

  • (BasicObject, nil) —

    The matching stored key, or nil if no key matches.



125
# File 'lib/farce/abstract/bounded_map.rb', line 125

def getkey(key) = internal_map.getkey(prepare_key(key))

#key?(key) ⇒ Boolean

This method is abstract.

Test whether a key is present, including when its associated value is nil.

Parameters:

  • key (BasicObject) —

    The key to look up.

Returns:

  • (Boolean) —

    Whether the key is present.



128
# File 'lib/farce/abstract/bounded_map.rb', line 128

def key?(key) = internal_map.key?(prepare_key(key))

#keys ⇒ Array<BasicObject>

Return a frozen snapshot of stored keys.

Returns:

  • (Array<BasicObject>)


190
# File 'lib/farce/abstract/bounded_map.rb', line 190

def keys = each_key.to_a.freeze

#max_size ⇒ Integer

Return the maximum number of entries retained by the current backing map.

Returns:

  • (Integer)


132
# File 'lib/farce/abstract/bounded_map.rb', line 132

def max_size = internal_map.max_size

#max_size=(limit) ⇒ Integer

Change the maximum number of retained entries and immediately evict any excess.

Parameters:

  • limit (Integer) —

    The new non-negative capacity.

Returns:

  • (Integer) —

    limit



137
138
139
# File 'lib/farce/abstract/bounded_map.rb', line 137

def max_size=(limit)
  internal_map.max_size = limit
end

#prune(to:) ⇒ Integer

Remove entries selected by the eviction policy until at most to remain. This does not change #max_size.

Parameters:

  • to (Integer) —

    The non-negative target size.

Returns:

  • (Integer) —

    Number of entries removed.



145
# File 'lib/farce/abstract/bounded_map.rb', line 145

def prune(to:) = internal_map.prune(to:)

#shift ⇒ Array(BasicObject, BasicObject)?

Remove and return the next entry selected by the eviction policy.

Returns:

  • (Array(BasicObject, BasicObject), nil)


149
150
151
152
# File 'lib/farce/abstract/bounded_map.rb', line 149

def shift
  pair = internal_map.shift
  [pair.first, unwrap_value(pair.last)] if pair
end

#size ⇒ Integer Also known as: length

This method is abstract.

Return the number of entries currently in the map.

Returns:

  • (Integer) —

    The number of entries.



155
# File 'lib/farce/abstract/bounded_map.rb', line 155

def size = internal_map.size

#store_if_absent(key) ⇒ BasicObject

Return an existing value, or store the block result for an absent key. Coordinated implementations share one initialization among concurrent callers for equal keys. Unsafe implementations can run competing loaders. The block runs without holding the map's structural lock, so other keys remain accessible. Coordinated assignment waits for initialization. Deletion or clearing can precede a pending initialization's insertion. At zero capacity, coordinated implementations run equal-key loaders sequentially. Each loader validates and transfers its result, but the map retains no value.

Parameters:

  • key (BasicObject) —

    The key to retrieve or initialize.

Yield Returns:

  • (BasicObject) —

    The value to store.

Returns:

  • (BasicObject) —

    The existing or newly stored value.

Raises:

  • (LocalJumpError) —

    If no block is given, even when the key exists.

  • (ThreadError) —

    If coordinated initialization recursively accesses its own gate.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/farce/abstract/bounded_map.rb', line 73

def store_if_absent(key)
  raise LocalJumpError, "no block given" unless block_given?

  map    = internal_map
  found  = true
  stored = map.fetch(prepare_key(key)) { found = false }
  return unwrap_value(stored) if found

  key = prepare_store_key(key)
  with_key_lock(key) do
    stored     = map.fetch(key) do
      wrapped  = wrap_value(yield)
      map[key] = wrapped
      return unwrap_value(wrapped)
    end
    unwrap_value(stored)
  end
end

#values ⇒ Array<BasicObject>

Return a frozen snapshot of stored values.

Returns:

  • (Array<BasicObject>)


194
# File 'lib/farce/abstract/bounded_map.rb', line 194

def values = each_value.to_a.freeze