# frozen_string_literal: true require "dry/core/constants" require "dry/matcher/case" require "dry/matcher/evaluator" module Dry # @see http://dry-rb.org/gems/dry-matcher class Matcher include Core::Constants RUBY2_KEYWORDS = respond_to?(:ruby2_keywords, true) # Generates a module containing pattern matching for methods listed in # `match_methods` argument with behavior defined by `with` matcher # # @param [] match_methods # @param [Dry::Matcher] with # @return [Module] # # @example Usage with `dry-monads` # class MonadicOperation # include Dry::Matcher.for(:call, with: Dry::Matcher::ResultMatcher) # # def call # Dry::Monads::Result::Success.new('Success') # end # end # # operation = MonadicOperation.new # # operation.call do |m| # m.success { |v| "#{v} was successful!"} # m.failure { |v| "#{v} has failed!"} # end #=> "Success was successful" def self.for(*match_methods, with:) matcher = with matchers_mod = Module.new do match_methods.each do |match_method| define_method(match_method) do |*args, &block| result = super(*args) if block matcher.(result, &block) else result end end ruby2_keywords(match_method) if RUBY2_KEYWORDS end end Module.new do const_set :Matchers, matchers_mod def self.included(klass) klass.prepend const_get(:Matchers) end end end # @return [Hash{Symbol => Case}] attr_reader :cases # @param [Hash{Symbol => Case}] cases def initialize(cases = {}) @cases = cases end # Evaluate {#cases}' matchers and returns a result of block given to # corresponding case matcher # # @param [Object] result value that would be tested for matches with given {#cases} # @param [Object] block # @yieldparam [Evaluator] m # @return [Object] value returned from the block given to method called # after matched pattern # # @example Usage with `dry-monads` # require 'dry/monads' # require 'dry/matcher/result_matcher' # # value = Dry::Monads::Result::Failure.new('failure!') # # Dry::Matcher::ResultMatcher.(value) do |m| # m.success { |v| "Yay: #{v}" } # m.failure { |v| "Boo: #{v}" } # end #=> "Boo: failure!" def call(result, &block) Evaluator.new(result, cases).call(&block) end # Shortcut for Dry::Matcher.for(..., with: matcher) # # @param [Array[Symbol]] # # @return [Module] def for(*methods) self.class.for(*methods, with: self) end end end