lib/dry/matcher/evaluator.rb in dry-matcher-0.5.0 vs lib/dry/matcher/evaluator.rb in dry-matcher-0.6.0
- old
+ new
@@ -1,31 +1,68 @@
module Dry
class Matcher
+ NonExhaustiveMatchError = Class.new(StandardError)
+
+ # {Evaluator} is used in {Dry::Matcher#call Dry::Matcher#call} block to handle different {Case}s
class Evaluator < BasicObject
+ # @param [Object] result
+ # @param [Hash{Symbol => Case}] cases
def initialize(result, cases)
@cases = cases
@result = result
+
+ @unhandled_cases = @cases.keys.map(&:to_sym)
@matched = false
@output = nil
end
def call
yield self
+
+ ensure_exhaustive_match
+
@output
end
+ # Checks whether `cases` given to {#initialize} contains one called `name`
+ # @param [String] name
+ # @param [Boolean] include_private
+ # @return [Boolean]
def respond_to_missing?(name, include_private = false)
- @cases.key?(name) || super
+ @cases.key?(name)
end
+ # Handles method `name` called after one of the keys in `cases` hash given
+ # to {#initialize}
+ #
+ # @param [String] name name of the case given to {#initialize} in `cases`
+ # argument
+ # @param [Array] args pattern that would be tested for match and used to
+ # resolve result
+ # @param [#call] block callable that will processes resolved value
+ # from matched pattern
+ # @yieldparam [Object] v resolved value
+ # @return [Object] result of calling `block` on value resolved from `args`
+ # if `args` pattern was matched by the given case called `name`
+ # @raise [NoMethodError] if there was no case called `name` given to
+ # {#initialize} in `cases` hash
def method_missing(name, *args, &block)
return super unless @cases.key?(name)
+ @unhandled_cases.delete name
handle_case @cases[name], *args, &block
end
private
+ def ensure_exhaustive_match
+ if @unhandled_cases.any?
+ ::Kernel.raise NonExhaustiveMatchError, "cases +#{@unhandled_cases.join(', ')}+ not handled"
+ end
+ end
+
+ # @param [Case] kase
+ # @param [Array] pattern
def handle_case(kase, *pattern)
return @output if @matched
if kase.matches?(@result, *pattern)
@matched = true