Module: Farce::Abstract::DuplicableMap

Included in:
BoundedMap, ConcurrentMap, TreeMap
Defined in:
lib/farce/abstract/duplicable_map.rb,
lib/farce/integrations/active_support/map.rb,
lib/farce/integrations/active_support/duplicable.rb

Overview

Map operations that return independent maps of the same kind. Include this in subclasses of Map that support copying. Operations work on an independent copy, not an atomic snapshot of the source. Local copies transform the current scope and retain constructor defaults for other scopes.

ActiveSupport Integration collapse

Instance Method Summary collapse

Instance Method Details

#compact ⇒ Map

Return an independent map without entries whose value is nil. False values are retained.

Returns:

  • (Map) —

    A map of the same class with the same settings.



38
39
40
41
42
43
# File 'lib/farce/abstract/duplicable_map.rb', line 38

def compact
  with_map_copy do |copy, map|
    nil_value = copy.wrap_value(nil)
    map.each { |key, value| map.delete(key) if nil_value.equal?(value) }
  end
end

#compact_blank ⇒ Map

Note:

This methods is only available if ActiveSupport has been loaded.

Return a same-kind map without blank values, including false.

Returns:



88
# File 'lib/farce/integrations/active_support/map.rb', line 88

def compact_blank = reject { |_, value| value.blank? }

#deep_dup ⇒ Map

Note:

This methods is only available if ActiveSupport has been loaded.

Deeply copy entries into a map of the same kind with the same settings. String and Symbol keys retain their identity, as with ActiveSupport's Hash#deep_dup. Copied keys and values must satisfy the map's normal storage rules.

Returns:



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/farce/integrations/active_support/map.rb', line 56

def deep_dup
  copier = ModeManager.new(mode: :copy) if respond_to?(:mode) && mode == :move
  with_map_copy(empty: true) do |copy, map|
    internal_map.each do |key, value|
      key = key.deep_dup unless String === key || Symbol === key
      value = unwrap_value(value).deep_dup
      value = copier.unwrap(copier.wrap(value)) if copier
      map[key] = copy.wrap_value(value)
    end
  end
end

#duplicable? ⇒ Boolean

Note:

This methods is only available if ActiveSupport has been loaded.

Returns true.

Returns:

  • (Boolean) —

    true



55
# File 'lib/farce/integrations/active_support/duplicable.rb', line 55

def duplicable? = true

#except(*keys) ⇒ Map

Return an independent map without the requested keys.

Parameters:

  • keys (Array<BasicObject>) —

    keys to omit, using the map's lookup rules

Returns:

  • (Map) —

    A map of the same class with the same settings.



30
31
32
33
34
# File 'lib/farce/abstract/duplicable_map.rb', line 30

def except(*keys)
  with_map_copy do |_, map|
    keys.each { map.delete(normalize_copied_key(it)) }
  end
end

#flatten(level = 1) ⇒ Array

Return keys and values in a flattened Array. The default depth flattens entry pairs. Zero preserves pairs, and negative depths flatten all levels.

Parameters:

  • level (Integer, #to_int) (defaults to: 1) —

    the number of Array levels to flatten

Returns:

  • (Array) —

    The flattened entries.



170
171
172
173
# File 'lib/farce/abstract/duplicable_map.rb', line 170

def flatten(level = 1)
  level = Integer.try_convert(level) || raise(TypeError, "level must be an Integer or respond to #to_int")
  to_a.flatten(level)
end

#invert ⇒ Map

Return an independent map with keys and values exchanged. New keys follow the map's normal key normalization and shareability rules. On collisions, the last visited entry wins. Bounded copies rebuild eviction history.

Returns:

  • (Map) —

    A map of the same class with the same settings.

Raises:



121
122
123
124
125
126
127
128
129
# File 'lib/farce/abstract/duplicable_map.rb', line 121

def invert
  copier = ModeManager.new(mode: :copy) if respond_to?(:mode) && mode == :move
  empty_copy.tap do |copy|
    each_pair do |key, value|
      key = copier.unwrap(copier.wrap(key)) if copier
      copy[value] = key
    end
  end
end

#merge(*others) {|key, old_value, new_value| ... } ⇒ Map

Return an independent map with entries from each input applied in order. Incoming keys pass through the normalizer. A block resolves collisions using the canonical key. Move-mode values are copied before transfer to preserve the source and inputs. Bounded copies retain eviction history and apply their usual capacity limits to new writes.

Parameters:

  • others (Array<Map, Hash, #to_hash>) —

    maps to merge, with later entries taking precedence

Yield Parameters:

  • key (BasicObject) —

    the canonical key shared by both maps

  • old_value (BasicObject) —

    the current value in the result

  • new_value (BasicObject) —

    the incoming value

Yield Returns:

  • (BasicObject) —

    the replacement value

Returns:

  • (Map) —

    A map of the same class with the same settings.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/farce/abstract/duplicable_map.rb', line 141

def merge(*others)
  copier = ModeManager.new(mode: :copy) if respond_to?(:mode) && mode == :move
  with_map_copy do |copy, map|
    others.each do |other|
      unless Map === other
        other = Hash.try_convert(other) || raise(TypeError, "input must be a Map, Hash, or respond to #to_hash")
      end
      other.each_pair do |key, value|
        key = normalize_copied_key(key)
        value = yield(key, copy.unwrap_value(map[key]), value) if block_given? && map.key?(key)
        value = copier.unwrap(copier.wrap(value)) if copier
        map[key] = copy.wrap_value(value)
      end
    end
  end
end

#reject {|key, value| ... } ⇒ Map, Enumerator

Return an independent map excluding entries accepted by the block. The block receives public values from the copy. Stored values and map settings are preserved.

Yield Parameters:

  • key (BasicObject) —

    an existing key

  • value (BasicObject) —

    the public value

Returns:

  • (Map, Enumerator) —

    A map of the same class, or an Enumerator without a block.



63
64
65
66
67
68
# File 'lib/farce/abstract/duplicable_map.rb', line 63

def reject
  return enum_for(__method__) { size } unless block_given?
  with_map_copy do |copy, map|
    map.each { |key, value| map.delete(key) if yield(key, copy.unwrap_value(value)) }
  end
end

#reverse_merge(other) ⇒ Map Also known as: with_defaults

Note:

This methods is only available if ActiveSupport has been loaded.

Return a same-kind map with defaults applied before the receiver's entries. Existing entries win. Bounded copies rebuild eviction history and enforce their capacity. Move-mode defaults are copied so the input remains usable.

Parameters:

  • other (Map, Hash, #to_hash) —

    default entries

Returns:



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/farce/integrations/active_support/map.rb', line 96

def reverse_merge(other)
  unless Map === other
    other = Hash.try_convert(other) || raise(TypeError, "input must be a Map, Hash, or respond to #to_hash")
  end
  copier = ModeManager.new(mode: :copy) if respond_to?(:mode) && mode == :move
  with_map_copy(empty: true) do |copy, map|
    other.each_pair do |key, value|
      key = normalize_copied_key(key)
      value = copier.unwrap(copier.wrap(value)) if copier
      map[key] = copy.wrap_value(value)
    end
    internal_map.each { |key, value| map[key] = value }
  end
end

#select {|key, value| ... } ⇒ Map, Enumerator Also known as: filter

Return an independent map containing entries accepted by the block. The block receives public values from the copy. Stored values and map settings are preserved.

Yield Parameters:

  • key (BasicObject) —

    an existing key

  • value (BasicObject) —

    the public value

Returns:

  • (Map, Enumerator) —

    A map of the same class, or an Enumerator without a block.



50
51
52
53
54
55
# File 'lib/farce/abstract/duplicable_map.rb', line 50

def select
  return enum_for(__method__) { size } unless block_given?
  with_map_copy do |copy, map|
    map.each { |key, value| map.delete(key) unless yield(key, copy.unwrap_value(value)) }
  end
end

#slice(*keys) ⇒ Map

Return an independent map containing only the requested keys that exist.

Parameters:

  • keys (Array<BasicObject>) —

    keys to retain, using the map's lookup rules

Returns:

  • (Map) —

    A map of the same class with the same settings.



15
16
17
18
19
20
21
22
23
24
25
# File 'lib/farce/abstract/duplicable_map.rb', line 15

def slice(*keys)
  with_map_copy do |_, map|
    retained = {}.compare_by_identity
    keys.each do |key|
      key = normalize_copied_key(key)
      retained[map.getkey(key)] = true if map.key?(key)
    end
    # Tree backends expose each, but not each_key.
    map.each { |key, _| map.delete(key) unless retained.key?(key) } # rubocop:disable Style/HashEachMethods
  end
end

#stringify_keys ⇒ Map

Note:

This methods is only available if ActiveSupport has been loaded.

Return a same-kind map with string keys, subject to its key normalizer.

Returns:



71
# File 'lib/farce/integrations/active_support/map.rb', line 71

def stringify_keys = transform_keys { |key| Symbol === key ? key.name : key.to_s }

#symbolize_keys ⇒ Map Also known as: to_options

Note:

This methods is only available if ActiveSupport has been loaded.

Return a same-kind map with symbol keys where conversion succeeds.

Returns:



76
77
78
79
80
81
82
# File 'lib/farce/integrations/active_support/map.rb', line 76

def symbolize_keys
  transform_keys do |key|
    key.to_sym
  rescue StandardError
    key
  end
end

#to_proc ⇒ Proc

Return a lambda that looks up a key using this map's current contents and lookup rules. The lambda is Ractor-shareable when this map is Ractor-shareable.

Returns:

  • (Proc) —

    A one-argument lookup lambda.



161
162
163
164
# File 'lib/farce/abstract/duplicable_map.rb', line 161

def to_proc
  lookup = ->(key) { self[key] }
  Ractor.shareable?(self) ? Ractor.shareable_lambda(self: self, &lookup) : lookup
end

#transform_keys(mapping = UNDEFINED) {|key| ... } ⇒ Map, Enumerator

Return an independent map with transformed keys and unchanged stored values. Mapping entries take precedence over the block. Unmapped keys are retained when no block is given. Transformed keys pass through this map's normalizer. On collisions, the last visited entry wins. Bounded copies rebuild eviction history as transformed entries are inserted.

Parameters:

  • mapping (Hash, #to_hash) (defaults to: UNDEFINED) —

    optional replacements for existing keys

Yield Parameters:

  • key (BasicObject) —

    an existing key

Yield Returns:

  • (BasicObject) —

    the replacement key

Returns:

  • (Map, Enumerator) —

    A map of the same class, or an Enumerator without a mapping or block.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/farce/abstract/duplicable_map.rb', line 78

def transform_keys(mapping = UNDEFINED)
  if UNDEFINED.equal?(mapping)
    return enum_for(__method__, mapping) { size } unless block_given?
    mapping = nil
  else
    mapping = Hash.try_convert(mapping) || raise(TypeError, "mapping must be a Hash or respond to #to_hash")
  end

  with_map_copy(empty: true) do |_, map|
    internal_map.each do |key, value|
      if mapping&.key?(key)
        key = normalize_copied_key(mapping.fetch(key))
      elsif block_given?
        key = normalize_copied_key(yield(key))
      end
      map[key] = value
    end
  end
end

#transform_values {|value| ... } ⇒ Map, Enumerator

Return an independent map with transformed values and unchanged keys. Results follow the map's transfer mode. Move-mode results are copied before transfer to preserve the source. Bounded copies count replacements as writes. The source's eviction history is unchanged.

Yield Parameters:

  • value (BasicObject) —

    the current value

Yield Returns:

  • (BasicObject) —

    the replacement value

Returns:

  • (Map, Enumerator) —

    A map of the same class, or an Enumerator without a block.



104
105
106
107
108
109
110
111
112
113
114
# File 'lib/farce/abstract/duplicable_map.rb', line 104

def transform_values
  return enum_for(__method__) { size } unless block_given?
  copier = ModeManager.new(mode: :copy) if respond_to?(:mode) && mode == :move
  with_map_copy do |copy, map|
    map.each do |key, value|
      value = yield copy.unwrap_value(value)
      value = copier.unwrap(copier.wrap(value)) if copier
      map[key] = copy.wrap_value(value)
    end
  end
end

#with_indifferent_access ⇒ Map

Note:

This methods is only available if ActiveSupport has been loaded.

Return a new map of the same class with interchangeable Symbol and String keys. Other key types and nested hashes are not normalized. Existing normalization is replaced. The copy preserves its value mode, value comparison, capacity, and Local scope where supported. Keys use equality. Entries from the current scope seed a Local copy. Values in move mode are copied to preserve the source.

Returns:

  • (Map) —

    An independent map with indifferent key access.



46
47
48
49
# File 'lib/farce/integrations/active_support/map.rb', line 46

def with_indifferent_access
  normalizer = Ractor.shareable_proc { |key| Symbol === key ? key.name : key }
  build_indifferent_access(**indifferent_access_options, normalize_keys: normalizer)
end