Class: Plumb::HashMap

Inherits:
Object
  • Object
show all
Includes:
Composable, CovariantFusion
Defined in:
lib/plumb/hash_map.rb

Defined Under Namespace

Classes: FilteredHashMap

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from CovariantFusion

#fuse_with

Methods included from Composable

#&, #/, #>>, #[], #absorb_input, #absorb_output, #as_node, #build, #check, #defer, #fusable_step?, #fuse_with, #generate, #idempotent?, included, #input_type, #invalid, #invoke, #match, #metadata, #not, #pipeline, #policy, resolve_operand, #static, #subtype_identity, #to_json_schema, #to_mermaid, #to_plumb_type, #to_s, #transform, #value, #where, #with, wrap, #|

Methods included from Callable

#parse, #resolve

Constructor Details

#initialize(key_type, value_type) ⇒ HashMap

Returns a new instance of HashMap.



12
13
14
15
16
17
# File 'lib/plumb/hash_map.rb', line 12

def initialize(key_type, value_type)
  @key_type = key_type
  @value_type = value_type
  @children = [key_type, value_type].freeze
  freeze
end

Instance Attribute Details

#childrenObject (readonly)

Returns the value of attribute children.



10
11
12
# File 'lib/plumb/hash_map.rb', line 10

def children
  @children
end

Instance Method Details

#accepted_typeObject

As a consumer, a HashMap accepts keys/values relaxed to what its key and value types accept (see HashClass#accepted_type).



49
# File 'lib/plumb/hash_map.rb', line 49

def accepted_type = Plumb::Subtyping.map_children(self) { |c| Plumb::Subtyping.accepted_type(c) }

#call(result) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/plumb/hash_map.rb', line 55

def call(result)
  return result.invalid!(errors: 'must be a Hash') unless result.value.is_a?(::Hash)

  errors = {}

  parsed = result.value.each.with_object({}) do |(key, value), memo|
    key_r = @key_type.resolve(key)
    value_r = @value_type.resolve(value)
    errs = []
    errs << "key #{key_r.errors}" unless key_r.valid?
    errs << "value #{value_r.value.inspect} #{value_r.errors}" unless value_r.valid?
    errors[key] = errs unless errs.empty?
    memo[key_r.value] = value_r.value
  end

  errors.empty? ? result.valid!(parsed) : result.invalid!(errors:)
end

#filteredObject



73
74
75
# File 'lib/plumb/hash_map.rb', line 73

def filtered
  FilteredHashMap.new(@key_type, @value_type)
end

#output_typeObject

The value you GET after re-mapping each key/value: both resolved to what they produce (mirror of #accepted_type).



53
# File 'lib/plumb/hash_map.rb', line 53

def output_type = Plumb::Subtyping.map_children(self) { |c| Plumb::Subtyping.resolved_output(c) }

#subtype_of?(other) ⇒ Boolean

A HashMap is a Hash, so it is a subtype of the "any Hash" top — an empty-schema HashClass like Types::Hash — or of an open catch-all-only HashClass Hash[_: V] when this map's value type is a subtype of V (the catch-all's Any key admits any map key). It is NOT a subtype of a HashClass that requires specific keys a map doesn't guarantee. Against another HashMap it is covariant in key and value types (handled by the default #subtype_of?).

Returns:

  • (Boolean)


25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/plumb/hash_map.rb', line 25

def subtype_of?(other)
  if other.is_a?(HashClass)
    return true if other._schema.empty?

    # Maps constrain every value but do not guarantee any named key is present.
    return false if other.literal_fields.any? { |k, _| !k.optional? }

    return other._schema.all? { |_k, field| Plumb::Subtyping.subtype?(@value_type, field) }
  end

  super
end

#value_preserving?Boolean

A HashMap re-maps each key and value through its key/value types, so it preserves the value only when both do (a coercing key or value would change the hash).

Returns:

  • (Boolean)


41
# File 'lib/plumb/hash_map.rb', line 41

def value_preserving? = children.all? { |c| Plumb::Subtyping.value_preserving?(c) }

#with_children(children) ⇒ Object

Rebuild around new children (see Plumb::Subtyping.map_children). self.class (not HashMap) preserves FilteredHashMap's leniency.



45
# File 'lib/plumb/hash_map.rb', line 45

def with_children(children) = self.class.new(children[0], children[1])