SexyPresenterというRails Presentater Gem作った

以前ブログに書いた、Refinementsを使ったRailsのプレゼンテーション層処理をGemにまとめた。

kmdsbng/sexy_presenter · GitHub

Gemfileに

gem "sexy_presenter"

と書いておくと使える。

このGemが提供する機能は1つだけだ。viewテンプレートの先頭にYAML形式で presenter モジュールを指定する。これだけ。

---
presenter: HeaderPresenter
---
Welcome to our web site.
Now we have <%= @customer_count %> customers. Join us!

presenterと指定したモジュールでは2つのことが書ける。

  • 既存クラスのrefineを書ける。refineで定義したメソッドは、viewテンプレートの中だけで有効化される。
  • viewテンプレート描画直前の処理を書くための before_render フックを書ける。

refineが使えるのは、単に using でモジュールをインクルードしてるから。 before_renderフックは、render :partial でviewを読み込んだ時に、データを準備したいことがよくあるのでつけた機能。結構便利。

便利な render :partial 機能に特化した CellsというGemがあるが、SexyPresenterで置き換えられると思う。

SexyPresenterはRefinementsを使ってるので、Ruby 2.1 以降でのみ使える。 また、モンキーパッチしてる関係で、現状Rails 4のみ対応してる。

SexyPresenterができてしまったので、これまで RailsのPresentation Layerライブラリとして有名だった Draper などのライブラリは、申し訳ないがすべてオワコンになってしまった。

この種のライブラリのやっていることはほぼ同じだ。モデルなどのクラスをextendしたりwrapしたりして、その場でだけ使える拡張を施す。しかしextend方式にしてもwrap方式にしても副作用があり、メンバのクラスを拡張するのが難しかったりして、痛し痒しな使い心地だった。

例えば Draper は対象モデルをPresenterでwrapするライブラリだ。Presenterでメソッドを追加し、モデルの要素にアクセスしたいときは委譲してくれる。ただ、アソシエーションまでwrapするので、生モデルと挙動が変わってしまうことがある。

ActiveDecoratorはDecoratorモジュールでモデルをextendする。extendはwrapに比べると副作用が少ないのが利点だが、モデルのメンバまではextendしてくれない。

もう一点重要な問題が、どちらの方式でも対象オブジェクトを指定するのが面倒ということだ。ActiveDecoratorはcontrollerからviewに渡される変数を勝手にextendしてくれるが、そのメンバまではextendしてくれないので、機能を使おうとするとわざわざ変数にセットしないといけなかったりして、使ってうれしいのかわからなくなってくる。しかも、これらの問題を解決するために涙ぐましい努力をしている。

だが、Rubyに Refinements が実装された現在、あるコンテキスト限定のクラスの拡張は、Refinementsを使うのが最もスマートな解法になってしまった。

Refinementsを使えば、ひとつのモジュールに、コンテキストに関連するクラスの拡張を全て記述できる。しかもusingを定義したファイルでは自動的に拡張されるため、漏れもない。作りが簡単になり、拡張のために今までやってたような苦労も必要ない。

だから、Presentation Layerを実現するのにextendしたりwrapするライブラリはもはや時代遅れになってしまった。

ではviewテンプレートを使わないケースなら使えるかというとそんなこともない。その場合は単にRefinementsを使えばいいだけなので、やはりextendやwrapは選択肢にならない。*1

まとめると、

  • Ruby2.1時代はRefinementsを使ってコンテキスト限定の拡張をするのがオシャレ。
  • SexyPresenterは今一番オシャレなイマドキRails Presentation Layer Library

ということです。

ぜひあなたのRailsアプリケーションにSexyPresenterを使ってみてください!

*1:局所的に機能を追加するというケース以外なら、もちろんextendもwrapも使えるケースはある。