Link Search Menu Expand Document

Part 3: 關於 View

11. 將代碼放回 controller

關於 View,最重要的守則就是在 template 中絕對沒有商務邏輯。

重構前:在 View 出現了 @posts = Post.all

<% @posts = Post.all %>

<% @posts.each do |post| %>
 <%= post.title %>
 <%= post.content %>
<% end %>

重構後:應該在 controller 中就先讀取 @posts

class PostsController < ApplicationController

  def index
    @posts = Post.all
  end

end
<% @posts.each do |post| %>
 <%= post.title %>
 <%= post.content %>
<% end %>

12. 將代碼重構到 Model

情境:在 View 中需要一些條件判斷,來決定要不要顯示某些內容

重構前:需要檢查有登入、該用戶是該篇文章作者或是編輯員

<% if current_user && (current_user == @post.user ||
                       @post.editors.include?(current_user) %>
  <%= link_to 'Edit this post', edit_post_url(@post) %>
<% end %>

重構後:這一整段條件判斷,可以重構到 Model 的一個方法。這樣 View 裡面只需要調用 @post.editable_by?(current_user) 即可,代碼乾凈又清楚。

<% if @post.editable_by?(current_user) %>
  <%= link_to 'Edit this post', edit_post_url(@post) %>
<% end %>
class Post < ActiveRecord::Base
  def ediable_by?(user)
    user && ( user == self.user || self.editors.include?(user)
  end
end

13. 將代碼重構到 Helper

情境:

  • HTML 與 Ruby 高度混雜
  • 該段程式碼有很多 if / else
  • 該段程式碼衣服穿很多層 simple_format(truncate(auto_link(@post.content), :length => 30) )

重構前:我們想要把文章內容自動加超連結、截斷只留前30字前、保留換行等等:

<%= simple_format(truncate(auto_link(@post.content), :length => 30) ) %>

重構後:抽取出一個 pretty_content helper 方法,這樣在各處 template 都可以重復使用這個 helper,而且也比較清楚。


module PostsHelper
  def pretty_content(content)
    simple_format(truncate(auto_link(content), :length => 30) )
  end
end

<%= pretty_content(@post.content) %>

那到底什麽情境適合把代碼重構到 Model? 什麽時候用 Helper 呢?如果跟 HTML 畫面顯示無關,跟商務邏輯有關,則放到 Model 裡面。如果跟 HTML 畫面顯示有關,則適合放在 Helper 裡面。一般來說,在 Model 裡面是不會處理 HTML 代碼的,這是 Helper 的事情。

14. 將代碼重構到 Partial 樣板

情境:Helper 是 Ruby 代碼,裡面不適合放太多的 HTML。如果你有一整段的 HTML 代碼想要抽取出來,應該用 Partial 樣板。

重構前:

def render_product_item(product)
  content_tag(:div, :class => "col-md-3") do
    content_tag(:div, link_to(product.title), product_path(product) ) +
    content_tag(:p, tag(:hr)) +
    content_tag(:span, product.price + "元")
  end
end
  <%= render_product_item(@product) %>

重構後:應該改用 partial 而不是 helper。


<div class="col-md-3">
  <div><%=link_to(product.title), product_path(product) %></div>
  <p>
    <hr>
    <span><%= product.price %></span>
  </p>
</div>
  <%= render :partial => "item", :locals => { :product => @product } %>

15. 使用 Partial 盡量用區域變數取代物件變數

情境:在使用 Partial 時

class Post < ApplicationController
  def show
    @post = Post.find(params[:id)
  end
end

重構前:在 action 中宣告的物件變數例如 @post,會穿透到這個 template 內所有使用的 partial。雖然很方便,但是如果 partial 內要用到的變數很多,就會搞不清楚到底要準備哪些變數才能使用這個 partial。

<%= render :partial => "sidebar" %>
<%= @post.title %>

重構後:將 @post 傳入這個 partial,這樣在這個 partial 裡面,就會變成區域變數 post。這個好處是可以增加這個 partial 的可重復使用性,也比較清楚要傳這些變數才能使用。

<%= render :partial => "sidebar", :locals => { :post => @post } %>
<%= post.title %>

16. 整理 Helper 檔案

情境:每次 rails g controller XXX 時,Rails 都會自動新增對應的 XXX_helper.rb 檔案

重構前:很多 Helper 檔案打開來裡面都是空的,沒有用到

app/helpers/user_posts_helper.rb
app/helpers/author_posts_helper.rb
app/helpers/editor_posts_helper.rb
app/helpers/admin_posts_helper.rb

重構後:簡化集中到少數的 Helper 檔案即可。這些 Helper 檔案跟 Controller 並沒有對應的關系,所有 Helper 檔案裡面宣告的方法都是通用的,不會因為放在哪一個 Helper 檔案而有差異。因此我們可以重新編排整理。

app/helpers/posts_helper.rb

Copyright © 2010-2021 Wen-Tien Chang All Rights Reserved.