Debugging Rails:了解9個常見的錯誤訊息

Adler @ 2014-10-11


身為一個開發者,與bug對抗根本是每天必備功課,而Rails也有非常完備的除錯(debug)方式可供使用。包括幾個強而有力的gem:better_errors、debugger、pry等等。

不過!在使用這些強大的debug工具之前,是否有遇過根本連錯誤訊息都看不懂的情況呢?筆者自己剛開始開發Rails時有些錯誤訊息還要先上google去查才能得到解答。在這裡整理了9個常見到的錯誤以及可能原因,假如大家都能熟悉這些錯誤,以後遇到bug時就可以準確的知道錯誤出在哪裡囉!

1. NoMethodError: undefined method

method是Ruby當中所有利用def...end定義的方法都叫method,這段錯誤訊息的意思是你呼叫了一個method,卻沒有定義這個method。通常是由幾個原因造成的:

(1)打錯字

最常出現的狀況是例如我們要把user的名字輸出到view當中,卻在view當中指定錯名稱。例如:

# controller
@user = User.find(params[:id])

View檔案中寫:

<%= @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

2. NameError: undefined local variable or method

其實這點跟剛剛的情況非常相像,但差別在於可能會有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"
# => a和b都是local variable無法在另一個method中使用

3. TypeError: no implicit conversion of ____ into ____

這樣的錯誤最主要都是試圖將一種資料型態,使用於另一種資料型態。同樣的錯誤訊息還有:

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。

4. Unknown Action

通常在view當中設定link,或是在routes.rb當中設定時會出現這個問題:

get "/sign_in", :to => "sessions#new", :as => "sign_in"

假如今天並沒有在sessions controller當中設定new action,就會出現這個錯誤訊息。

5. No Routes Match

通常是打錯字了,例如postspath打成posyspath,Rails會從路徑列表中搜尋這個名稱但找不到。

另外一種可能是在routes中沒有定義,例如自訂的action一定要先放在routes.rb當中:

resources :posts do
    collection do
        get :old_entries
    end
end

如果沒有放,那在尋找路徑oldentriesposts_path時,就會有這個錯誤訊息。

6. ActionView::MissingTemplate

意思是在controller當中有定義,但卻在view當中沒有相對應的檔案名稱,例如有一個show action,在view資料夾中就必須要有一個show.html.erb才抓得到。如果是設定輸出成json就沒有這個問題。

7. ActiveRecord::PendingMigrationError

意思是有新的migration檔案,卻還沒有進行migrate,Rails必須在migrate都完成的情況下才能啟動server。解決方法是執行rake db:migrate指令。

8. SyntaxError

代表文法有錯,依照錯誤訊息檢查是否if、do、each都有搭配一個end,所有括弧都是完整的{}、()、[]等等。假如有非常複雜的if...else邏輯時會容易出現這個問題。

9. ActiveRecord::RecordInvalid

通常是我們在使用ActiveRecord時用.save!方法時所跳出的訊息,如果我們只使用.save,就不會有錯誤訊息,但也不會正確存入。假如我們發現無法儲存,可以利用.save!所跳出的錯誤訊息來檢查為何無法儲存。但如果是到實際的producion環境,就改使用.save,比較不會整個網站爆掉,影響使用者體驗。

CC圖片授權:OpenClipartWikiPedia