【編者按】本文最早發(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/