Introduction
dry-matcher offers flexible, expressive pattern matching for Ruby.
You can build your own matcher or use the out-of-the-box support for matching on dry-monads Result
values.
Building a matcher
To build your own matcher, create a series of "case" objects with their own resolving logic. First argument of the case block is the value to match, second argument is the list of patterns (see below). The block must either return the result or Dry::Matcher::Undefined
if the value has no match. The latter signals dry-matcher to try the next case.
require "dry-matcher"
# Match `[:ok, some_value]` for success
success_case = Dry::Matcher::Case.new do |(code, value), _|
if code.equal?(:ok)
value
else
# this is a constant from dry/core/constants
Dry::Matcher::Undefined
end
end
# Match `[:err, some_error_code, some_value]` for failure
failure_case = Dry::Matcher::Case.new do |(code, value), patterns|
if code.equal?(:err) && (patterns.empty? || patterns.include?(value))
value
else
Dry::Matcher::Undefined
end
end
# Build the matcher
matcher = Dry::Matcher.new(success: success_case, failure: failure_case)
Then use these cases as part of an API to match on results:
my_success = [:ok, "success!"]
result = matcher.(my_success) do |m|
m.success do |v|
"Yay: #{v}"
end
# :not_found and :lost are patterns
m.failure :not_found, :lost do |v|
"Oops, not found: #{v}"
end
m.failure do |v|
"Boo: #{v}"
end
end
result # => "Yay: success!"
Cases are executed in order. The first match wins and halts subsequent matching.
my_failure = [:err, :not_found, "missing!"]
result = matcher.(my_failure) do |m|
m.success do |v|
"Yay: #{v}"
end
m.failure :not_found do |v|
"Oops, not found: #{v}"
end
m.failure do |v|
"Boo: #{v}"
end
end
result # => "Oops, not found: missing!"