draperやactive_decoratorを使ってViewに書いてしまったロジックを減らす
ruby on rails

draperやactive_decoratorを使ってViewに書いてしまったロジックを減らす

このエントリーをはてなブックマークに追加

ワールドカップ面白かったですね。各試合を楽しんで観ていました(ダイジェストですが)技術推進室の中村です。ドイツ強かった。

Railsで開発していると、Modelの値をViewに表示する時に、そのまま表示するのではなく、ロジックを書いて整形して表示するということがあると思います。 そういう場合helperを使うというのも手ではありますが、最近ではMVC(Model View Controller)のViewとModelの間にPresenter(Decorator)という層を設けて、Viewで行なっていたロジック(特にModelに関連するロジック)をPresenterに任せるやり方をよくします。 Presenter(Decorator)については下記を参考にしてください。

  • Presenter and Decorator in Rails
  • Exhibit vs Presenter

  • Railsではそういう時に使うgemとしてdraperactive_decoratorがあります。ここではdraperとactive_decoratorそれぞれについて簡単に紹介したいと思います。

    draperを使ってみる

    まずはインストールから。

    1.Gemfileにgem ‘draper’を追加してbundle installを実行します。

    gem 'draper'
    
    bundle install
    

    これでインストールは終わりです。

    2.続いてRailsのgenerateコマンドを使ってファイルを生成します。今回はUserというModelに対するPresenterを作成します。

    bundle exec rails g decorator user
    

    するとapp/decorators/user_decorator.rbが生成されます。

    3.続いて生成されたapp/decorators/user_decorator.rbにメソッドを追加します。今回は名前を最初の5文字で切るメソッドを追加します

    https://gist.github.com/3a708aeefb19698f1bb1

    このshort_nameメソッドの中のobjectにはUserDecoratorクラスのインスタンスが入っています。

    4.最後にControllerです。このUserDecoratorを使ってUserの値を取得するように変更します。今回は詳細表示の画面の時に呼ばれるshowメソッドの中を変更します。

    https://gist.github.com/b72bf985153ea502cac1

    これでshow.html.erb(詳細ページのテンプレート)の中では@user.short_nameとするだけでname属性の値のうち最初の5文字が表示されます。

    https://gist.github.com/nakaearth/1bea0e1edb92d56b317b

    次にページングのgemと一緒に使う場合についてふれたいと思います。今回はkaminariを使います。

    Railsでページングを実装する場合、最近はkaminariというgemを使うケースが多いと思います。

    で、このkaminariを使って取得したModelの配列(例えば今回の例のようにUserというModelの場合、User.page(1).per(20)で取得したUserの配列)の一要素に対して、draperで定義したメソッドを適用するには一工夫必要です。何もしなければエラーが出てうまく動作しません。

    じゃ、何をすれば良いのか・・・。 それはズバリ、config/initializers/draper.rbを作成しこれに以下のコードを追加すれば良いのです。それだけです。

    https://gist.github.com/7211c2639cfa7bc5a3b7

    この件はここに詳細が書いてありますので、興味のある方はご一読していただければと思います

    次にController側で以下のようにUserDecoratorを使うようにコードを修正します。

    https://gist.github.com/7ccd6c62a4939815a874

    で、ちゃんとshort_nameメソッドが使えるかindex.html.erbを表示してテストしてみると使えることが確認できると思います。

    https://gist.github.com/e4bec6a770db1b10fc57
    https://gist.github.com/0ea0988de9d251319e6d

    active_decoratorを使ってみる

    それでは次にactive_decoratorを使ってみましょう。active_decoratorもbundle installした後、以下のようにrailsのgenerateコマンドをたたくとapp/decorators以下にファイルが生成されます。

    gem 'active_decorator'
    
    $ bundle install
    $ bundle exec rails g decorator user
    
    https://gist.github.com/d366a9ec36618ba7e572

    作成されたapp/decorators/user_decorator.rbに上のサンプルと同じにshort_nameメソッドを追加します。

    https://gist.github.com/cfcaf64e3111f861f421

    追加したら、View側でその追加したメソッドを使うようにしてみましょう。上の例と同じようにshow.html.erbとindex.html.erbを変更します。

    https://gist.github.com/598ae921f81c68f595b1
    https://gist.github.com/d69f2ec7eb314dd29cc1

    たったこれだけです。draperに比べてそれ程多くの記述をしなくても、同じようなことができます。

    どちらを使うか

    active_decoratorのコードを見てみるとAbstractController:: Renderingにview_assigns_with_decoratorメソッドを追加してalias_method_chainを使ってview_assignsメソッドを置き換えています。更にViewのソースも一部同じようにメソッドの置き換えをしています。

    このメソッドの置き換えにより利用する側は多くのコードを追加・変更する事なくactive_decoratorの機能を使えるようになっています。 その点draperは自分で使う機能をDecoratorに取り込む必要があります(今回の場合だったらdecorates_findersを)。利用する側のコードの追加・変更も若干増えます。

    ただdraperはactive_decoratorに比べて機能が豊富です。更にdraperはModelをDecoratorクラスがラップする形になっていますので(今回の例でいうとUserクラスをUserDecoratorがラップしている)、元々Modelにあるメソッドと同じ名前のメソッドをDecoratorに定義しても、どちらも使えます。逆にactive_decoratorはModelに既にあるメソッドと同じ名前のメソッドを定義したらエラーが発生します。

    https://gist.github.com/nakaearth/be4ed234a444e64168c0

    Modelの値を整形して表示する必要がある場合や表示するのにロジックが発生する場合など、今回紹介したdraperやactive_decoratorを使って実装するとView側のソースが整理されて良いと思います。


    名無しのエンジニア
    MySQLがオンラインALTER TABLEでOOM Killerに殺されたはなし
    GMOメディアとスクラム