開發新手最容易犯的50個 Ruby on Rails 錯誤(1)

【編者按】本文最早發佈與 JETRuby 博客,主要介紹了開發新手最容易犯的 Ruby 錯誤。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。

一年前,我們創立了以 “Rubyboost” 爲名的 Ruby on Rails 課程。簡而言之,本課程的目標是使對編程瞭解不多的新手也能在兩個月內,提升技能、成爲初級開發者。在成功完成課程之後,學生會收到爲其兩個月的實習邀請,實習地點就在我們公司。如果一切順利,就會得到聘用。不得不說,這是一種相對公平且簡單的成爲職業開發者的道路,你覺得呢?

順帶說一句,你根本想不到,有多少人願意來參加並學習 Rails 編程!

在分析了所有受訓者編寫的代碼之後,我們總結了50個最常見的錯誤!更糟糕的是,每個小組所犯的錯誤與前一組的錯誤幾乎一模一樣。

以下是 Rails 新手常常忽略或做錯的地方。我們還包含了“對“,”錯”兩個版本的代碼樣本,使得教程更爲清楚。

1、他們不使用自動生成的方法

############
## WRONG ##
############  

if course.visible    
  # do something
  end

##############
##  RIGHT   ##
##############  

if course.visible?    
  # do something
  end

通常,Rails 和許多 gems 會爲它們使用的對象添加一些有用的幫助方法。例如,Rails 會自動爲布爾字段添加聲明。通常,這些方法的名字是以問號結尾的。請牢記這一點!

2、他們不知道“N+1”查詢來自何處

#############
##  WRONG  ##
#############  

 @homeworks = lesson.homeworks

  - @homeworks.each do |homework|
    %p homework.user.email

#############
##  RIGHT  ##
#############  

  @homeworks = lesson.homeworks.includes(:user)

  - @homeworks.each do |homework|
    %p homework.user.email

瞭解 ORM 如何與數據庫交互是非常重要的。但是,新手往往沒有這種瞭解。因此,他們很少使用 “includes”、“preload” 與 “eager_load” 這類方法,並且對 “bullet” gem 一無所知。

在第一個例子中,N+1 查詢會傳遞至數據庫。”N” 是已經完成的家庭作業數量。查詢數量可能是10、20甚至100。而在第二個例子中,只有2個查詢!

3、他們不用 scopes(域)

############
## WRONG  ##
############  

def index    
  @lessons = Сourse.lessons.order(position: :asc)  
end

############
##  RIGHT ##
############  

class Lesson < ActiveRecord::Base
    belongs_to :course

    scope :by_position, -> { order(position: :asc) }  
 end  

 def index    
   @lessons = course.lessons.by_position  
 end

Scopes 允許你隱藏數據庫的實現,並將代碼唯一化(uniqualize)。而且,代碼的可讀性也會大幅提升,因爲他們透露了開發者的意圖,而非數據庫的結構。

4、他們不瞭解 “after_create” 與 “after_commit” 間的差別

模型的數據,包括其在 “after_create” 中的新 ID,可以從內部,而非外部進行讀取,原因是交易尚未完成。

如果我在數據庫中創建了一條記錄,之後打算將其 ID 放入 redis 或任意的存儲中,會得到以下結果:

  • 如果 ID 在交易完成之前使用,“after_create” 可能會導致無效數據。

  • 藉助 “Sidekiq” 或其他任意後臺工作,我總是可以使用 “after_commit” 確保數據的完整性。

5、他們總是使用 ORM

#############
##  WRONG  ##
#############

  Article.all.each { |article| article.delete }

  Article.all.map { |article| article.title }

  Course.all.select { |course| course.created_at < 5.years.ago }.each { |course| course.articles.delete_all }

  #############
  ##  RIGHT  ##
  #############

  Article.delete_all

  Article.pluck(:title)

  old_courses_ids = Course.where(‘created_at < ?’, 5.years.ago’).pluck(:id)
  Article.where(course_id: old_courses_ids).delete_all

儘管使用對象無疑非常方便,但整個過程卻非常緩慢,而且需要很多內存。新手們可能並不理解代碼的工作原理,以及如何提高其效率。

6、他們不瞭解 “dependent destroy” 與 “delete_all” 的區別

在被移除之前,“dependent destroy” 會選擇所有受限記錄,建立其對象,並調用各自的毀滅方法。此方法允許你移除所有受限數據。但是,當涉及大量數據時,這種方法就不管用了。

至於 “dependent delete_all”,它會通過一條 SQL 查詢移除自己。它效率很高,但是,在這種情況下,你得自己考慮數據庫的完整性。

7、他們不用帶 bang 的方法

#############
##  WRONG  ##
#############

  class Article
    validates :body, length: { minimum: 200 }  
  end

  articles_data.each do |article_data|
    Article.create(article_data)  
  end

#############
##  RIGHT  ##
#############  

# There are 2 possible solutions

  articles_data.each do |article_data|
    Article.create!(article_data)  
  end  

  # In this case a developer will be able to see that data he was not expencting to receive will get on the input

  articles_data.each do |article_data|
    article = Article.new(article_data)

    unless article.save
      puts ‘Can not save article’      
      #process this situation    
     end   
   end  
# Give a user a choice.

根據協議,將 bang(!) 添加至方法名的情況有如下兩種:

  • 如果某個方法修改了其訪問的對象

  • 如果某個方法在執行失敗後拋出了異常

新手們常常忽略第二種情況。如果代碼出了問題,你必須儘快找到問題根源。例如,如果完全不處理將記錄保存至數據庫的結果,最好還是拋出異常以找到哪段代碼處理了無效數據。

在上例中,如果一個無效的物品傳給輸入,就會被忽視。

8、他們不在遷移中設置默認字段

#############
##  WRONG  ##
#############  

class Article
    after_initialize :set_default_status    

    def set_default_status      
      self.status = ‘pending’    
        end  
      end


#############
##  RIGHT  ##
#############  

class MyMigration    
   def up
      change_column :articles, status, :string, default: ‘pending’    
    end    

  def down
      change_column :articles, status, :string    
    end  
  end

如果字段中的某個模型必須要有一個默認值,應該通過數據庫進行安裝。

9、他們不在遷移中設置限制條件

#############
##  WRONG  ##
#############  

class MyMigration    
  def change
      add_column :profiles, user_id, :integer    
    end  
  end


#############
##  RIGHT  ##
#############  

class MyMigration    
def change
      add_column :profiles, user_id, :integer, null: false   
   end  
 end

對於基礎架構的限制條件越多,我們的應用就會越可靠。此外,別忘記 “null:false”,用戶不可以沒有簡介。

10、他們不在遷移中寫反向遷移

如果不能回滾,遷移的意義在哪兒?

以上是新手們最常犯的 Ruby on Rails 錯誤的第一部分,如果喜歡本文,請記得分享哦。

未完待續……

本文系 OneAPM 工程師編譯整理。OneAPM 能爲您提供端到端的 Ruby 應用性能解決方案,我們支持所有常見的 Ruby 框架及應用服務器,助您快速發現系統瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Ruby 監控從來沒有如此簡單。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客

本文轉自 OneAPM 官方博客

原文地址:http://jetruby.com/expertise/common-ruby-rails-mistakes-beginners-make-model-database/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章