Rubyでは、private
なメソッドは普通に呼んでもエラーになります。
class Foo
private
def bar
puts "Don't call me!"
end
end
Foo.new.bar
#=> NoMethodError: private method `bar' called for #<Foo:0x007fe1232991c0>
一方、Rubyにはsend
というメソッドがあって動的なディスパッチが可能となっていますが、これはprivate
なメソッドも呼べてしまいます。
Foo.new.send :bar
#=> Don't call me!
そこで、public
なメソッドのみが呼べるpublic_send
というものが用意されています。
Foo.new.public_send :bar
#=> NoMethodError: private method `bar' called for #<Foo:0x007fe123238aa0>
しかし、public_send
にはこんな抜け道が……
Foo.new.public_send :instance_eval, 'bar'
#=> Don't call me!
そう、instance_eval
を使い、引数として呼びたいメソッドの名前を「文字列で」渡せばprivate
なメソッドも呼べてしまうのです(シンボルでは動作しません)。
この辺はRubyの自由度の高さですが、それが裏目に出る場合もあります。
例えばRailsで外部からの入力を直接、動的にディスパッチする必要がある場合、public_send
を使うだけでは危険です。おそらく呼びたいメソッドの種類はそう多くないので、ホワイトリスト形式でそれらをリストアップし、残りはsendすべきではないでしょう(そもそもそんな危ない橋を渡るな、という話ですが……)。
whitelist = %w(foo bar buzz)
if whitelist.include? params[:method_name]
obj.public_send params[:method_name]
else
raise "Not in whitelist"
end
以上、Rubyの世界ではすべてがpublic
なんだ!というお話でした。