def self.method と def method を混同すると NoMethodError になる
def self.methodはクラスメソッド(ClassName.methodで呼ぶ)を定義し、def methodはインスタンスメソッド(インスタンス.methodで呼ぶ)を定義します。どちらか一方しか定義していないのに、もう一方の呼び方をするとNoMethodErrorになります。
エラーメッセージの読み方
main.rb:7:in `<main>': undefined method `square' for class MathUtil (NoMethodError)
main.rb- ファイル名
7- 行番号 — 実際にクラッシュした行
<main>- 発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
NoMethodError- 例外クラス — 何が起きたか。ここを検索するのが最短です
undefined method `square' for class MathUtil- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 class MathUtil 2 def square(n) ^ 3 n * n 4 end 5 end 6 7 puts MathUtil.square(4)
self.を付けないと、squareはインスタンスメソッドとして定義されます。MathUtil.squareのようにクラス自身から呼ぶには、def self.squareにする必要があります。
直し方: (空) を self. にします。
パターン2
1 class StringUtil 2 def shout(text) ^ 3 text.upcase + "!" 4 end 5 end 6 7 puts StringUtil.shout("hi")
インスタンスを作らずにクラス名から直接呼び出したいメソッドは、self.を付けたクラスメソッドとして定義します。
直し方: (空) を self. にします。
パターン3
1 class ArrayUtil 2 def sum_all(list) ^ 3 list.sum 4 end 5 end 6 7 puts ArrayUtil.sum_all([1, 2, 3])
「クラスの中で定義すればどちらの呼び方でも使えるはず」という思い込みは誤りです。self.の有無で完全に別のメソッドになります。
直し方: (空) を self. にします。
パターン4
1 class TimeUtil 2 def now_label ^ 3 "now" 4 end 5 end 6 7 puts TimeUtil.now_label
ユーティリティ的なメソッドをクラスメソッドとして提供したい場合、defにself.を付け忘れると呼び出せません。
直し方: (空) を self. にします。
パターン5
1 class NumberUtil 2 def double(n) ^ 3 n * 2 4 end 5 end 6 7 puts NumberUtil.double(5)
self.を付けたメソッドはクラス自身に属し、self.を付けないメソッドはインスタンスに属します。混同すると呼び出し方が合わなくなります。
直し方: (空) を self. にします。
よくある誤解
「クラスの中で定義したメソッドは、クラスからでもインスタンスからでも呼べるはず」という思い込みは誤りです。self.の有無でどちらの側に属するメソッドかが完全に分かれており、混ぜて呼ぶことはできません。
まとめ
def self.method と def method を混同すると NoMethodError になるは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。