over 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 系列文目錄