Rails 4.0.0
ruby 2.0.0p247
Draperとは?
具体例を書いた方が分かりやすいと思うのでコードで。
Draperなし(helper頼み)
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
end
end
module ProductsHelper
def product_name_with_hoge(product)
"#{product.name} hoge"
end
end
%p=product_name_with_hoge(@product)
みたいな感じになってしまうので、ここだけオブジェクト志向の世界から抜け出た感じになってしまいます。
viewのコードにproductって単語が繰り返されているのもまた気持ち悪いです。
でも、name_with_hoge
みたいなメソッド名にしてしまうと別のhelperと競合してしまう可能性があるので仕方ないですね。
Draperあり(helperなし)
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id]).decorate
end
end
あらたに#decorate
を呼び出しています。
class ProductDecorator < Draper::Decorator
def name_with_hoge
"#{object.name} hoge"
end
end
%p=@product.name_with_hoge
Viewのコードがスッキリになりましたね。
なにより直感的なコードになったと思います。
helperはちょこっと何かをしたいという時は便利だと思うのですが、Viewがゴチャゴチャしがちな気がします。
ほかにできる便利なこと
helperにアクセスできる
class ProductDecorator < Draper::Decorator
def name_with_icon
content = h.content_tag(:i, nil, class: 'icon-user')
content << object.name
end
end
#h
で既存のhelperにアクセス出来ます。
Railsは便利なhelperがたくさんありますからね。
関連モデルを自動的に#decorate
する
class ProductDecorator < Draper::Decorator
decorates_association :price
end
class PriceDecorator < Draper::Decorator
def created_at_with_hoge
"#{object.created_at} hoge"
end
end
%p=@product.price.created_at_with_hoge
のようにいちいち#decorate
しなくても勝手にdecoratedなmodelがもらえます。
attributeをフィルターする
Viewからアクセスできるattributeを制限したい場合などもたぶん良いです。
アクセスしたいattributeだけを明示的に#delegate
します。
(この場合、ふだん#delegate
に必要なto:
は推測できるので省略できます)
class ProductDecorator < Draper::Decorator
delegate :name
end
そんなこと気にしない場合は、
class ProductDecorator < Draper::Decorator
delegate_all
end
とすれば、Decoratorに定義されていないメソッドはmodelに渡されます。
共通のメソッドはmoduleで書きだしたり
Rails4(37signales)なスタイルを真似て、decorators/concerns
みたいなディレクトリ作って書き出してもいいかもね。
module TimestampsDecoration
def created_at
object.created_at.strftime('%F %R')
end
end
config.eager_load_paths += Dir[Rails.root.join('app', 'decorators', 'concerns')]
class ProductDecorator < Draper::Decorator
include TimestampsDecoration
end
でも、TimestampDecoration
に対してconcerns
ってフォルダ名は適切なじゃない気もするな。。
まとめ
modelに属さない便利メソッドはまたhelperとして居続けていいと思うんですよね。
ただ、modelにname_with_hoge
みたいなメソッドがあると気持ち悪し、helperに書いてもイマイチなので、decorator(presenter)として書き出すのがいいんじゃないかなと思います。
DraperのほかにもActiveDecorator
ほかに、同様のことが出来るものにamatsudaさんのActiveDecoratorがあります。
その他は、Ruby Tool Box - Rails Presentersにありますが、ActiveDecoratorかDraperを選ぶ人が多そうです。
好みでどちらを使うか選んだらいいかなと思います。
あとがき
ほんとうはconcerns
のところだけ書く予定が、Draperの記事がQiitaに見当たらなかったので、まるっと書いてみた次第です。
ほとんどREADME.md
の和訳だが…
詳しくは、drapergem/draperを参照のこと!