Around steps

Regular step operations take an input, act on it, and return an output to pass to the next step. They operate in sequence, with control being passed from one operation to the next.

Sometimes, a step operation needs to wrap all of the subsequent steps. A common case for this is handling database transactions. An operation providing a database transaction across steps needs to wrap around all the subsequent operations so it can roll back the transactoin in case one of the operations failures.

Use an around step to give an operation this behaviour. The operation will receive #call(input, &block), where &block is the collection of steps it is wrapping. It should call the block to run those steps and handle as appropriate.

An operation to wrap steps in a database transaction would work like this (replace MyDB with whatever your database system requires):

class MyContainer
  extend Dry::Container

  register(:transaction) do |input, &block|
    result = nil

      MyDB.transaction do
        result = block.(Success(input))
        raise MyDB::Rollback if result.failure?
    rescue Test::Rollback

And can be integrated into a business transaction like this:

class MyOperation
  include Dry::Transaction(container: MyContainer)

  around :transaction
  step :persist_something
  step :persist_another_thing