← Back to Upcase

Bubbling up errors in service objects


(daphsta) #1

Hey all,

I’m creating service objects to handle business logic that does not belong in ActiveRecord models neither should it be in the controllers. I am seeking opinions on how to bubble back errors from a service object back to the controller calling it without using any gems. For example, this would be in my controller:

class SessionsController < ApplicationController
  def create
    result = AuthenticateUser.call(session_params)

    if result.success?
      session[:user_token] = result.token
      redirect_to root_path
    else
      flash.now[:message] = t(result.error)
      render :new
    end
  end
end

For the sake of brevity, I have left out the private method that constructs the session_params.
I would like the service object (AuthenticateUser) to be able to return an error message and also to check if it successful or not. I appreciate suggestions on how to construct a module to handle errors which can bubble back up to the caller of the object.

Cheers


(Andy Waite) #2

One option is to make use of ActiveModel in your service object. This allows you to treat the class in a very similar way to an ActiveRecord model, and take advantage of custom validations, the errors collection, etc.

class AuthenticateUser
  include ActiveModel::Model
  attr_accessor :username, :password
  attr_reader :token

  validate :check_credentials

  def save
    @token = ... # do something
  end

  private

  def check_credentials
    unless username == "foo" && password == "bar"
      errors.add(:base, "Invalid credentials")
    end
  end
end

The controller would then become:

class SessionsController < ApplicationController
  def create
    result = AuthenticateUser.new(
      username: session_params.fetch(:username),
      password: session_params.fetch(:password)
    )

    if result.save
      session[:user_token] = result.token
      redirect_to root_path
    else
      flash.now[:message] = t(result.errors[:base])
      render :new
    end
  end
end

Although it’s a little out-of-date, the book Growing Rails Applications covers this approach in good detail.

A downside is that the service class is coupled to Rails and can’t be tested in isolation.