dry-transaction icon indicating copy to clipboard operation
dry-transaction copied to clipboard

around steps and Dry::Transaction::Operation don't get along

Open dmaze opened this issue 6 years ago • 1 comments

I've gotten into the habit of writing dry-transaction operations as individual classes that include Dry::Transaction::Operation. (It avoids the scary 5-letter "m" word.) If you use this in an operation class that's intended to be an around step, like so:

class First
  include Dry::Transaction::Operation
  def call(input)
    yield Success(input + 1)
  end
end

You will get a backtrace when you run it. https://gist.github.com/dmaze/ea78510a8870089be510a55d8fb38a8e is a more complete reproduction script. Running that prints out:

first 1
done with first
LocalJumpError: yield called out of block
              call at ./dry-transaction-operation.rb:20
     block in call at .../gems/dry-matcher-0.7.0/lib/dry/matcher.rb:35
              call at org/jruby/RubyMethod.java:129
              call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/callable.rb:33
              call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/step_adapters/around.rb:12
              call at org/jruby/RubyMethod.java:129
              call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/step_adapter.rb:41
     block in call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/step.rb:52
    with_broadcast at .../gems/dry-transaction-0.13.0/lib/dry/transaction/step.rb:61
              call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/step.rb:52
  block in compile at .../gems/dry-transaction-0.13.0/lib/dry/transaction/stack.rb:19
              bind at .../gems/dry-monads-0.4.0/lib/dry/monads/right_biased.rb:48
  block in compile at .../gems/dry-transaction-0.13.0/lib/dry/transaction/stack.rb:19
              call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/stack.rb:12
              call at .../gems/dry-transaction-0.13.0/lib/dry/transaction/instance_methods.rb:28
            <main> at ./dry-transaction-operation.rb:46

More specifically, it looks like the Operation mixin wraps #call in a dry-matcher, and the generated method doesn't pass the block up to the user code (it looks like it might repurpose it for something else).

An easy enough workaround is to include Dry::Monads::Result::Mixin instead.

I'm running this with jruby-9.2.0.0 and the gem versions shown in the backtrace, should that turn out to matter.

dmaze avatar Nov 14 '18 21:11 dmaze

I found a workaround - dry-transaction doesn't actually care that the object includes Dry::Transaction::Operation specifically, it just uses the call method. So you can instead replace it with Dry::Monads[:result] directly:

class First
  include Dry::Monads[:result]
  def call(input)
    yield Success(input + 1)
  end
end

TastyPi avatar Jun 15 '23 14:06 TastyPi