開(kāi)發(fā)新手最容易犯的50個(gè) Ruby on Rails 錯(cuò)誤(1)

【編者按】本文最早發(fā)布與 JETRuby 博客愕鼓,主要介紹了開(kāi)發(fā)新手最容易犯的 Ruby 錯(cuò)誤钙态。文章系國(guó)內(nèi) ITOM 管理平臺(tái) OneAPM 編譯呈現(xiàn)。

一年前菇晃,我們創(chuàng)立了以 “Rubyboost” 為名的 Ruby on Rails 課程册倒。簡(jiǎn)而言之,本課程的目標(biāo)是使對(duì)編程了解不多的新手也能在兩個(gè)月內(nèi)磺送,提升技能驻子、成為初級(jí)開(kāi)發(fā)者。在成功完成課程之后估灿,學(xué)生會(huì)收到為其兩個(gè)月的實(shí)習(xí)邀請(qǐng)拴孤,實(shí)習(xí)地點(diǎn)就在我們公司设拟。如果一切順利炫七,就會(huì)得到聘用。不得不說(shuō)霸褒,這是一種相對(duì)公平且簡(jiǎn)單的成為職業(yè)開(kāi)發(fā)者的道路司顿,你覺(jué)得呢芒粹?

順帶說(shuō)一句,你根本想不到大溜,有多少人愿意來(lái)參加并學(xué)習(xí) Rails 編程化漆!

在分析了所有受訓(xùn)者編寫(xiě)的代碼之后,我們總結(jié)了50個(gè)最常見(jiàn)的錯(cuò)誤钦奋!更糟糕的是座云,每個(gè)小組所犯的錯(cuò)誤與前一組的錯(cuò)誤幾乎一模一樣。

以下是 Rails 新手常常忽略或做錯(cuò)的地方付材。我們還包含了“對(duì)“朦拖,”錯(cuò)”兩個(gè)版本的代碼樣本,使得教程更為清楚厌衔。

1璧帝、他們不使用自動(dòng)生成的方法

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

if course.visible    
  # do something
  end
  
##############
##  RIGHT   ##
##############  

if course.visible?    
  # do something
  end

通常,Rails 和許多 gems 會(huì)為它們使用的對(duì)象添加一些有用的幫助方法富寿。例如睬隶,Rails 會(huì)自動(dòng)為布爾字段添加聲明锣夹。通常,這些方法的名字是以問(wèn)號(hào)結(jié)尾的苏潜。請(qǐng)牢記這一點(diǎn)银萍!

2、他們不知道“N+1”查詢(xún)來(lái)自何處

#############
##  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 如何與數(shù)據(jù)庫(kù)交互是非常重要的恤左。但是贴唇,新手往往沒(méi)有這種了解。因此赃梧,他們很少使用 “includes”滤蝠、“preload” 與 “eager_load” 這類(lèi)方法,并且對(duì) “bullet” gem 一無(wú)所知授嘀。

在第一個(gè)例子中物咳,N+1 查詢(xún)會(huì)傳遞至數(shù)據(jù)庫(kù)√阒澹”N” 是已經(jīng)完成的家庭作業(yè)數(shù)量览闰。查詢(xún)數(shù)量可能是10、20甚至100巷折。而在第二個(gè)例子中压鉴,只有2個(gè)查詢(xún)!

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 允許你隱藏?cái)?shù)據(jù)庫(kù)的實(shí)現(xiàn)油吭,并將代碼唯一化(uniqualize)。而且署拟,代碼的可讀性也會(huì)大幅提升婉宰,因?yàn)樗麄兺嘎读碎_(kāi)發(fā)者的意圖,而非數(shù)據(jù)庫(kù)的結(jié)構(gòu)推穷。

4心包、他們不了解 “after_create” 與 “after_commit” 間的差別

模型的數(shù)據(jù),包括其在 “after_create” 中的新 ID馒铃,可以從內(nèi)部蟹腾,而非外部進(jìn)行讀取,原因是交易尚未完成区宇。

如果我在數(shù)據(jù)庫(kù)中創(chuàng)建了一條記錄娃殖,之后打算將其 ID 放入 redis 或任意的存儲(chǔ)中,會(huì)得到以下結(jié)果:

  • 如果 ID 在交易完成之前使用萧锉,“after_create” 可能會(huì)導(dǎo)致無(wú)效數(shù)據(jù)珊随。

  • 借助 “Sidekiq” 或其他任意后臺(tái)工作,我總是可以使用 “after_commit” 確保數(shù)據(jù)的完整性柿隙。

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

盡管使用對(duì)象無(wú)疑非常方便,但整個(gè)過(guò)程卻非常緩慢禀崖,而且需要很多內(nèi)存衩辟。新手們可能并不理解代碼的工作原理,以及如何提高其效率波附。

6艺晴、他們不了解 “dependent destroy” 與 “delete_all” 的區(qū)別

在被移除之前,“dependent destroy” 會(huì)選擇所有受限記錄掸屡,建立其對(duì)象封寞,并調(diào)用各自的毀滅方法。此方法允許你移除所有受限數(shù)據(jù)仅财。但是狈究,當(dāng)涉及大量數(shù)據(jù)時(shí),這種方法就不管用了盏求。

至于 “dependent delete_all”抖锥,它會(huì)通過(guò)一條 SQL 查詢(xún)移除自己。它效率很高碎罚,但是磅废,在這種情況下,你得自己考慮數(shù)據(jù)庫(kù)的完整性荆烈。

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.

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

  • 如果某個(gè)方法修改了其訪問(wèn)的對(duì)象

  • 如果某個(gè)方法在執(zhí)行失敗后拋出了異常

新手們常常忽略第二種情況憔购。如果代碼出了問(wèn)題宫峦,你必須盡快找到問(wèn)題根源。例如倦始,如果完全不處理將記錄保存至數(shù)據(jù)庫(kù)的結(jié)果斗遏,最好還是拋出異常以找到哪段代碼處理了無(wú)效數(shù)據(jù)。

在上例中鞋邑,如果一個(gè)無(wú)效的物品傳給輸入诵次,就會(huì)被忽視。

8枚碗、他們不在遷移中設(shè)置默認(rèn)字段

#############
##  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

如果字段中的某個(gè)模型必須要有一個(gè)默認(rèn)值逾一,應(yīng)該通過(guò)數(shù)據(jù)庫(kù)進(jìn)行安裝。

9肮雨、他們不在遷移中設(shè)置限制條件

#############
##  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

對(duì)于基礎(chǔ)架構(gòu)的限制條件越多遵堵,我們的應(yīng)用就會(huì)越可靠。此外,別忘記 “null:false”陌宿,用戶不可以沒(méi)有簡(jiǎn)介锡足。

10、他們不在遷移中寫(xiě)反向遷移

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

以上是新手們最常犯的 Ruby on Rails 錯(cuò)誤的第一部分,如果喜歡本文爽蝴,請(qǐng)記得分享哦沐批。

未完待續(xù)……

本文系 OneAPM 工程師編譯整理。OneAPM 能為您提供端到端的 Ruby 應(yīng)用性能解決方案蝎亚,我們支持所有常見(jiàn)的 Ruby 框架及應(yīng)用服務(wù)器九孩,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因发框。分鐘級(jí)部署躺彬,即刻體驗(yàn),Ruby 監(jiān)控從來(lái)沒(méi)有如此簡(jiǎn)單缤底。想閱讀更多技術(shù)文章顾患,請(qǐng)?jiān)L問(wèn) OneAPM 官方技術(shù)博客

本文轉(zhuǎn)自 OneAPM 官方博客

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末个唧,一起剝皮案震驚了整個(gè)濱河市江解,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徙歼,老刑警劉巖犁河,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異魄梯,居然都是意外死亡桨螺,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)酿秸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)灭翔,“玉大人,你說(shuō)我怎么就攤上這事辣苏「蜗洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵稀蟋,是天一觀的道長(zhǎng)煌张。 經(jīng)常有香客問(wèn)我,道長(zhǎng)退客,這世上最難降的妖魔是什么骏融? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任链嘀,我火速辦了婚禮,結(jié)果婚禮上档玻,老公的妹妹穿的比我還像新娘怀泊。我一直安慰自己,他們只是感情好窃肠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布包个。 她就那樣靜靜地躺著刷允,像睡著了一般冤留。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上树灶,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天纤怒,我揣著相機(jī)與錄音,去河邊找鬼天通。 笑死泊窘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的像寒。 我是一名探鬼主播烘豹,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诺祸!你這毒婦竟也來(lái)了携悯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤筷笨,失蹤者是張志新(化名)和其女友劉穎憔鬼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胃夏,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轴或,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仰禀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片照雁。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖答恶,靈堂內(nèi)的尸體忽然破棺而出饺蚊,到底是詐尸還是另有隱情,我是刑警寧澤亥宿,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布卸勺,位于F島的核電站,受9級(jí)特大地震影響烫扼,放射性物質(zhì)發(fā)生泄漏曙求。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悟狱。 院中可真熱鬧静浴,春花似錦、人聲如沸挤渐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浴麻。三九已至得问,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間软免,已是汗流浹背宫纬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膏萧,地道東北人漓骚。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像榛泛,于是被迫代替她去往敵國(guó)和親蝌蹂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 最近在 Ruby China 看到 @huacnlee 分享的 The Rails Doctrine - 中文翻譯...
    freefishz2閱讀 1,063評(píng)論 0 6
  • 總結(jié)一下: 程序員的幸福最大化 約定優(yōu)于配置 主廚精選 多元化的設(shè)計(jì)模式 推崇優(yōu)美的代碼 提供實(shí)用工具 重視整合系...
    waynedeng閱讀 843評(píng)論 0 3
  • 一曹锨、異同對(duì)比選擇1孤个、Python和ruby的相同點(diǎn): * 都強(qiáng)調(diào)語(yǔ)法簡(jiǎn)單,都具有更一般的表達(dá)方式艘希。python是縮...
    沃倫蓋茨閱讀 4,144評(píng)論 2 24
  • 人生如戲硼身,是我入戲太深「蚕恚縱數(shù)十載歲月佳遂,也不過(guò)匆匆。片片桃花的飄與落撒顿,飄起的是紅塵丑罪,落地的也是紅塵。只不過(guò)飄起的桃花...
    羽月閣閱讀 119評(píng)論 0 5
  • 夜間凤壁,傾盆大雨和著狂風(fēng)追我跑吩屹,你跑我追 有旋律的拍打著窗,雨水漸漸蔓延了街道 匯成了一條小溪 白天拧抖,風(fēng)也沒(méi)有了夜間...
    詩(shī)詩(shī)檸檬c閱讀 418評(píng)論 0 0