身為一個開發者,與bug對抗根本是每天必備功課,而Rails也有非常完備的除錯(debug)方式可供使用。包括幾個強而有力的gem:better_errors、debugger、pry等等。
不過!在使用這些強大的debug工具之前,是否有遇過根本連錯誤訊息都看不懂的情況呢?筆者自己剛開始開發Rails時有些錯誤訊息還要先上google去查才能得到解答。在這裡整理了9個常見到的錯誤以及可能原因,假如大家都能熟悉這些錯誤,以後遇到bug時就可以準確的知道錯誤出在哪裡囉!
method是Ruby當中所有利用def...end定義的方法都叫method,這段錯誤訊息的意思是你呼叫了一個method,卻沒有定義這個method。通常是由幾個原因造成的:
(1)打錯字
最常出現的狀況是例如我們要把user的名字輸出到view當中,卻在view當中指定錯名稱。例如:
# controller
@user = User.find(params[:id])
#view
Username: <%= @user.namme %>
# name 拼錯造成NoMethodError
這樣就會在執行時出現**undefined method “namme”**的錯誤。打錯字的情況也常常出現在model或class的名稱當中,這時候就會出現Uninitialized Constant的錯誤訊息:
@user = Users.find(params[:id])
# NameError: Uninitialized Constant
(2)在view當中使用controller method
Rails預設view當中是無法使用controller method的,只能使用helper。除非我們在controller當中設定某個method為helper method,才可以進行使用。以Rails Cast中的user情況為例:
# 在controller當中一定要定義這行,否則在view當中執行會出現undefined method錯誤
helper_method :current_user
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
(3)instance method和class method混著使用
在處理model物件時比較容易發生這類型的錯誤,在model中,處理整個class和處理單一物件的用法會不相同,例如我們在User這個class當中設定一個檢查各個欄位資料是否都有填入的method:
def has_blank_column?
# ... 省略
end
User.first.has_blank_column?
# => true
User.has_blank_column?
# => undefined method "has_blank_column?"
該method並未加上self,屬於instance method且只能使用於一個user,而非整個User class
(4)class類型使用錯誤
在處理陣列(Array)與物件(Object)時,有些方法容易搞混,例如在合併時,merge、push是兩種class的方法,用錯時也會有錯誤訊息。
a = [1,2,3,4]
b = [5,6,7,8]
a.push(b)
# => [1,2,3,4,5,6,7,8]
a.merge(b)
# => undefined method "merge"
# => merge是用於合併object
其實這點跟剛剛的情況非常相像,但差別在於可能會有variable的scope用錯問題。
在一個method內的local variable,無法在另一個method內使用;或是一個class內的class variable無法在其他class內使用。假設今天有一個Employee class,我們設定檢查bonus的method:
class Employee
def total_pay
a = self.pay
b = self.bonus
a + b
end
def double_bonus
a + b*2
end
end
User.first.double_bonus
# => undefined local variable "a"
這樣的錯誤最主要都是試圖將一種資料型態,使用於另一種資料型態。同樣的錯誤訊息還有:
ArgumentError: comparison of ____ with ____ failed
TypeError: nil can’t be coerced into ____
以上的空格可以自行套用至各種類型,包括Float, Fixnum, String, Decimal等等。遇到這種情況,請從該變數開始判斷該變數是否應用在錯的地方,例如:
a = 10
b = nil
c = "string"
a + b
# => TypeError: nil can't be coerced into Fixnum
a + c
# => TypeError: no implicit conversion of String into Fixnum
當然,許多情況也可能是gem本身的bug造成的,如果模糊不清的時候還是google看看有沒有相關issue。
通常在view當中設定link,或是在routes.rb當中設定時會出現這個問題:
get '/sign_in', :to => 'sessions#new', :as => 'sign_in'
假如今天並沒有在sessions controller當中設定new action,就會出現這個錯誤訊息。
通常是打錯字了,例如posts_path打成posys_path,Rails會從路徑列表中搜尋這個名稱但找不到。
另外一種可能是在routes中沒有定義,例如自訂的action一定要先放在routes.rb當中:
resources :posts do
collection do
get :old_entries
end
end
如果沒有放,那在尋找路徑old_entries_posts_path時,就會有這個錯誤訊息。
意思是在controller當中有定義,但卻在view當中沒有相對應的檔案名稱,例如有一個show action,在view資料夾中就必須要有一個show.html.erb才抓得到。如果是設定輸出成json就沒有這個問題。
意思是有新的migration檔案,卻還沒有進行migrate,Rails必須在migrate都完成的情況下才能啟動server。解決方法是執行rake db:migrate指令。
代表文法有錯,依照錯誤訊息檢查是否if、do、each都有搭配一個end,所有括弧都是完整的{}、()、[]等等。假如有非常複雜的if...else邏輯時會容易出現這個問題。
通常是我們在使用ActiveRecord時用**.save!**方法時所跳出的訊息,如果我們只使用.save,就不會有錯誤訊息,但也不會正確存入。假如我們發現無法儲存,可以利用.save!所跳出的錯誤訊息來檢查為何無法儲存。但如果是到實際的producion環境,就改使用.save,比較不會整個網站爆掉,影響使用者體驗。
CC圖片授權:WikiPedia
本文同步刊登於我的部落格:特快車