LANLIB

Serviceクラスの実行結果をServiceResponseクラスで返す

はじめに

serviceクラスの実行結果を今までは結構適当にしていました。

例えば、更新した件数を返したり、成功したかどうかをtrue,falseで返したり、まちまちでした。

GitLabではServiceResponseというクラスを作ってそれを使って実行結果のレスポンスを返していて、とても良いと思ったのでまとめます。

中身

GitLabでは下記の様になっていました。

https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/service_response.rb

# frozen_string_literal: true

class ServiceResponse
  def self.success(message: nil, payload: {}, http_status: :ok)
    new(status: :success, message: message, payload: payload, http_status: http_status)
  end

  def self.error(message:, payload: {}, http_status: nil)
    new(status: :error, message: message, payload: payload, http_status: http_status)
  end

  attr_reader :status, :message, :http_status, :payload

  def initialize(status:, message: nil, payload: {}, http_status: nil)
    self.status = status
    self.message = message
    self.payload = payload
    self.http_status = http_status
  end

  def success?
    status == :success
  end

  def error?
    status == :error
  end

  def errors
    return [] unless error?

    Array.wrap(message)
  end

  private

  attr_writer :status, :message, :http_status, :payload
end

使い方

成功した場合は

ServiceResponse.success(message: 'success!')

をserviceクラスの実行した際の戻り値に設定して、

失敗した場合は

ServiceResponse.error(message: 'failed')

をserviceクラスの実行した際の戻り値に設定します。

下記の様に、ステータスを確認できます。

response = ServiceResponse.success(message: 'success!')

response.success? #=> true
response.error? #=> false
response.status #=> :success
response.message #=> 'success!'

controller側では

result = ArticleUpdateService.new(@article, params).call

if result.success?
  redirect_to article_path(@article), notice: result.message
elsif result.error?
  render :new, alert: result.message
end

みたいな形で成功した場合と失敗した場合で処理を分けることができます。