Error Handling

When using dry-operation, errors are handled through the Failure type from dry-monads. Each step in your operation should return either a Success or Failure result. When a step returns a Failure, the operation short-circuits, skipping the remaining steps and returning the failure immediately.

You'll usually handle the failure from the call site, where you can pattern match on the result to handle success and failure cases. However, sometimes it's useful to encapsulate some error handling logic within the operation itself.

Global error handling

You can define a global failure handler by implementing an #on_failure method in your operation class. This method is only called to perform desired side effects and it won't affect the operation's return value.

class CreateUser < Dry::Operation
  def initialize(logger:)
    @logger = logger
  end

  def call(input)
    attrs = step validate(input)
    user = step persist(attrs)
    step notify(user)
    user
  end

  private

  def on_failure(failure)
    # Log or handle the failure globally
    logger.error("Operation failed: #{failure}")
  end
end

The #on_failure method can optionally accept a second argument that indicates which method encountered the failure. This is helpful if you're defining more than an operation in a class:

class CreateUser < Dry::Operation
  def initialize(logger:)
    @logger = logger
  end

  def call(input)
    attrs = step validate(input)
    user = step persist(attrs)
    step notify(user)
    user
  end

  private

  def on_failure(failure, step_name)
    # step_name is going to be `:call` here
  end
end

octocatEdit on GitHub