method_missing で super を呼ばないと本来のエラーが消える
method_missingを独自定義すると、未定義のメソッド呼び出しをすべて横取りできます。想定していない呼び出しに対してsuperを呼ばずnilなどを返してしまうと、本来出るはずだったNoMethodErrorが出ず、バグが静かに握りつぶされます。
なぜエラーが出ないのか
出力: false
(エラーなし)- Rubyは何も報告しません。文法として正しいためです
出力: false- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 class Ghost 2 def method_missing(name, *args) 3 name == :boo ? "Boo!" : super 4 end 5 6 ^ 7 end 8 9 g = Ghost.new 10 puts g.respond_to?(:boo)
method_missingで応答できるようにしたメソッドでも、respond_to_missing?を実装しない限りrespond_to?は「知らない」と答えてしまいます。
直し方: (空) を def respond_to_missing?(name, include_private = false)
name == :boo || super
end にします。
パターン2
1 class Robot 2 def method_missing(name, *args) 3 name == :beep ? "Beep!" : super 4 end 5 6 ^ 7 end 8 9 r = Robot.new 10 puts r.respond_to?(:beep)
method_missingだけを実装してrespond_to_missing?を省略すると、実際には反応できるメソッドなのにrespond_to?がfalseを返す食い違いが起きます。
直し方: (空) を def respond_to_missing?(name, include_private = false)
name == :beep || super
end にします。
パターン3
1 class Cat 2 def method_missing(name, *args) 3 name == :meow ? "Meow!" : super 4 end 5 6 ^ 7 end 8 9 c = Cat.new 10 puts c.respond_to?(:meow)
method_missingとrespond_to_missing?は必ずセットで実装するのが基本です。片方だけでは動的なメソッドの存在確認と実際の呼び出し結果が食い違います。
直し方: (空) を def respond_to_missing?(name, include_private = false)
name == :meow || super
end にします。
パターン4
1 class Duck 2 def method_missing(name, *args) 3 name == :quack ? "Quack!" : super 4 end 5 6 ^ 7 end 8 9 d = Duck.new 10 puts d.respond_to?(:quack)
d.quackを実際に呼べば動くのに、respond_to?(:quack)はfalseと答えるという矛盾が起きます。
直し方: (空) を def respond_to_missing?(name, include_private = false)
name == :quack || super
end にします。
パターン5
1 class Alarm 2 def method_missing(name, *args) 3 name == :ring ? "Ring!" : super 4 end 5 6 ^ 7 end 8 9 a = Alarm.new 10 puts a.respond_to?(:ring)
respond_to?に依存するライブラリのコードは、respond_to_missing?の実装漏れがあると誤動作します。
直し方: (空) を def respond_to_missing?(name, include_private = false)
name == :ring || super
end にします。
よくある誤解
「method_missingは自分が処理したいメソッド名だけを拾ってくれるはず」という思い込みは誤りです。method_missingは条件に一致しない呼び出しも含めて全て渡ってくるため、想定外の名前に対しては明示的にsuperを呼んで本来のNoMethodErrorを発生させる必要があります。
まとめ
method_missing で super を呼ばないと本来のエラーが消えるは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。