RailsのDraperを使用してトッピング機能を実装してみた

今回は、Rails で Draper を使用して Decorator を実装してみた。

今回やりたいこと

前回、RubyGoFのDecoratorパターンを勉強したので、今回はRailsでも実装してみたいと思う。
※とりあえず、前の記事(Rubyで書くDecoratorパターン - たくあんポリポリ)で書いたのと同じで、アイスにカシューナッツをトッピングできるものを目指す。

今回の環境

  • cloud9
  • Rails 5.0.2
  • ruby 2.3.0p0
  • mysql 5.5.53 
  • Draper 3.0.0.pre1 ※Rails5ではこれ以降じゃないと使用出来ない

実装

Helper版

Controller
class IcesController < ApplicationController
  def show
    @ice = Ice.find(params[:id])
  end
end
View
<h1>Ices#show</h1>
<p><%= topping(@ice) %></p>
Helper
module IcesHelper
  def topping(ice)
    "CashewNuts #{ice.name} Icecream"
  end
end
Result

f:id:c_taquna:20170601003054p:plain

Draper版

Controller
class IcesController < ApplicationController
  def show
    @ice = Ice.find(params[:id]).decorate
  end
end
View
<h1>Ices#show</h1>
<p><%= @ice.topping %></p>
Decorator
class IceDecorator < Draper::Decorator
  delegate_all

  def topping
    "CashewNuts #{ice.name} Icecream"
  end
end
Result

f:id:c_taquna:20170601003054p:plain

Helper版とDraper版

app/helper 以下に書かれたメソッドは全てのViewから読み出すことが可能。つまり、Helperを使用する限り、別のhelperに同じtoppingという名前のメソッドが存在してしまうとメソッド名が競合してしまい、表示が思った通りにいかない可能性がある。そういった状態にならないようにするためには、アイスの他にクレープのtopppingメソッドがほしかった場合は以下のように書く必要がでてくる。

アイスの場合
  def ice_topping
    "CashewNuts #{ice.name} Icecream"
  end

topppingの前に"ice_"をつけている。

クレープの場合
  def crepe_topping
    "Strawberry #{crepe.name} Crepe"
  end

topppingの前に"crepe_"をつけている。

しかし、どうもiceやcrepeがたくさん出てきていて気持ち悪い。そのような場合にDraper版を使用する。これは、アイスやクレープのオブジェクトを、Decoratorオブジェクト化してインスタンス変数に格納する。Viewでインスタンス変数を使用して、クラス内に書かれているtoppingメソッドを実行する。つまり、同じメソッド名でかいてもお互いに影響しあわない。そうなるとtopping前の"ice""crepe"は削除できる。(これできれいになりました。)

結局、どんな時につかうの?

いろいろなところで言われているが、Viewに表示する内容をちょこっとだけ装飾したいとき。今回はIcecreamの前にCashewNutsをつけた。
あと、helperが混沌としてきて、名前の競合とかが怖くなってきたらDraperで整理できるとよいかな。