Class: Plumb::TaggedHash
- Inherits:
-
Object
- Object
- Plumb::TaggedHash
- Includes:
- Composable
- Defined in:
- lib/plumb/tagged_hash.rb
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
Returns the value of attribute children.
-
#hash_type ⇒ Object
readonly
Returns the value of attribute hash_type.
-
#key ⇒ Object
readonly
Returns the value of attribute key.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Equality includes the discriminator and base hash because #children contains only variants; comparing children alone would conflate distinct tagged hashes.
-
#accepted_type ⇒ TaggedHash
Relaxes each variant's non-discriminator fields to their accepted types.
- #call(result) ⇒ Object
-
#initialize(hash_type, key, children) ⇒ TaggedHash
constructor
A new instance of TaggedHash.
-
#subtype_of?(other) ⇒ Boolean
A tagged hash is a subtype when every selectable variant is a subtype.
- #with_children(children) ⇒ Object
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, #output_type, #pipeline, #policy, resolve_operand, #static, #subtype_identity, #to_json_schema, #to_mermaid, #to_plumb_type, #to_s, #transform, #value, #value_preserving?, #where, #with, wrap, #|
Methods included from Callable
Constructor Details
#initialize(hash_type, key, children) ⇒ TaggedHash
Returns a new instance of TaggedHash.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/plumb/tagged_hash.rb', line 14 def initialize(hash_type, key, children) @hash_type = hash_type @key = Key.wrap(key) @children = children raise ArgumentError, 'all types must be HashClass' if @children.size.zero? || @children.any? do |t| !t.is_a?(HashClass) end raise ArgumentError, "all types must define key #{@key}" unless @children.all? { |t| !!t.at_key(@key) } # types are assumed to have literal values for the index field :key @index = @children.each.with_object({}) do |t, memo| key_type = t.at_key(@key) # Accept either bare or base-constrained single-value tags. tag = Plumb::Subtyping.literal_value(key_type) if Undefined.equal?(tag) raise ParseError, "key type at :#{@key} #{key_type} must match a single literal value" end memo[tag] = t end freeze end |
Instance Attribute Details
#children ⇒ Object (readonly)
Returns the value of attribute children.
9 10 11 |
# File 'lib/plumb/tagged_hash.rb', line 9 def children @children end |
#hash_type ⇒ Object (readonly)
Returns the value of attribute hash_type.
9 10 11 |
# File 'lib/plumb/tagged_hash.rb', line 9 def hash_type @hash_type end |
#key ⇒ Object (readonly)
Returns the value of attribute key.
9 10 11 |
# File 'lib/plumb/tagged_hash.rb', line 9 def key @key end |
Instance Method Details
#==(other) ⇒ Boolean
Equality includes the discriminator and base hash because #children contains only variants; comparing children alone would conflate distinct tagged hashes.
43 44 45 46 47 48 |
# File 'lib/plumb/tagged_hash.rb', line 43 def ==(other) other.instance_of?(self.class) && key.eql?(other.key) && hash_type == other.hash_type && children == other.children end |
#accepted_type ⇒ TaggedHash
Relaxes each variant's non-discriminator fields to their accepted types.
63 64 65 66 67 68 69 70 71 |
# File 'lib/plumb/tagged_hash.rb', line 63 def accepted_type relaxed = @children.map do |child| schema = child._schema.each_with_object({}) do |(k, field), h| h[k] = k.eql?(@key) ? field : Plumb::Subtyping.accepted_type(field) end child.class.new(schema:) end self.class.new(@hash_type, @key, relaxed) end |
#call(result) ⇒ Object
73 74 75 76 77 78 79 80 81 |
# File 'lib/plumb/tagged_hash.rb', line 73 def call(result) result = @hash_type.call(result) return result unless result.valid? child = @index[result.value[@key.to_sym]] return result.invalid!(errors: "expected :#{@key.to_sym} to be one of #{@index.keys.join(', ')}") unless child child.call(result) end |
#subtype_of?(other) ⇒ Boolean
A tagged hash is a subtype when every selectable variant is a subtype. The base hash only narrows those variants, so ignoring it here is conservative; comparing variants pairwise would be unsound across different discriminators.
55 56 57 58 59 |
# File 'lib/plumb/tagged_hash.rb', line 55 def subtype_of?(other) return true if self == other @children.all? { |variant| Plumb::Subtyping.subtype?(variant, other) } end |
#with_children(children) ⇒ Object
12 |
# File 'lib/plumb/tagged_hash.rb', line 12 def with_children(children) = self.class.new(hash_type, key, children) |