使用 SimpleDelegator 装饰模型

大多数 Rails 开发人员首先在模板中修改他们的模型信息:

<h1><%= "#{ @user.first_name } #{ @user.last_name }" %></h1>
<h3>joined: <%= @user.created_at.in_time_zone(current_user.timezone).strftime("%A, %d %b %Y %l:%M %p") %></h3>

对于具有大量数据的模型,这可能很快变得麻烦并导致从一个模板到另一个模板的复制粘贴逻辑。

此示例使用 stdlib 中的 SimpleDelegator

默认情况下,对 SimpleDelegator 对象的所有请求都将传递给父对象。你可以使用表示逻辑覆盖任何方法,也可以添加特定于此视图的新方法。

SimpleDelegator 提供了两种方法:__setobj__ 用于设置要委托的对象,__getobj__ 用于获取该对象。

class UserDecorator < SimpleDelegator
  attr_reader :view
  def initialize(user, view)
    __setobj__ @user
    @view = view
  end

  # new methods can call methods on the parent implicitly
  def full_name
    "#{ first_name } #{ last_name }"
  end

  # however, if you're overriding an existing method you need
  # to use __getobj__
  def created_at
    Time.use_zone(view.current_user.timezone) do
      __getobj__.created_at.strftime("%A, %d %b %Y %l:%M %p")
    end
  end
end

一些装饰器依靠魔术来连接这种行为,但是你可以通过初始化页面上的对象使表达逻辑的来源变得更加明显。

<% user = UserDecorator.new(@user, self) %>
<h1><%= user.full_name %></h1>
<h3>joined: <%= user.created_at %></h3>

通过将对视图对象的引用传递给装饰器,我们仍然可以在构建表示逻辑时访问所有其余视图助手,而无需包含它。

现在,视图模板只关注将数据插入页面,而且更加清晰。