Railsにpresenter層を導入する

  • 10
    いいね
  • 0
    コメント

あるオブジェクトに関連するhtmlコードを生成するクラスです。
htmlの中でrubyを実行するのではなくrubyでhtmlを生成するという逆転の発想です。
雑然としたhtmlテンプレートを整理することができます。

presenterのメリット

  • 雑然としたhtmlテンプレートを整理する
  • 重複したhtmlコードをメソッドとして抽出することで機能追加やデバッグやリファクタリングがしやすくなる
  • 色々なところでフォームの塊等を使い回すことができる

decoratorとの違い

  • decoratorは値を返す、ドメイン層寄り
  • presenterはhtmlを返す、ユーザーインターフェース層寄り

参考コード

presenterの大元のクラス

presenter.rb
class Presenter
  attr_reader :view_context
  delegate :link_to, :fa_icon, :params, :sort_link, :number_to_currency,
    :time_ago_in_words, to: :view_context

  def initialize(view_context)
    @view_context = view_context
  end

  private

  def markup(tag_name = nil, options = {})
    root = Nokogiri::HTML::DocumentFragment.parse('')
    Nokogiri::HTML::Builder.with(root) do |doc|
      if tag_name
        doc.send(tag_name, options) do
          yield(doc)
        end
      else
        yield(doc)
      end
    end
    root.to_html.html_safe
  end
end

presenterクラスを継承してrooms_presenterを作る

rooms_presenter.rb
class RoomsPresenter < Presenter
  delegate :receive_call_logs_path, :room_path, to: :view_context

  def initialize(rooms, view_context)
    @rooms           = rooms
    super(view_context)
  end

  def render
    markup(:table, id: 'table-id' class: 'table-class') do |m|
      m.thead do
        header_components(m)
      end
      m.tbody do
        @rooms.each do
          body_components(m, room)
        end
      end
    end
  end

  private

  def header_components(m)
    "よしなに"
  end

  def body_components(m, room)
    "よしなに"
  end
end

rooms_presenterをview側でnewする

rooms/index.haml
= RoomsPresenter.new(@rooms, self).render

まとめ

Rubyでhtmlを生成するというプロセスなので、テンプレートエンジンよりRubyの良さを活かしやすい(例えば継承してコードを使いまわすとか)ですが、presenter最大のメリットはhtmlの塊に名前をつけて管理できるところだと思います。それだけでこのviewはどういう意図があるのか?がより理解しやすくなるので。

参考文献・参考図書