HelperとDecoratorの使い分け

はじめに

先日、会社の人からHelperとDecorator(デコレーター)の違いについて教えてもらったのでメモがてらまとめてみます。

両者とも共通しているのはビューにロジックを書かないために使われるものですね。

RubyというかRailsにおいてビューの役割はコントローラーから渡されたデータを表示するだけなので、データを加工したり操作をしてはいけません。

なのでロジックに関するものはhelperで全部書いてしまえばいいのではないかと思いますが、そうなるとHelperがfat化になる可能性もあるし、単一責任の原則を厳密には守れていないみたいです。

この時に使うのがDecoratorになります。

両者をどのように使い分けるかですが、HelperはModelに依存しないロジック、DecoratorはModelに依存するロジックをそれぞれ書きます。

Modelに依存しないロジックとは日時表示やparamsなどで、Modelに依存するロジックとはその名の通りModelのデータ表記に関するものです。

ModelとHelperとDecoratorの関係図

Image from Gyazo

説明ばかりしてもわかりづらいのでコードを書いてみます。

Helper

まずはHelperからです。

今回はHelperには日にちのロジックを書きます。

app/helpers/application_helper.rb

module ApplicationHelper
 # 受け取った引数を年月日曜日で表示するメソッド
  def year_month_day(date)
    date.strftime("%Y年%m月%d日(#{%w(日 月 火 水 木 金 土)[date.wday]})")
  end
end

ビュー(Haml)

= year_month_day(Time.now)

ブラウザではこのような表記になります。

Image from Gyazo

Decorator

次にDecoratorです。

Decoratorを使うためにはdraperというgemをインストールする必要があります。

gemfile

gem 'draper'

今回はすでにモデルは定義されているので、rails g decoratorでDecoratorファイルを作成します。

$ rails g decorator 作成したいファイル名

Decoratorファイルにメソッドを定義します。

app/decorators/user_decorator.rb

class UserDecorator < Draper::Decorator
  delegate_all

 # Userテーブルに紐付いているlevelテーブルのidによって条件分岐をして表示する値を変える。
  def post_ranking
    if level.id > 10
      "すごい"
    else
      "まだまだだね"
    end
  end
end

次にビューファイルで呼び出します。

app/views/users/show.html.haml

   #(htmlの)クラス名=インスタンス変数(呼び出し元は状況に応じて変わる).Decoratorで定義されたメソッド
    .user-content__questions=@user.post_ranking

これでブラウザを表示するとこうなります。

Image from Gyazo

うーん、定義しているのに定義していないと言われますね。。。

調べるとコントローラでインスタンスに対してdecorateをつけるとDecoratorファイルに定義されたメソッドが呼び出すことができるようになります。

コントローラでメソッドを呼び出したいインスタンスにdecorateをつけてみます。

app/controllers/users_controller.rb

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id]).decorate
  end
end

再度、ブラウザを読み込みます。

Image from Gyazo

おっ!ちゃんと呼び出されていますね!

これはdecorateをつけることでモデルのインスタンスをDecoratorクラスのインスタンスに変えること(モデルをDecoratorに組み込む)で、Decoratorファイルのメソッドを使えるようにしているってことなのかな(自信ない)

イメージとしてはこんな感じ

Image from Gyazo

Decoratorファイル内にあるdelegate_allはモデルにあるメソッドをDecoratorクラスにも使えるようにするための記述のようです。

この記事を書いた段階での、自分の中での使い分け基準としてはインスタンス.メソッドで呼び出せるものはDecoratorに定義して、呼び出せないのはhelperに定義するって言われるとしっくりくるかな。

helperで定義したメソッドの引数にインスタンスを渡すのはなんか違和感あるから、Decoratorは使ったほうがいいなと思いました。

まとめ

・HelperはModelに依存しないロジックを書く

・DecoratorはModelに依存するロジックを書く

参考

github.com

qiita.com

tech.kitchhike.com