method_missingとは
呼びだされたメソッドが存在しなかった時に呼ばれるメソッド。
NoMethodErrorを返してくれるやつ。
BasicObjectクラスに定義されている。
rubyのメソッド探索の流れ
rubyでは呼びだされたメソッドが見つかるまで継承ツリーを辿っていき、最終的に最上位であるBasicObjectクラスまで探しに行く。
BasicObjectまで探しに行っても見つからなかった場合は、レシーバーの直接のクラスにmethod_missingメソッドが無いか探索し、ない場合は再び継承ツリーを辿ってmethod_missingメソッドを探しに行く。
例
以下のようなクラス、モジュールがあったとします。
module Hello
def hello
p "Helloモジュール"
end
end
class GrandParent
def hello
p "GrandParentクラス"
end
end
class Parent < GrandParent
include Hello
end
class Child < Parent
end
child = Child.new
child.hello #=> "Helloモジュール"
p Child.ancestors #=> [Child, Parent, Hello, GrandParent, Object, Kernel, BasicObject]
childオブジェクトに対してhelloメソッドが呼ばれると、childオブジェクトの直接のクラスであるChildクラスから継承ツリーを辿ってhelloメソッドを探しに行く。最初にhelloメソッドをもっているのはHelloモジュールなので、"Helloモジュール"という文字列が出力される。
メソッド探索の流れは
Childクラス -> Parentクラス -> Helloモジュール -> GrandParentクラス
-> Objectクラス -> Kernelモジュール -> BasicObjectクラス
となる。
したがって、存在しないメソッドを呼び出したときも上記の流れでmethod_missingメソッドを探しに行く。
BasicObject#method_missingをオーバーライド
特定のクラスでmethod_missingメソッドをオーバーライドすることで定義されていないメソッドが呼ばれてもNoMethodErrorを出さない(特定の処理を実行させる)ことができる。
method_missingに渡される引数は以下のようになっている。
引数 | 中身 |
---|---|
第一引数 | 呼び出しに失敗したメソッド名のシンボル |
残りの引数 | 呼び出しに失敗したメソッドに渡された引数(可長変引数) |
例
Arrayのような振る舞いをさせたいDelegateArrayクラスをArrayを継承せずに作ってみます。
class DelegateArray
attr_reader :array
def initialize
@array = []
end
def method_missing(name, *args)
if Array.instance_methods.include?(name)
p "method_missingが呼ばれました"
return @array.__send__ name, *args
end
# method_missingないで存在しないメソッドが呼ばれると
# SystemStackErrorが発生するので、Arrayに定義された
# メソッド以外のときはsuperを呼び出す
super
end
end
delegate_array = DelegateArray.new
delegate_array << 1
p delegate_array.array
実行結果
$ ruby delegate_array.rb
"method_missingが呼ばれました"
[1]