Module: Rigor::Effects::Label

Defined in:
lib/rigor/effects/label.rb

Overview

The effect-label grammar and the subsumption relation over it (ADR-103 WD1; normative in docs/type-specification/effect-labels.md).

A label is a dot-path of lowercase segments — io, io.net.http, nondet.time. The relation that matters is segment-aware prefix subsumption: io admits io.net.http and rejects iota. Every method here is pure and total; a malformed input is answered, never raised on, so a caller can ask valid? and the rest in either order.

Constant Summary collapse

PATTERN =

label = segment { "." segment }, segment = [a-z][a-z0-9]*. Deliberately narrow: no underscores, no hyphens, no uppercase, no empty segment, no trailing dot. The same grammar is the RFC's label production, so a label spells identically in Steins and Rigor.

/\A[a-z][a-z0-9]*(?:\.[a-z][a-z0-9]*)*\z/

Class Method Summary collapse

Class Method Details

.ancestors(label) ⇒ Object

The label's proper ancestors, outermost first and excluding the label itself: "io.net.http" -> ["io", "io.net"]. A root has no ancestors.



58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/rigor/effects/label.rb', line 58

def ancestors(label)
  parts = segments(label)
  return [].freeze if parts.length <= 1

  result = []
  prefix = nil
  parts[0...-1].each do |segment|
    prefix = prefix ? "#{prefix}#{SEPARATOR}#{segment}" : segment
    result << prefix
  end
  result.freeze
end

.parent(label) ⇒ Object

The label one segment shallower, or nil for a root (and for a malformed label).



47
48
49
50
51
52
53
54
# File 'lib/rigor/effects/label.rb', line 47

def parent(label)
  return nil unless valid?(label)

  index = label.rindex(SEPARATOR)
  return nil unless index

  label[0, index]
end

.root(label) ⇒ Object

The label's outermost segment — the root whose ownership the registry checks.



72
73
74
# File 'lib/rigor/effects/label.rb', line 72

def root(label)
  segments(label).first
end

.segments(label) ⇒ Object

The label's segments, outermost first: "io.net.http" -> ["io", "net", "http"]. A malformed label yields an empty array rather than a partial parse.



30
31
32
33
34
# File 'lib/rigor/effects/label.rb', line 30

def segments(label)
  return [].freeze unless valid?(label)

  label.split(SEPARATOR).freeze
end

.subsumes?(bound, label) ⇒ Boolean

Whether bound admits label under segment-aware prefix subsumption. A label subsumes itself; io subsumes io.net.http; io does NOT subsume iota, because the match is on segment boundaries and not on characters.

Returns:

  • (Boolean)


39
40
41
42
43
44
# File 'lib/rigor/effects/label.rb', line 39

def subsumes?(bound, label)
  return false unless valid?(bound) && valid?(label)
  return true if bound == label

  label.start_with?("#{bound}#{SEPARATOR}")
end

.valid?(str) ⇒ Boolean

Whether str is a well-formed label. Anything that is not a String is not.

Returns:

  • (Boolean)


24
25
26
# File 'lib/rigor/effects/label.rb', line 24

def valid?(str)
  str.is_a?(String) && PATTERN.match?(str)
end