Class: Plumb::Key

Inherits:
Object
  • Object
show all
Defined in:
lib/plumb/key.rb

Overview

A hash-schema key. Two flavours:

* a LITERAL key — a Symbol or String name (`name:`, `'name' =>`). It is
matched by exact lookup and carries `#to_key`/`#to_sym`. A trailing `?`
(`name?:`) marks it optional. This is the common case and behaves exactly
as before.
* a MATCHER key — any Plumb type / `#===` object used as a key
(`Types::String[/^id_/] => ...`, or the `_` catch-all which wraps
`Types::Any`). It matches other keys via `matcher === other_key`, has no
single `#to_key`, and is inherently optional (it imposes no specific
required key).

Constant Summary collapse

OPTIONAL_EXP =

OPTIONAL_EXP = /(\w+)(?)?$/

/(?<word>[A-Za-z0-9_$]+)(?<qmark>\?)?/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, optional: false, symbolize: false) ⇒ Key

Returns a new instance of Key.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/plumb/key.rb', line 27

def initialize(key, optional: false, symbolize: false)
  @node_name = :key
  if key.is_a?(::Symbol) || key.is_a?(::String)
    key_type = symbolize ? Symbol : key.class
    match = OPTIONAL_EXP.match(key.to_s)
    name = match[:word]
    @to_key = key_type == Symbol ? name.to_sym : name
    @to_sym = @to_key.to_sym
    @optional = !match[:qmark].nil? ? true : optional
    @matcher = @to_key
    @literal = true
  else
    # A type/matcher key. It matches other keys structurally, so it has no
    # concrete #to_key and never imposes a required key.
    @matcher = Composable.wrap(key)
    @to_key = nil
    @to_sym = nil
    @optional = true
    @literal = false
  end
  freeze
end

Instance Attribute Details

#matcherObject (readonly)

Returns the value of attribute matcher.



25
26
27
# File 'lib/plumb/key.rb', line 25

def matcher
  @matcher
end

#node_nameObject (readonly)

Returns the value of attribute node_name.



25
26
27
# File 'lib/plumb/key.rb', line 25

def node_name
  @node_name
end

#to_keyObject (readonly)

Returns the value of attribute to_key.



25
26
27
# File 'lib/plumb/key.rb', line 25

def to_key
  @to_key
end

#to_symObject (readonly)

Returns the value of attribute to_sym.



25
26
27
# File 'lib/plumb/key.rb', line 25

def to_sym
  @to_sym
end

Class Method Details

.wrap(key, symbolize: false) ⇒ Object



21
22
23
# File 'lib/plumb/key.rb', line 21

def self.wrap(key, symbolize: false)
  key.is_a?(Key) ? key : new(key, symbolize:)
end

Instance Method Details

#catch_all?Boolean

The _ catch-all: a matcher key over the Any top, so it matches every key.

Returns:

  • (Boolean)


54
# File 'lib/plumb/key.rb', line 54

def catch_all? = !@literal && @matcher.is_a?(AnyClass)

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
72
73
74
75
76
77
# File 'lib/plumb/key.rb', line 69

def eql?(other)
  return false unless other.is_a?(Key)

  if @literal
    other.literal? && @to_key == other.to_key
  else
    !other.literal? && @matcher == other.matcher
  end
end

#hashObject

Dedupe/lookup identity. Literal keys hash by name (unchanged); matcher keys hash by matcher class (structural equality disambiguates via #eql?), so two _ catch-alls collapse. Two keys are eql? when both literal with the same name, or both matchers with equal matchers — optionality is deliberately ignored (so name?/name collapse), and HashClass#== compares it separately.



67
# File 'lib/plumb/key.rb', line 67

def hash = @literal ? @to_key.hash : @matcher.class.hash

#inspectObject



83
84
85
86
87
88
89
90
91
# File 'lib/plumb/key.rb', line 83

def inspect
  if @literal
    "#{@to_key}#{'?' if @optional}"
  elsif catch_all?
    '_'
  else
    @matcher.inspect
  end
end

#literal?Boolean

A concrete Symbol/String key (exact lookup) vs a type/matcher key.

Returns:

  • (Boolean)


51
# File 'lib/plumb/key.rb', line 51

def literal? = @literal

#match?(other) ⇒ Boolean

Does this key match other (a raw hash key)? Literal keys match by == (Symbol#===/String#===); matcher keys by matcher === other.

Returns:

  • (Boolean)


58
# File 'lib/plumb/key.rb', line 58

def match?(other) = @matcher === other

#optional?Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/plumb/key.rb', line 79

def optional?
  @optional
end

#to_sObject



60
# File 'lib/plumb/key.rb', line 60

def to_s = (@to_key || @matcher).to_s