11
0

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.

Linkbal(リンクバル)Advent Calendar 2019

Day 9

【Rails】modelやhelperに無駄なmethodを増やさないpresenter

Posted at

Presenterという層を導入してみて、良かったなあってこと、書こうと思います🧸
正直 n番煎じで、どこにでもある記事なのですが、Presenterを使う時のルールから考えれば、感じたメリットがやや違うなあと思ったので自分なりに書きます🐲
(実は、導入したのは弊社の別のエンジニアなのですが、もういないので勝手にいただきます😏)

こんなRailsの開発を回避したい!

そもそも、どういう開発でPresenterが使われるのか?
僕なりに、問題提起していこうかと思います。

Viewのロジックをどんどんモデルに追加してしまう

厳密には以下のようなことはありえないですが、
それview以外じゃ使わないでしょう的なことを察してほしいです。🤫

.rb
class User < ApplicationRecord
  def self.name_array(users)
    users.map { |e| e.name }
  end
end

以下の通りですが、viewのロジックばっかりのモデルは少し嫌ですね。

モデルとは、データベースや外部サービスへのアクセスをはじめ、ビジネスロジック全般を担当するコンポーネントのことです。
出典 : モデルの基本

controllerのアクションmethod の外でインスタンス変数を作ってしまう

別に問題はないけど「え、こんなインスタンス作ったっけ?」ってなります。
(気づかずに同じインスタンスを作ってしまうとかも嫌ですね。)

.rb
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から呼び出されると、ロジックを追うのだけで大変になる
.rb
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のデフォルトを考え直そうという提案がされたプルリク

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を作っています。

app/presenters/base_presenter.rb
require 'delegate'

class BasePresenter < SimpleDelegator
  attr_reader :resource, :args

  def initialize(resource, **args)
    @resource = resource
    @args = args
    super(resource)
  end
end

クラス名は、どのコントローラーのどのアクションmethodなのかわかりやすいようにしています

app/presenters/users/index_presenter.rb
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で呼び出すだけです

.rb
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 パターンではありません。

弊社ではPresentercurrent_userという基本的なオブジェクトと、その他のhashを渡しています。
内部で複数のインスタンス変数が定義されたりするので、ちょっと特殊な使い方です。

所感

そういえば、Presenterを導入した記事を読むと、昔の人はrailshtmlを返却していたみたいですね。
弊社はrailsjsonを返却するAPIで、Vueでフロントを制御しています。
昔の人はどんな開発をしていたんだろうか、、、あまり想像できていない🙄🙄

11
0
0

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
11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?