dry-transaction
dry-transaction copied to clipboard
around steps and Dry::Transaction::Operation don't get along
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.
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