Introduction to the Interactor Gem: Simplifying Business Logic in Ruby

The Interactor gem is a powerful tool that simplifies the implementation of workflows in Ruby applications. By breaking down tasks into smaller components, known as interactors, and utilizing the context for data sharing, we can simplify business logic and improve code maintainability. In this blog post, we’ll explore how interactors can simplify business logic by implementing order fulfillment for an e-commerce application.

For this, we’ll create two interactors: ValidateCart and ProcessPayment.

The ValidateCart interactor is responsible for validating the shopping cart. It contains the logic to perform this validation and, if the cart is considered valid, sets the cart_valid variable to true in the interactor’s context. Otherwise, it fails with an error message indicating that the cart is invalid.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ValidateCart
  include Interactor

  def call
    # Logic to validate the shopping cart
    if valid_cart?
      context.cart_valid = true
    else
      context.fail!(message: "The shopping cart is invalid.")
    end
  end

  private

  def valid_cart?
    # Check if the shopping cart is valid
  end
end

The ProcessPayment interactor is responsible for processing the payment for the order. It checks if the cart_valid variable is true in the context, indicating that the cart has been successfully validated by the previous interactor. If so, it performs the payment processing logic and sets the payment_processed variable to true in the context. Otherwise, it fails with an error message stating that it’s not possible to process the payment without a valid shopping cart.

1
2
3
4
5
6
7
8
9
10
11
12
class ProcessPayment
  include Interactor

  def call
    if context.cart_valid
      # Logic to process the payment for the order
      context.payment_processed = true
    else
      context.fail!(message: "Cannot process payment without a valid shopping cart.")
    end
  end
end

Using the Interactors

Now that we have our interactors defined, we can use them to finalize an order. Let’s see how this can be done:

1
2
3
4
5
6
7
8
9
10
11
result = ValidateCart.call
if result.success?
  result = ProcessPayment.call(result.context)
  if result.success?
    puts "Order processed successfully!"
  else
    puts "Error processing payment: #{result.message}"
  end
else
  puts "Error validating the shopping cart: #{result.message}"
end

In this example, we call the ValidateCart interactor and check if the execution was successful. If it is, we call the ProcessPayment interactor, passing the returned context. We then check if the payment processing was successful and display the appropriate message accordingly.