【问题标题】:Putting presentation logic in controller is a good practice in Ruby?将表示逻辑放在控制器中是 Ruby 的一个好习惯吗?
【发布时间】:2012-07-27 04:14:05
【问题描述】:

一些推荐[1]建议你使用

<%= current_user.welcome_message %>

而不是

<% if current_user.admin? %>
  <%= current_user.admin_welcome_message %>
<% else %>
  <%= current_user.user_welcome_message %>
<% end %>

但问题是您的代码中必须有决策逻辑。

我的理解是把决定放在templatecontroller 更好,因为它让你的控制器更干净。对吗?

有没有更好的方法来处理这个问题?

http://robots.thoughtbot.com/post/27572137956/tell-dont-ask

【问题讨论】:

  • 控制器在哪里发挥作用?您的两个案例都没有涉及控制器。

标签: ruby-on-rails ruby templates presentation-layer


【解决方案1】:

您不是第一个对此感到疑惑的人。如果视图和控制器应该几乎没有逻辑,并且模型应该与表示无关,那么表示逻辑属于哪里?

事实证明,我们可以使用一种称为装饰器模式的旧技术。这个想法是用另一个包含你的表示逻辑的类来包装你的模型对象。这个包装类称为装饰器。装饰器从您的视图中抽象出逻辑,同时保持您的模型与其表示隔离。

Draper 是一个出色的 gem,可以帮助定义装饰器。

您提供的示例代码可以这样抽象:

在控制器中使用@user = UserDecorator.new current_user 将装饰器传递给视图。

您的装饰器可能如下所示。

class UserDecorator
  decorates :user

  def welcome_message
    if user.admin?
      "Welcome back, boss"
    else
      "Welcome, #{user.first_name}"
    end
  end
end

您的视图将只包含@user.welcome_message

请注意,模型本身不包含创建消息的逻辑。相反,装饰器包装模型并将模型数据转换为可呈现的形式。

希望这会有所帮助!

【讨论】:

  • 我觉得这个答案不令人满意。大多数情况下,您将在管理员、普通用户或访客(未登录)上拥有许多分支,因此您不想要一个装饰器在#welcome_message 上分支,而是需要许多对象,一些装饰器和其他可能都是 NullObjects回复#welcome_message。这样你只需要问一次它是什么类型的用户。
  • 如果提问者提到他们有两个用户类,我会制作两个装饰器。相反,我选择保留提供的界面。与通过分离数据和表示逻辑获得的清晰度相比,装饰器的委托成本相当微不足道。
  • 当然可以,但是您开始走错路了。我要说的是,即使到目前为止我只有一条信息要支持,我也不会这样写,但也许这就是我。
  • 很公平。我只是想以一种对提问者来说认知摩擦最小的方式来介绍装饰器的概念。
【解决方案2】:

我会为此使用一个助手。假设您必须根据某些语言环境翻译欢迎消息。

app/helper/user_helper.rb

module UserHelper

  def welcome_message(user)
    if user.admin?
      I18n.t("admin_welcome_message", :name => user.name)
    else
      I18n.t("user_welcome_message", :name => user.name)
    end
  end 

end

在你看来,你可以写

<%= welcome_message(user) %>

请注意,装饰器/演示器提供了一种非常干净的面向对象的方法,但恕我直言,使用帮助器更简单且足够。

【讨论】:

    【解决方案3】:

    不,您根本不希望在用户类和控制器中出现 任何 条件。博客文章中那个例子的重点是参考多态性,只是很好的老式 OO 设计。

    # in application_controller for example
    def current_user
      if signed_in?
        User.find(session[:user_id])
      else
        Guest.new
      end  
    end
    
    #app/models/user.rb
    class User
       def welcome_message
         "hello #{name}"
       end
    end
    
    #app/models/guest.rb
    class Guest
      def welcome_message
        "welcome newcomer"
      end
    end
    

    ...你明白了。

    仅,不要在模型中乱扔仅演示方法,而是创建一个充当演示者的装饰器:

    require 'delegate'
    class UserPresenter < SimpleDelegator
      def welcome_message
        "hello #{name}"
      end
    end
    

    现在current_user 看起来像这样:

    # application_controller
    def current_user
      if signed_in?
        UserPresenter.new(User.find(session[:user_id]))
      else
        Guest.new
      end
    end
    

    【讨论】:

    • 我认为模型中的视图代码与控制器中的代码一样糟糕。正如@adriandz 在他的回答中提到的那样,建议将这些方法放在装饰器中。
    • Tanzeeb,如果您真的阅读了我的全部答案,您就会知道我对将表示逻辑放入模型中的看法相同。
    • 你应该把它作为你的实际推荐,而不是最后的署名。 OP 正在寻求最佳实践,这是你教他的机会。
    【解决方案4】:

    装饰用户模型并将welcome_message直接添加到它。是的,这可能在某些时候涉及某种条件语句。

    http://robots.thoughtbot.com/post/14825364877/evaluating-alternative-decorator-implementations-in

    【讨论】:

      【解决方案5】:

      在我看来,如果文本是唯一改变的东西,它就不属于视图。如果您需要重组页面,那就是表示逻辑。这,这只是数据不同而已。

      【讨论】:

      • 如果文本需要本地化怎么办?
      • @nathanvda:然后你翻译它。 @welcome_notice = I18n.t(@user.admin? ? :welcome_notice_for_admins : :welcome_notice_for_users, :name =&gt; @user.name)。文本翻译,除了那些不变的(即可以说是设计的一部分)不应该出现在视图中。
      • @nathanvda:哦......请阅读您的答案,您所说的基本相同。那我不明白你的问题。我们都同意这不应该出现在视图中。
      • 那么您会将I18n.t 调用放在模型中吗?在控制器中?
      • @nathanvda:模型、控制器、装饰器,除了视图之外的任何地方。装饰器对于初学者来说可能有点难,所以如果它在控制器中我不会太弯曲变形。这对模型来说有点太形象了,但即使这样也比视图好。我的偏好可能类似于 cjhveal 的答案,如果需要 i18n,则插入 t() 调用(即使我的快速示例代码三个 cmets 是从控制器的角度编写的)。
      【解决方案6】:

      我认为您应该观看 Presenters 上的 railscasts 剧集来寻找答案。

      【讨论】:

        【解决方案7】:

        视图中的逻辑很难维护,我们应该将业务逻辑放在模型中,将所有视图逻辑放在助手中。

        如果您希望您的代码采用面向对象的方式,请使用装饰器(帮助程序的面向对象方式)

        最佳示例:https://github.com/jcasimir/draper

        【讨论】:

          【解决方案8】:

          将定义current_user.welcome_message 的代码放在_app/helpers/application_helper.rb_ 中,然后任何使用application 布局呈现的视图都可以访问它。

          另一种选择是定义一个 custom 辅助模块,它不一定与给定的视图或控制器相关联(请参阅下面链接的视频),并在模块中 include您希望拥有该功能的视图/控制器。

          这不是非黑即白的。但是,根据您的描述,这听起来像是在您的 application_controller.rb 中插入的代码,并且它不是具有证明其自己控制器合理性的功能的代码,最有效和最有效的选择可能是创建自定义帮助模块并将其包含在您希望拥有该功能的助手中。也就是说,这最终是应用程序的设计者(即)需要做出决定的判断调用。

          Here 是一篇很好的文章,概述了 2011 年 5 月的帮助模块

          Here 是一个 RailsCast 概述 custom 辅助模块(即,在模块中的自定义不一定与给定的控制器或视图相关联)。简短、甜美、切中要害。

          【讨论】:

          • 使用辅助方法根本不是 OP 链接到的文章的意图。
          • @adriandz 是的,但是您在哪里定义 current_user.welcome_message 的逻辑?问题不是问一种方法是否比另一种更好(显然current_user.welcome_message 在您看来比另一种更好),而是问这种逻辑应该去哪里?
          【解决方案9】:

          你可以为这些东西定义辅助方法。我认为在模型中制作欢迎句子不是一个好主意,但在控制器中也是如此。但是你应该尽量让你的视图从代码中变得干净,如果你可以使用帮助器,那么你应该这样做。

          【讨论】:

            【解决方案10】:

            一个好的做法是拥有真正的View 实例。不幸的是,Rails 对 MVP 的模仿(有区别,查一下)似乎假装视图是模板。这是错误的。

            视图应该包含 MVC 和 MVC 启发模式中的表示逻辑。他们还应该操作多个模板并决定使用哪些模板来表示模型层的状态和信息(是的,模型是一个层而不是 ORM 实例)。

            所以,回答这个问题:演示逻辑在控制器中没有位置。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-08-27
              • 1970-01-01
              • 1970-01-01
              • 2022-08-05
              • 2016-06-09
              • 2014-03-09
              • 2017-07-15
              • 2015-05-08
              相关资源
              最近更新 更多