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 transaction in case one of the operations failures.
around step to give an operation this behavior. 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 begin MyDB.transaction do result = block.(Success(input)) raise MyDB::Rollback if result.failure? result end rescue MyDB::Rollback result end end end
And can be integrated into a business transaction like this:
class MyOperation include Dry::Transaction(container: MyContainer) around :transaction, with: "transaction" # Multiple subsequent steps writing to the database step :create_user, with: "users.create" step :create_account, with: "accounts.create" end