Presenter
という層を導入してみて、良かったなあってこと、書こうと思います🧸
正直 n番煎じで、どこにでもある記事なのですが、Presenter
を使う時のルールから考えれば、感じたメリットがやや違うなあと思ったので自分なりに書きます🐲
(実は、導入したのは弊社の別のエンジニアなのですが、もういないので勝手にいただきます😏)
こんなRailsの開発を回避したい!
そもそも、どういう開発でPresenter
が使われるのか?
僕なりに、問題提起していこうかと思います。
Viewのロジックをどんどんモデルに追加してしまう
厳密には以下のようなことはありえないですが、
それview
以外じゃ使わないでしょう的なことを察してほしいです。🤫
class User < ApplicationRecord
def self.name_array(users)
users.map { |e| e.name }
end
end
以下の通りですが、viewのロジックばっかりのモデルは少し嫌ですね。
モデルとは、データベースや外部サービスへのアクセスをはじめ、ビジネスロジック全般を担当するコンポーネントのことです。
出典 : モデルの基本
controllerのアクションmethod の外でインスタンス変数を作ってしまう
別に問題はないけど「え、こんなインスタンス作ったっけ?」ってなります。
(気づかずに同じインスタンスを作ってしまうとかも嫌ですね。)
class UsersController < ApplicationController
before_action :set_greate_users
def index
@target_users = current_user.target_users
end
private
def set_greate_users
@great_users = current_user.great_users
end
end
以下の記事でも、謎の場所でインスタンスを定義するな!と言ってる過激派もいます。
Controller Best Practices: Don’t Hide Instance Variables
神helperにしてしまう
そもそもviewのロジックはhelperに追加しようというのが基本的だと思うのですが、危険性を書きます。
- helperは、どこでも読み込まれるので、メソッド名が衝突する可能性がある
- helperの修正を行うと、意図しない箇所も変更されてしまう
- 色々なhelperから呼び出されると、ロジックを追うのだけで大変になる
module UserHelper
def great_users
current_user.great_users
end
end
ほぼ以下の記事の受け売りなのは秘密です。
Decorator と Presenter を使い分けて、 Rails を ViewModel ですっきりさせよう
なぜそんな開発になったの?
僕も開発していて、ハッとしたので、書き残します。
rubocopのAbcSizeが怒るから
ありがちですね。
そもそもアクションmethodを起点に何かが起こるので、アクションmethod内で色々したくなります。
でも、ロジックが多く書けない以上、インスタンス変数から呼び出せるようにモデルにロジックを追加するしかない気持ちもわかります。
Metrics/AbcSize: Assignment Branch Condition size for show is too high. [16.31/15]
AbsSize
を追加されたプルリク
AbcSize
のデフォルトを考え直そうという提案がされたプルリク
-
Relax metrics cops threshold
↓ちゃんと明文化することが大事って書いてありました。
I think that at this point is way more important to document some of the defaults and why they were picked than relaxing them. Not to mention that default values like 18 and 14 seem extremely random.
rails_best_practicesに言われたから
rails_best_practices
がアクションmethod内部で変数に対して4回以上methodを呼び出すと、以下のようなエラーが出力されます。
2~3個のインスタンスを作成していたら、確かに出がちなエラーだと思います。
/Users/.../app/controllers/users_controller.rb:5 -
move model logic into model (current_user use_count > 4)
Presenterを導入しよう
ここからは n番煎じ、presenterの導入方法なので、どの記事を読んでも同じです。
他の記事読んでも大差ないです。
Presenterを使う時のマイルール
何でもPresenter
を使えってことではありません。
僕なりの使い方を書きます。
- インスタンス変数が4個以上になるなら
Presenter
*4という数字に意図はありません、ノリです。 - どこでも使う
view
のロジック(URLの置換)などはhelper
を使う -
Decorator
という考え方は入れない(混乱するので)
Presenterのコード
弊社ではPresenter
にいくつもの引数を渡せるように、BasePresenter
を作っています。
require 'delegate'
class BasePresenter < SimpleDelegator
attr_reader :resource, :args
def initialize(resource, **args)
@resource = resource
@args = args
super(resource)
end
end
クラス名は、どのコントローラーのどのアクションmethodなのかわかりやすいようにしています
module Users
class IndexPresenter < BasePresenter
# arg[:taregt_id]で、第2引数のhashの中身を呼び出せます
# ↓ロジックを書く
def target_name
User.find(arg[:target_id]).name
end
alias_method :current_user, :resource
# ↑resourceでも呼び出せますが、current_userで呼び出せるようにしています
end
end
controllerで呼び出すだけです
class UsersController < ApplicationController
def index
@presenter = Users::IndexPresenter.new(current_user, target_id: params[:id])
end
end
あとは@presenter.target_name
で呼び出せます!
DecoratorとPresenterの違いって?
僕もよくわかりません😳
あえて載せませんが、rails presenter
で検索してヒットする記事は全てDecorator/Presenter
の解釈が違いました。
弊社では Decorator/Presenter の記事に書いてあった Exhibit vs Presenter が一番近いなと思いました。
Decorator パターンは定義上、1個のオブジェクトしか内包しないので、「Presenter」のように2個のオブジェクトに委譲を行うような設計は厳密には Decorator パターンではありません。
弊社ではPresenter
にcurrent_user
という基本的なオブジェクトと、その他のhashを渡しています。
内部で複数のインスタンス変数が定義されたりするので、ちょっと特殊な使い方です。
所感
そういえば、Presenter
を導入した記事を読むと、昔の人はrails
でhtml
を返却していたみたいですね。
弊社はrails
はjson
を返却するAPIで、Vue
でフロントを制御しています。
昔の人はどんな開発をしていたんだろうか、、、あまり想像できていない🙄🙄