about 11 years ago

這一篇的重點是 Object-Oriented View。

14. Decorate using Decorator ( don’t put everything in model )

在前面我們介紹了幾個手法,包括 將 Logic 收納到 Helper 裡面

def render_article_publish_status(article)
  if article.published?
    "Published at #{article.published_at.strftime('%A, %B %e')}"
  else
    "Unpublished"
  end
end

以及 將 Helper 裡面的 Logic 重新整理到 Model

class Article < ActiveRecord::Base
  def human_publish_status
    if published?
      "Published at #{article.published_at.strftime('%A, %B %e')}"
    else
      "Unpublished"
    end
  end
end

但是,再怎麼整理,Model 還是會肥起來:

class Article < ActiveRecord::Base
  def human_publish_status
  end

  def human_publish_time
  end

  def human_author_name
  end

  ........
end

最後你只好把這些 Logic 又抽出成 Module:

class Article < ActiveRecord::Base
  include HumanArticleAttributes
end

等等...這樣好像有很大的問題?XDDDDD 這些程式碼其實大部分都是 View 裡面的 Logic,怎麼到最後都變成 Model 裡面的東西。

Drapper ( Decorators/View-Models for Rails Applications )

我們可以用 Decorators/View-Models 解決這樣的問題。因為這本來就是屬於「View 層次」的東西。

有一個還不錯的 Gem 叫 Draper 可以進行這樣的抽象整理。

其實開發者最希望 View 裡面只要有一行

  <%= @article.publication_status %>

這樣就好了。

我們可以透過 Draper 的 DSL,做到這樣的封裝。

class ArticleDecorator < Draper::Decorator
  delegate_all

  def publication_status
    if published?
      "Published at #{published_at}"
    else
      "Unpublished"
    end
  end

  def published_at
    object.published_at.strftime("%A, %B %e")
  end
end

然後在 Controller 裡面呼叫 decorate 就可以了

def show
  @article = Article.find(params[:id]).decorate
end

15. Decoration using View Object

另外一種作法是把 View 裡面複雜的邏輯抽成 View Object

這是一個 event 頁面。在這個頁面裡面,如果當前 User 是 event host,則顯示 "You",否則顯示 Host name。且參加者裡面也要剔除當前 User。

<dl class="event-detail">
  <dt>Event Host</dt>
  <dd>
    <% if @event.host == current_user %>
      You
    <% else %>
      <%= @event.host.name %>
    <% end %>
  </dd>
  <dt>Participants</dt>
  <dd><%= @event.participants.reject { |p| p == current_user }.map(&:name).join(", ") %></dd>
</dl>

寫成 Helper 實在是有點囉唆。我們不如改用 View Object 進行整理。

class EventDetailView
  def initialize(template, event, current_user)
    @template = template
    @event = event
    @current_user = current_user
  end

  def host
    if @event.host == @current_user
      "You"
    else
      @event.host.name
    end
  end

  def participant_names
    participants.map(&:name).join(", ")
  end


  private

  def participants
    @event.participants.reject { |p| p == @current_user }
  end
end

則 View 就可以很漂亮的被收納成以下:

<dl class="event-detail">
  <dt>Host</dt>
  <dd><%= event_detail.host %></dd>
  <dt>Participants</dt>
  <dd><%= event_detail.participant_names %></dd>
</dl>


Maintainable Rails View 系列文目錄

← Maintainable Rails View (3) Maintainable Rails View (5) →
 
comments powered by Disqus