Rails のソースを読んでいると、あらゆるところで method_missing
をオーバーライドしています。
例えば、String
クラスにある inquiry
は
env = 'production'.inquiry
env.production? #=> true
env.development? #=> false
というふうに文字列に #{文字列}?
と問い合わせると true/false
を返してくれるようになるメソッドですが、実装を見ると
class String
def inquiry
ActiveSupport::StringInquirer.new(self)
end
end
========================================================================
class StringInquirer < String
private
def respond_to_missing?(method_name, include_private = false)
method_name[-1] == '?'
end
def method_missing(method_name, *arguments)
if method_name[-1] == '?'
self == method_name[0..-2]
else
super
end
end
end
こんな感じで method_missing
を使って定義しています。
method_missing
は呼び出したメソッドが存在しないときに、呼ばれるメソッドです。
そのため、例えば env.production?
と呼ぶと、そんなメソッドはないので StringInquirer
の method_missing
が呼ばれます。
すると、一行目の
if method_name[-1] == '?'
で true
となり(method_name
には、呼び出したメソッド名が入ってくる)、
self == method_name[0..-2]
「自分自身」と「?
を除いたメソッド名」との比較が行われます。
試しに、?
がつかないメソッドを呼んでみると
'production'.inquiry.production #=> NoMethodError
else
に入って、祖先クラスの method_missing
が呼ばれている事が分かります。
試しに作ってみます。
class String
private
def method_missing(method_name, *arguments)
if /AA\z/ === method_name.to_s
puts <<-EOF
∧_∧
( ・ω・)
|⊃/(___
/└-(____/
 ̄ ̄ ̄ ̄ ̄ ̄
EOF
else
super
end
end
end
'oyasumi'.to_AA #=>
# ∧_∧
# ( ・ω・)
# |⊃/(___
# /└-(____/
#  ̄ ̄ ̄ ̄ ̄ ̄
'oyasumi'.toooooooooooo_AA #=>
# ∧_∧
# ( ・ω・)
# |⊃/(___
# /└-(____/
#  ̄ ̄ ̄ ̄ ̄ ̄
できた!
ただし、これだけではメソッドは「存在していない」ことになっているので、
'oyasumi'.respond_to? :to_AA #=> false
respond_to_missing? をオーバーライドして、メソッド存在させます。
class String
private
def respond_to_missing?(method_name, include_private = false)
/AA\z/ === method_name.to_s
end
end
'oyasumi'.respond_to? :to_AA #=> true
確かに、String
に to_AA
というメソッドが存在しています。
こうやって、ライブラリとかは動的にメソッドを作っているんですね。おもしろい!