# Allow Dry::Matcher blocks to be shared/nested

**URL:** <https://discourse.dry-rb.org/t/allow-dry-matcher-blocks-to-be-shared-nested/1283>\
**Category:** Ideas\
**Created:** [June 21, 2021, 6:42pm UTC](https://discourse.dry-rb.org/t/allow-dry-matcher-blocks-to-be-shared-nested/1283 "2021-06-21T18:42:26Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![paul](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/paul/32/202_2.png) [@paul](https://discourse.dry-rb.org/u/paul)\
**Post date:** [June 21, 2021, 6:42pm UTC](https://discourse.dry-rb.org/t/allow-dry-matcher-blocks-to-be-shared-nested/1283/1 "2021-06-21T18:42:26Z")

</div>

We’re using Dry::Transaction extensively within our Rails application, specifially for controller actions and background jobs. Most of our controller actions invoke a Transaction, and then render a response/status based on the Result of the Transaction. The bulk of Transactions invoked from the controller action have a similar set of steps (eg :authorize, :validate, etc…). To dry up the error handling code, we extracted the common failure handler to a separate method:

```ruby
# Controller action
def update
  txn.call(params) do |on|
    on.success { ... }
    on.failure(:special) { ... }
    on.failure { handle_common_failures(on) }
  end
end

# Shared helper
def handle_common_failures(on)
  on.failure(:authorize) { render status: :not_found}
  on.failure(:validate) { |errors| render error: errors, status: :unprocessable_entity }
  on.failure do |err|
    status = case err
             when ActiveRecord::RecordInvalid, Dry::Schema::Result
               :unprocessable_entity
             when ActiveRecord::RecordNotFound
               :not_found
             else
               :forbidden
             end

    render error: err, status: status
  end
end

```

However, this doesn’t work out-of-the box with Dry::Matchers, because the `@output` of each branch gets checked if its `defined?` ([dry-matcher/evaluator.rb at master · dry-rb/dry-matcher · GitHub](https://github.com/dry-rb/dry-matcher/blob/master/lib/dry/matcher/evaluator.rb#L53)). What’s happening is that in the controller action the `on.failure` gets invoked, which defines `@output` (to `nil`) then `yields` to our helper. Then when we try to invoke a different set of matchers in the helper, `@output` is already defined, so none of those matcher branches are attempted.

We’ve monkey-patched `Dry::Matcher::Evaluator#method_missing` to separately check if we’ve “completed” a matcher branch (with the `@matched` ivar), rather than if it has been started:

```ruby
class Dry::Matcher::Evaluator 
  def method_missing(name, *args, &block) 
    kase = @cases.fetch(name) { return super }

    @unhandled_cases.delete name

    return @output if @matched

    kase.(@result, args) do |result|
      @output = yield(result)
      @matched = true
      @output
    end
  end
end

```

We’ve been running with this monkey-patch for years (June 2018 according to the git history). I’m wondering if I opened a PR with the implementation in our monkey-patch, is it something that would be accepted upstream?

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [June 21, 2021, 7:15pm UTC](https://discourse.dry-rb.org/t/allow-dry-matcher-blocks-to-be-shared-nested/1283/2 "2021-06-21T19:15:47Z")

</div>

Couldn’t you call `handle_common_failures` unnested?

```ruby
def update
  txn.call(params) do |on|
    on.success { ... }
    on.failure(:special) { ... }
    handle_common_failures(on)
  end
end

```

---

<div class="post-metadata">

**Author:** ![paul](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/paul/32/202_2.png) [@paul](https://discourse.dry-rb.org/u/paul)\
**Post date:** [June 21, 2021, 8:23pm UTC](https://discourse.dry-rb.org/t/allow-dry-matcher-blocks-to-be-shared-nested/1283/3 "2021-06-21T20:23:14Z")

</div>

Yeah, I suppose that’s an option. It lacks the symmetry of having each branch of the matcher within a block in the controller action, though. Also, I simplified what we’re actually doing for the purposes of this question, I debated if I should go with the simplest possible example, or the more Rails-centric code we’re actually using. Instead of the helper, we use an ActionController::Renderer called `:failure` that takes the matcher, so the symmetry of all the `render ...` blocks reads very nicely:

```ruby
def update
  txn.call(params) do |on|
    on.success { |post| render post }
    on.failure(:special) { |msg| render text: msg, status: :forbidden }
    on.failure { |_| render failure: on }
  end
end

```

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [June 22, 2021, 8:44pm UTC](https://discourse.dry-rb.org/t/allow-dry-matcher-blocks-to-be-shared-nested/1283/4 "2021-06-22T20:44:58Z")

</div>

Honestly, since the Ruby language has added pattern-matching, I would expect dry-matcher to be deprecated and mothballed.
