Class: ToyAdt::Matcher

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/toy_adt/matcher.rb

Constant Summary collapse

POSITIONAL_ARGS =
Set[:req, :opt, :rest]
KEYWORD_ARGS =
Set[:keyreq, :key, :keyrest]
BLOCK_ARGS =
Set[:block]

Instance Method Summary collapse

Constructor Details

#initialize(from:, &fn) ⇒ Matcher

Returns a new instance of Matcher.



14
15
16
17
18
19
20
21
22
# File 'lib/toy_adt/matcher.rb', line 14

def initialize(from:, &fn)
  @from = from
  @fields = from::FIELDS
  @sub_classes = @fields.to_h { [_1, from.const_get(_1.capitalize)] }

  create_branch_methods

  instance_eval(&fn) if block_given?
end

Instance Method Details

#call(input) ⇒ Object Also known as: ===



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/toy_adt/matcher.rb', line 28

def call(input)
  # Find the subclass the input matches to
  branch_name, _ = @sub_classes.find do |_, klass|
    matches_type?(klass, input)
  end

  # If there are none either hit the else or fail hard
  if branch_name.nil?
    return @else_fn.call(input) if @else_fn
    T.absurd(input)
  end

  # All values to compare against conditions
  values = input.deconstruct_keys(nil)

  # Try to find the first matching branch function via
  # conditions:
  #
  #     value.match do |m|
  #       m.some(value: 0..5) {}
  #       m.some {}
  #       m.else {}
  #     end
  #
  # In this case a "Some" type with a value matching will hit
  # first. Granted less specific branches going first means
  # those get hit first, so not recommended.
  _, branch_fn = @branches.find do |branch_condition, _|
    name, conditions = branch_condition

    # First thing is the key is a tuple of subclass / branch
    # name, the second part is the condition if there are any.
    branch_name == name && conditions.all? { |k, condition|
      matches_type?(condition, values[k])
    }
  end

  return branch_fn.call(input) if branch_fn
  return @else_fn.call(input) if @else_fn

  T.absurd(input)
end

#else(&fn) ⇒ Object



24
25
26
# File 'lib/toy_adt/matcher.rb', line 24

def else(&fn)
  @else_fn = fn
end

#to_procObject



73
74
75
# File 'lib/toy_adt/matcher.rb', line 73

def to_proc
  -> input { call(input) }
end