重構 MVC; 代碼分享工具(重構,改進,打分)

include 模塊和 extend 模塊的不一樣:
 
Class Extension: 經過向singleton class中加入Module來定義class method,是對象擴展的一個特例。
(由於類是特殊的對象。)

例子: html

class C; end
 
module M
  def my_method
    'a class method'
  end
end
#用extend方法: class C; extend M; end
class << C
  include M
end
 
p C.my_method   #=> "a class method"

 

Model和controller.git

  • 若是 Model 超過 3 個 PageDown(3個屏幕頁面放不下),拆 Module
  • model能夠include進class(增長實例方法),  類能夠extend model(增長類方法。)。
  • 一個Controller的action若是超過15行,太fat,就應當重構。
  • 若是 Controller 每一個 action 都有重複的code,使用 before_action
  • 若是多個 Controller 有一樣的少樣幾行 action,如 Admin,考慮用 繼承inheritance

helper的使用:
 

Tips 1 : 預先封裝會重複寫不少次,須要改格式的欄位

標題,圖片,按鈕(鏈接),敘述。
例子:
app/helpers/orders_helper.rb
  def render_order_created_time(order) order.created_at.to_s(:short) end 

這樣若是之後全站的訂單時間格式須要改,那麼只要改一處就好了。github

 

Tips 2: 不要 HTML 與 Ruby 混雜寫 View

 儘可能都用 Helper 輸出邏輯判斷類的代碼。

 小結,須要使用helper的地方:數據庫

  • HTML 與Ruby 高度混雜
  • 該段程式碼有不少 if / else
  • 該段程式碼衣服穿不少層 simple_format(truncate(auto_link(@post.content), :length => 30) )
 注意:不要過分使用helper
  • content_tag標籤用了2個以上。
  • 有純html代碼 
  • 有html_safe方法

以上狀況能夠用partial提煉。編程

 

使用 Partial 的原則api

  • 若是 View 超過 2.5 個 PageDown 請拆 Partial
  • 若是元件須要被複用,也是拆 Partial
  • 特殊元件可拆 partial
  • 登入 / 登出 navbar
  • Google Analytics
 
code climate測試app的代碼得分(重構)  https://codeclimate.com/
 
 
 關於Controller的重構:
 
1. 善用Model scope,對query SQL能夠在model層中使用scope定義好它,在controller內直接調用。
 
2. 用association method build()新建關聯對象。
  def create @post = current_user.posts.build(params[:post]) @post.save end
 
3.model虛擬屬性:
利用在model對某個名字定義一套存取方法,操做沒有在數據庫中存在的字段,曾爲虛擬屬性。
class User < ActiveRecord::Base def full_name [first_name, last_name].join(' ') end def full_name=(name) split = name.split(' ', 2) self.first_name = split.first self.last_name = split.last end end 


在view中:
#當表格提交時調用了full_name=(XXX)方法,而後獲得了first_name和last_name兩個參數。
<% form_for @user do |f| %>
  <%= f.text_field :full_name %>
<% end %>
在controller🀄️:
class UsersController < ApplicationController
 def create @user = User.create(params[:user]) end 

 

 
4. 使用model的callback功能,提取controller中的action代碼。
 
情景: 新增文章時,check_box_tag的auto_tagging, 若是打勾表示自動下標籤。
重構前: 須要先在action中檢查params[:auto_tagging], 而後調用model方法產生標籤。
<% form_for @post do |f| %>
  <%= f.text_field :content %>
  <%= check_box_tag 'auto_tagging' %>
<% end %>
class PostController < ApplicationController def create @post = Post.new(params[:post]) if params[:auto_tagging] == '1' @post.tags = AsiaSearch.generate_tags(@post.content) else @post.tags = "" end @post.save end end
重構後: 新增虛擬屬性 auto_tagging, 和一個回調before_save來檢查是否自動產生標籤。
class Post < ActiveRecord::Base attr_accessor :auto_tagging before_save :generate_taggings private def generate_taggings unless auto_tagging == '1' self.tags = Asia.search(self.content) end end 
<% form_for :note, ... do |f| %>
  <%= f.text_field :content %>
  <%= f.check_box :auto_tagging %>
<% end %>
class PostController < ApplicationController def create @post = Post.new(params[:post]) @post.save end end 
 
5.  把action中的邏輯放到Model層,設置一個好名字的方法。在contorll/action中調用這個方法就好。
 
6.  Factory method工廠方法。和5相似,把邏輯都寫在model中的一個 類方法中。
class Invoice < ActiveRecord::Base def self.new_by_user(params, user) invoice = self.new(params) invoice.address = user.address invoice.phone = user.phone invoice.vip = ( invoice.amount > 1000 ) if Time.now.day > 15 invoice.delivery_time = Time.now + 2.month else invoice.delivery_time = Time.now + 1.month end return invoice end end 
class InvoiceController < ApplicationController def create @invoice = Invoice.new_by_user(params[:invoice], current_user) @invoice.save end end
 

 
關於Modle: 善用Module抽取相關代碼。
 
 
若是model層很多有高度相關的代碼,但願一眼就能夠看到它們是在一塊兒的,或者這些代碼能夠用在別的Model中。就能夠抽取它們放入   app/models/concerns 目錄下(相似view的render partial)
 
controller,在 rails 中  app/controllers/concerns 目錄就是拿來放 controller 的 module 檔案
 
 
 
  
app/models/concerns/has_cellphone.rb
module HasCellphone
 def self.included(base) base.validates_presence_of :cellphone base.before_save :parse_cellphone base.extend ClassMethods end def parse_cellphone # do something end module ClassMethods def foobar # do something end end end 

或是使用 Rails ActiveSupport::Concern 語法,能夠更簡潔一點:ruby

app/models/concerns/has_cellphone.rb
module HasCellphone extend ActiveSupport::Concern included do validates_presence_of :cellphone before_save :parse_cellphone end def parse_cellphone # do something end class_methods do def foobar # do something end end end 

最後在 model 裏面 include 便可。markdown

class User < ActiveRecord::Base include HasCellphone end 
 
 
 
 
關於View
 
關於 View,最重要的守則就是在 template 中絕對沒有商務邏輯。
 
那到底什麼情境適合把代碼重構到 Model? 何時用 Helper 呢?
 
若是跟 HTML 畫面顯示無關,跟商務邏輯有關,則放到 Model 裏面。
若是跟 HTML 畫面顯示有關,則適合放在 Helper 裏面。
通常來講,在 Model 裏面是不會處理 HTML 代碼的,這是 Helper 的事情。
 
 
Helper 是 Ruby 代碼,裏面不適合放太多的 HTML。
若是你有一整段的 HTML 代碼想要抽取出來,應該用 Partial 樣板。
 
Partial中使用區域變量代替實例變量。
用全寫的:  <%= render :partial => "sidebar", :locals => { :post => @post } %>
不要用簡寫:<%= render :partial => "sidebar" %>
 
整理Helper檔案:
能夠整理一下,集中在一個helper中使用,由於和Controller沒有對應關係。全部Helper中的方法都是同用的。
 


分析工具:
  • Rubocop 是一個 gem 能夠分析 Rails 代碼,建議一些能夠重構的地方
  • CodeClimate 是一個線上的工具,能夠爲項目評分,並建議哪裏須要修改。
 
補充:

當你仍不知足 Rails 的 concerns 機制時,你會須要更多面向對象的知識,在編程語言教程中有介紹到。關於 Rails 的部分推薦如下補充資料:app

相關文章
相關標籤/搜索