Errors

Errors are a natural part of every application. This guide explores how to handle errors within Operandi, drawing parallels to ActiveModel errors.

Error Structure

Operandi errors follow a structure similar to ActiveModel errors. Here's a simplified example:

{
  email: ["must be a valid email"],
  password: ["is too short", "must contain at least one number"]
}

Adding Errors

To add an error to your service, use the errors.add method.

By default, adding an error marks the service as failed, preventing subsequent steps from executing. This behavior can be customized in the configuration for individual services and errors.

class ParsePage < ApplicationService
  # Arguments
  arg :url, type: String
  # ...

  # Steps
  step :validate
  step :parse
  # ...

  private

  def validate
    # Multiple errors can be added with the same key
    errors.add(:url, "must be a valid URL") unless url.match?(URI::DEFAULT_PARSER.make_regexp)
    errors.add(:url, "must be a secure link") unless url.start_with?("https")
  end

  # ...
end

Quick Error with fail!

The fail! method is a shortcut for adding an error to the :base key:

This is equivalent to:

Reading Errors

To check if a service has errors, you can use the #failed? method. You can also use methods like errors.any? to inspect errors.

You can access errors outside the service using the #errors method.

Adding Warnings

Sometimes, you may want to add a warning instead of an error. Warnings are similar to errors but they do not mark the service as failed. By default they also do not stop execution and do not roll back the transaction (both behaviors can be configured globally or per-message).

Copying Errors

From ActiveRecord Models

Use errors.copy_from (or its alias errors.from_record) to copy errors from an ActiveRecord model:

From Another Service

Copy errors from a child service that wasn't run in the same context:

Converting Errors to Hash

Use errors.to_h to get a hash representation of all errors:

Per-Message Options

When adding errors, you can control behavior on a per-message basis:

Control Break Behavior

Control Rollback Behavior

Checking for Errors and Warnings

Operandi provides convenient methods to check error/warning states:

By following these guidelines, you can effectively manage errors and warnings in Operandi, ensuring a smoother and more robust application experience.

Exception Classes

Operandi defines several exception classes for different error scenarios:

Exception
Description

Operandi::Error

Base exception class for all Operandi errors

Operandi::ArgTypeError

Raised when an argument or output type validation fails

Operandi::ReservedNameError

Raised when using a reserved name for arguments, outputs, or steps

Operandi::InvalidNameError

Raised when using an invalid name format

Operandi::NoStepsError

Raised when a service has no steps defined and no run method

Operandi::MissingTypeError

Raised when defining an argument or output without a type option when require_arg_type or require_output_type is enabled

Operandi::StopExecution

Control flow exception raised by stop_immediately! to halt execution without rollback

Operandi::FailExecution

Control flow exception raised by fail_immediately! to halt execution and rollback transactions

MissingTypeError

This exception is raised when you define an argument or output without a type option. Since require_arg_type and require_output_type are enabled by default, all arguments and outputs must have a type.

To fix this, add a type option to all arguments and outputs:

If you need to disable type enforcement for legacy services, you can use the config method:

NoStepsError

This exception is raised when you attempt to execute a service that has no steps defined and no run method as a fallback:

To fix this, either define at least one step or implement a run method:

What's next?

Learn about callbacks to add logging, benchmarking, and other cross-cutting concerns to your services.

Next: Callbacks

Last updated