在Rails中将逻辑从视图移动到模型

时间:2017-04-05 20:14:29

标签: ruby-on-rails ruby

我试图更好地保持逻辑不受我的观点和模型的影响。我正在构建一个Rails应用程序。它有Review模型和Album模型。这是album/show.html.erb视图中的一些逻辑。我想将其移至Review模型,但我不确定如何做到这一点。

<% @reviews.each do |r| %>
  <div>
    <div class="review-rating" data-number="<%= r.rating %>">
    </div>
    <p class="white"><%= r.comment %></p>
    <p class="grey"><i>posted <%= time_ago_in_words(r.created_at) %> ago</i></p> 
    <% if user_signed_in? %>
      <% if current_user.id == r.user_id %> 
        <%= link_to 'Edit', edit_album_review_path(@album, r.id), class: 'grey' %> |
        <%= link_to 'Delete', album_review_path(@album, r.id), method: :delete, data: {confirm: "Are you sure?"}, class: 'grey' %>
        <br /><br />
      <% end %> 
    <% else %> 
      <br />
    <% end %>
  </div>
<% end %>

具体来说,我想移动这部分

<p class="grey"><i>posted <%= time_ago_in_words(r.created_at) %> ago</i></p> 

我对代码依赖于.each方法这一事实感到困惑。我不确定如何在模型中解决这个问题。我试过这个:

<p class="grey"><i><%= time_posted(r) %></i></p> 

review.rb模型中

def time_posted(r)
  "posted  #{time_ago_in_words(r.created_at)} ago"
end

但是Rails抛出了这个错误:

  

“#&lt;#:0x007fff2ab8a8e0&gt;”的未定义方法`time_posted'“

帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

在视图中保留 view 逻辑没有错。在您的情况下,您将按照您希望将其呈现给用户的方式迭代评论和格式化。考虑一下,如果你想改变你显示“发布”的方式,比如说,以前的数字,哪里是合理的地方去修改它?模型?不要这么认为,模型保持和操作数据,视图代表它,所以你的例子对我来说似乎很好。

答案 1 :(得分:1)

让我们暂时讨论一下,看看我们如何能够挽救你的尝试:

class Review
  def time_posted
    "posted  #{ time_ago_in_words(self.created_at) } ago"
  end
end

我们更改了方法,使其适用于self - 意味着该方法所属的对象:

<p class="grey"><i><%= r.time_posted %></i></p> 

然而它非常漂亮 - 我们从视图中移除了逻辑,但我们将其填入模型中。模型工作是跟踪业务逻辑,当你考虑实际推入轨道中的模型(验证,回调,I18n,关联,状态跟踪等)时,推送表示逻辑变得非常缺乏吸引力

那你能做什么?

1。创建一个辅助方法:

module ReviewHelper
  def time_posted(r)
    "posted  #{ time_ago_in_words(r.created_at) } ago"
  end
end

<p class="grey"><i><%= time_posted(r) %></i></p> 

基本上帮助程序只是模块 - rails使用/app/helpers中声明的所有帮助程序并使用它们来扩展视图上下文,以便您可以从视图中调用它们。它简单,并保持视图逻辑隐藏。

2。使用presenter pattern

class ReviewPresenter < SimpleDelegator
  def time_posted(r)
    "posted  #{ time_ago_in_words(r.created_at) } ago"
  end
end

class Review
  def present 
    @presenter ||= ReviewPresenter.new(self)
  end
end
<% @reviews.map(:present).each do |r| %>
  <p class="grey"><i><%= r.time_posted %></i></p> 
<% end %>

这使得它很好并且封装了,并避免将大量方法放入&#34;全局&#34;像帮助程序一样查看上下文命名空间。但是,如果它值得额外的复杂性取决于你。

有几种宝石可以使演示者模式更容易使用draper