Class: Plumb::Not

Inherits:
Object
  • Object
show all
Includes:
Composable
Defined in:
lib/plumb/not.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Composable

#&, #/, #>>, #absorb_input, #absorb_output, #accepted_type, #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, #where, #with, wrap, #|

Methods included from Callable

#parse, #resolve

Constructor Details

#initialize(step = nil, errors: nil) ⇒ Not

Returns a new instance of Not.



25
26
27
28
29
30
31
32
33
# File 'lib/plumb/not.rb', line 25

def initialize(step = nil, errors: nil)
  @step = Composable.wrap(step)
  @errors = errors || "must not be #{step.inspect}"
  # Store the *wrapped* step (as every container does), so `Not[String]` and
  # `Not[Types::String]` are the same node — and so the subtype engine isn't
  # fooled into treating a raw-class child as an atomic leaf.
  @children = [@step].freeze
  freeze
end

Instance Attribute Details

#childrenObject (readonly)

Returns the value of attribute children.



9
10
11
# File 'lib/plumb/not.rb', line 9

def children
  @children
end

#errorsObject (readonly)

Returns the value of attribute errors.



9
10
11
# File 'lib/plumb/not.rb', line 9

def errors
  @errors
end

Class Method Details

.new(step = nil, errors: nil) ⇒ Object

Double negation cancels: Not(Not(X)) is just X — it accepts exactly what X does. So wrapping an existing Not returns the inner type instead of nesting (covering both #not and Not[]). A custom error message keeps the Not, since collapsing would discard it.



15
16
17
18
19
20
# File 'lib/plumb/not.rb', line 15

def self.new(step = nil, errors: nil)
  wrapped = Composable.wrap(step)
  return wrapped.children.first if errors.nil? && wrapped.is_a?(self)

  super
end

Instance Method Details

#[](step) ⇒ Not

Parameters:

  • step (Object)

Returns:



37
38
39
# File 'lib/plumb/not.rb', line 37

def [](step)
  self.class.new(step)
end

#call(result) ⇒ Object



59
60
61
62
63
# File 'lib/plumb/not.rb', line 59

def call(result)
  result = @step.call(result)
  # In-place inversion — no fork here, the cursor is ours to flip.
  result.valid? ? result.invalid!(errors: @errors) : result.valid!
end

#subtype_of?(other) ⇒ Boolean

Negation is contravariant: Not(A) <= Not(B) when B <= A, because excluding a wider set produces a narrower complement.

Parameters:

Returns:

  • (Boolean)


45
46
47
48
49
50
# File 'lib/plumb/not.rb', line 45

def subtype_of?(other)
  return true if self == other
  return Plumb::Subtyping.subtype?(other.children.first, @step) if other.is_a?(Not)

  super
end

#value_preserving?Boolean

Returns true because negation only changes validity.

Returns:

  • (Boolean)

    true because negation only changes validity



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

def value_preserving? = true

#with_children(children) ⇒ Object

See Also:



23
# File 'lib/plumb/not.rb', line 23

def with_children(children) = self.class.new(children.first, errors: errors)