method_missing
Rubyでは通常、存在しないメソッドを呼び出した場合エラーが発生する。
class Siman end s = Siman.new s.hello
method_missing.rb:5:in `<main>': undefined method `hello' for #<Siman:0x007f811c066798> (NoMethodError)
NoMethodErrorが発生。
このエラーメッセージは実はmethod_missingというメソッドが内部で呼び出されて表示させているものなのである。
なのでこのmethod_missingをオーバーライドすることで独自のエラーメッセージを表示させることが出来る。
class Siman def method_missing(name, *args) puts "#{name}メソッドは存在していません。" end end s = Siman.new s.hello
helloメソッドは存在していません。
method_missingの第1引数は呼びだそうとしたメソッド名を表し、第2引数はその呼びだそうとしたメソッドの引数を表している。
define_method
rubyにはdefine_methodというメソッドを動的に定義できるメソッドが存在する。
class Siman define_method :hello do puts "hello world" end end s = Siman.new s.hello
hello world
「普通にdefで定義すればいいじゃん」と思うかもしれないが、ここで重要なのは「define_methodはプログラム実行中でも実行できる」ということである。つまり、先ほどのmethod_missingと組み合わせると「存在しないメソッドを生成」というとんでもないことが出来てしまう。(黒魔術)
send
sendメソッドはメソッドを動的に呼び出すためのメソッドである。呼び出しに変数などを使用できるため、柔軟性が高い。
class Siman def say(word) puts "siman like #{word}" end end s = Siman.new words = %w(ruby python c++ java php) words.each do |word| s.send(:say, word) end
siman like ruby siman like python siman like c++ siman like java siman like php
第1引数には呼び出したいメソッドの名前、第2引数にはメソッドに渡す引数を指定する。
存在しないメソッドを召喚
ここまでで紹介した「method_missing」「define_method」「send」メソッドを使用して、「存在しないメソッドを呼び出す」黒魔術を実行する。
class Siman def method_missing(name, *args) Siman.class_eval{ define_method "#{name}" do |*args| puts "#{args.join(' ')}" end } send(name, *args) end end s = Siman.new s.say("hello world")
hello world
method_missing -> define_method -> sendの華麗なコンボにより
* 存在しないメソッドの呼び出しに成功してしまった *
そんなRubyが僕は大好きです。