14
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RailsのREST APIサーバーのレスポンス整形にActiveDecoratorを使う

Posted at

要約

  • コントローラー用のConcernを作って、デコレートメソッドの呼び出しを短く
  • デコレーターにas_jsonメソッドを定義して、コントローラーの見た目を簡潔に
class IssuesController < ApplicationController
  include DecoratorAction

  def show
    render json: deco { Issue.find_by id: params[:id] }
  end
end

背景

REST APIサーバを作っている時に、返却するJSONをモデルそのままではなく整形する必要があることがあります。例えば、

  • 数字と文字列の変換
  • 真理値と0/1の変換
  • 日付フォーマットの変更

などです。整形処理はアプリケーションのプレゼンテーションに関わることです。モデルクラスには書きたくありません。

目的

このような場合、Decoratorパターンを使うのが一般的です。

Railsアプリケーション向けの実装では、DraperActiveDecoratorが知られています。特にActiveDecoratorがシンプルで好きです。

ActiveDecoratorを使って、JSONの整形処理をモデルクラスからDecoratorクラスに分離したいです。

例題 「真理値を1/0に変換」

issue_decorator.rbに、でclose_flgを真理値から、数値の1/0に変換します。

module IssueDecorator
  def close_flg
    super ? 1 : 0
  end
end

close_flgメソッドを上書きすると実現できます。

課題1 JSONをレンダリングするときに自動的にデコレートされない

ActiveDecoratorはAuto-decorating via renderします。具体的にはAbstractController::Rendering#view_assignsの実行時に、モデルのデコレーションを行います1

REST APIサーバーで、特に次のようにJSONのレンダリングにビューを使わない場合、

class IssuesController < ApplicationController
  def show
    render json: Issue.find_by(id: params[:id]), except: %i[id created_at updated_at]
  end
end

decoratorへ自動変換されません。

対策

コントローラーで手動デコレーション

issues_controller.rbで取得したオブジェクトをデコレーションします。

class IssuesController < ApplicationController
  def show
    issue = Issue.find_by id: params[:id]
    decorator = ActiveDecorator::Decorator.instance.decorate(issue)
    render json: decorator, except: %i[id created_at updated_at]
  end
end

全てのコントローラーでデコレーション用のメソッドを呼ぶのは不便です。

デコレーションメソッドをConcernとして定義

Concernにメソッド定義して、includeで使いまわせるようにしましょう。

Concernを定義

decorator_action.rbを作る

module DecoratorAction
  extend ActiveSupport::Concern

  private

  # デコレーターを適用
  def deco
    ActiveDecorator::Decorator.instance.decorate yield
  end
end

Concernを使う

class IssuesController < ApplicationController
  include DecoratorAction

  def show
    render json: deco { Issue.find_by id: params[:id] }, except: %i[id created_at updated_at]
  end
end

ActiveDecoratorの呼び出しが短くなって、うれしいですね。

課題2 デコレーターで情報を減らせない

デコレーターでは情報の変更と追加はできます。減らすことができません。
ActiveRecordオブジェクトが持っている

  • id
  • created_at
  • updated_at

の情報を、APIのレスポンスから除外したい時は、renderメソッドのexceptオプションを指定して除外します。

render json: deco { Issue.find_by id: params[:id] }, except: %i[id created_at updated_at]

DBから取得したモデルを、そのまま返すだけのAPIはもう少しシンプルに書きたいです。

対策

デコレーターでas_jsonメソッドを定義

ActiveRecordオブジェクトのJSON表現を変更したいときは、as_jsonメソッドをオーバーライドします。

json - How to override to_json in Rails? - Stack Overflow

You should override as_json in your Model to create the JSON structure you want. as_json

issue_decorator.rbは次のようにします。

module IssueDecorator
  def as_json(_)
    {
      title: title,
      description: description,
      close_flg: close_flg ? 1 : 0,
    }
  end
end

最終形

class IssuesController < ApplicationController
  include DecoratorAction

  def show
    render json: deco { Issue.find_by id: params[:id] }
  end
end
  1. https://github.com/amatsuda/active_decorator/blob/master/lib/active_decorator/monkey/abstract_controller/rendering.rb

14
11
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?