【问题标题】:Rails convention - placing logic in view vs controller vs partialRails 约定 - 将逻辑置于视图 vs 控制器 vs 部分
【发布时间】:2016-07-24 19:23:16
【问题描述】:

当前用户发送的消息显示为绿色,否则显示为蓝色。遵循 Rails 约定,该逻辑属于哪里?


简介

用户会访问/group/:id看到消息列表,所以对应的视图是views/groups/show.html.erb,对应的控制器是controllers/groups_controller。 rb.

我们要显示的消息在 @group 中的数组中,如 @group.messages。数组按时间戳排序。

设置消息颜色样式的代码并不重要,但为了简单起见,我们会说有两个类选择器(一个用于 from,一个用于 to ) 我们可以简单地向消息所在的 div 添加一个类属性来更改其颜色。

用户发送和接收的消息都保存在数组@group.messages中。
如果我们有一个单独的消息存储在 message 中,我们可以测试它是否是由当前用户发送的:

if session[:user_id] == message.user_id

问题

消息按时间戳排序,并且需要按该顺序显示。出于这个原因,我看不到任何干净的方式来处理控制器中的逻辑。

我想在视图中保留尽可能多的逻辑,尤其是在局部视图之外,但在考虑了在中呈现 sentreceived 消息的选项之后不同的方式,我发现的最干净的选择是将逻辑放在 message 部分中。


处理部分消息中的逻辑:

<% if message.user.id == session[:user_id] %>
  <div class="to">
    <p> <%= message.body %> </p>
  </div>
<% else %>
  <div class="from">
    <p> <%= message.body %> </p>
  </div>
<% end %>    

优点:

  • 此方法用一个简洁明了的 if 语句处理逻辑

  • 它允许我们使代码 DRY,因为如果我们想在其他页面上使用该逻辑,我们就不必在其他任何地方使用它

  • 由于每条消息只有一个正文,因此我们不必制作另一个部分来显示没有这种格式的消息

缺点:

  • 逻辑在局部!我认为与我一起工作的人或其他程序员甚至我自己会首先查看控制器 then 在视图中的视图 then 进行任何更改或查看代码

  • 感觉不像普通的 Rails 约定


处理视图中的逻辑:

可能有两种干净的解决方案 -
1) 设置逻辑
中的消息样式 2) 为发送/接收的消息呈现不同的部分

逻辑内部的样式:

<% @group.messages.each do |message| %>
  <% if message.user.id == session[:user_id] %>
    <div class="to">
      <p> message.body </p>
    </div>
  <% else %>
    <div class="from">
      <p> message.body </p>
    </div>
  <% end %>
<% end %>   

渲染不同的部分:

<% @group.messages.each do |message| %>
  <% if message.user.id == session[:user_id] %>
    <%= render :partial => '/messages/sent_message', :message => message %>
  <% else %>
    <%= render :partial => '/messages/received_message', :message => message %>
  <% end %>
<% end %>  

优点:

  • 两种视图解决方案都将逻辑排除在局部之外

  • 在视图中决定将某物显示为一种颜色或另一种颜色是有道理的

  • 使用两个部分的视图解决方案是干净的,并且允许我们避免在逻辑中设置样式,这也意味着我们可以更改部分中的样式并影响各处消息的外观。

缺点:

  • 两个视图选项都意味着我们的代码不再是 DRY。使用这些方法意味着如果我们想在其他 3 个页面上使用相同的功能,我们将不得不再编写 3 次相同的代码

  • 视图不应该决定任何事情是有道理的

  • 使用两个部分的视图解决方案意味着我们将用部分填充视图/消息文件夹,并且仍然没有用于呈现消息的默认部分

  • 在我看来,这两种视图解决方案都感觉很脏


关于我的解决方案的要点 -

  • 没有选项允许将逻辑保存在控制器中

  • 将逻辑放在视图中意味着要在多个页面上提供相同的功能,相同的代码将被写在多个地方

  • 看起来最简洁、对我来说最有意义的选项意味着将逻辑放在部分内部,必须有更好的方法..对吗?

    李>
  • 似乎没有一个解决方案遵循 Rails 约定


我编写的三个选项中哪一个最好遵循 Rails 约定?

是否可以将逻辑放在控制器中?

有没有更好的方法来设计这个,以便有一个遵循 Rails 约定的明确解决方案?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 model-view-controller


    【解决方案1】:

    您可能已经意识到,您描述的三个版本中的每一个都不是 DRY 或不可扩展的。你已经很好地分析了每个选项的优缺点,所以我没有什么可以补充的。 :)

    为了向您的模型添加演示功能,Rails 社区使用演示器。在 Presenters here 上有一篇很棒的文章解释了更多关于他们的信息。

    基本上,你会想要一个部分的消息:

    <div class=<%=@presenter.css_class%>>
      <p> <%= message.body %> </p>
    </div>
    

    然后是演示者:

    class MessagesPresenter
      def initialize(message, current_user)
        @message = message
        @current_user = current_user
      end
    
      def css_class
        message.user == current_user ? 'to' : 'from'
      end
    
      private
    
      attr_reader :message, :current_user
    end
    

    和控制器:

    @presenter = MessagesPresenter.new(@message, current_user)
    

    瞧!演示者在视图和部分中都可用,并且是存储所有演示逻辑的好地方。

    【讨论】:

      【解决方案2】:

      由于这些示例在 CSS 类中的唯一区别,因此您重复了很多次。不能根据标签是否属于current_user来添加或删除标签上的类吗?

      这确实是一个演示问题,您可以处理这个简单的逻辑,使用装饰器 (http://johnotander.com/rails/2014/03/07/decorators-on-rails/) 显示正确的 CSS 标签。我推荐使用 Draper (https://github.com/drapergem/draper)。

      首先,为简单起见,在application_controller.rb 中添加一个current_user 辅助方法以返回经过身份验证的用户。

      添加装饰器:

      MessageDecorator.rb

      def recipient_class
        user_id == current_user.id ? "to" : "from"  # (user_id delegates to message object)
      end
      

      现在你的视图可以有更清晰的逻辑

      观看次数

      消息部分:

      <div class="<%= message.recipient_class %>">
        <p><%= message.body %></p>
      </div>
      

      主视图中的部分集合:

      <%= render partial: "message", collection: @messages, as: :message %>
      

      最后,在控制器操作中调用 decorate 处理消息:

      @messages = @group.messages.decorate
      

      编辑

      您也可以使用简单的辅助方法而不是装饰器:

      def css_class_for_message(message)
        message.user_id == current_user.id ? "to" : "from"
      end
      

      【讨论】:

      • current_user 中的application_controller.rb 是否需要作为辅助方法?我以为这使它可用于视图,而这种情况只需要它在控制器中可用,对吧?
      • 是的,为了让装饰器可以访问它,它确实需要一个辅助方法。如果使用 Draper,您还需要声明 include Draper::LazyHelpers
      • 你能证明添加装饰器而不是像recipient_class 那样向模型添加方法是合理的吗?当然,您必须使当前用户的 id 可用于该方法,或者将其传递给该方法。在这种情况下使用装饰器对我来说似乎有点矫枉过正。不过我以前从未使用过,所以我很想知道您对此有何看法。
      • 模型不关心视图,就像你说的,current_user 不可访问。您是对的,对于一个用例来说这可能是多余的,但是,我更喜欢使用像 class_for_message(message) 这样的辅助方法,而不是修改模型。
      • 您可能需要查看这篇文章以获得很好的并排比较:robots.thoughtbot.com/…@MatthewCliatt
      猜你喜欢
      • 2013-11-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      • 2013-04-23
      • 2014-03-18
      相关资源
      最近更新 更多