Rubyにpackage_privateを導入しようとしたら
Rubyのメソッドの可視性はpublic, protected, privateの3種類です。そこに同一パッケージ(イコール同一gem?)からのみ呼べるpackage_privateを導入してみましょう。
結論から言うと断念しました。
以下のような場合は簡単です。
class String def nya "#{self}にゃ" end package_private :nya end
privateなどでもお馴染の引数付きでpackage_privateを呼んだ場合です。これは指定されたメソッドを同一パッケージから呼ばれたか判断するメソッドで包んで、同一パッケージからの呼び出し(ここではcallerで判断しました)じゃない場合はNoMethodErrorを投げる、でおっけー。
ではなぜ断念したか。以下のようなケースだと雲行きが怪しくなってきます。
class String package_private def nya "#{self}にゃ" end private def pyon "#{self}ぴょん" end end
この場合、privateが呼ばれたら、package_privateを解除したいわけです。ってことはprivateの定義も上書きしなくちゃいけない?
で、本格的に断念したのが以下のケース。
class String package_private end class String def nya "#{self}にゃ" end end
この場合のnyaメソッドはデフォルトの可視性であるpublicで定義されるべきなのですが、package_privateを解除する術が思い付かず、断念って感じです。ここらへんからはCの世界なので、CRubyのコードも読んでみたのですが、組み込むのはなかなかしんどそう。うむー。
追記(2013-04-16 08:10)
と、こんな記事を書いておいたら、これを読んだ@kyubingから神に等しきコードが飛んできました。
最初パッと見たところ、2度目のclass Nekoでpackage_privateが有効になりっぱなしにならないかな〜と思いましたが、そこらへんがRubyの妙のようで。
Rubyはインスタンスメソッドが追加されると、Module#method_added(method_name)がコールされます。そのときのバックトレースは以下のような感じになるみたいです。
ファイル名:メソッドが定義された行:in `<class:クラス名>' ファイル名:クラスの定義が開始された行:in `<main>' # mainかはクラスを定義した場所に依る
Class.newやclass_evalを使うともう少し違うバックトレースになりますが、それでも基本的に「何行目でクラスの定義が開始されたか」という情報は取れます。
と言うことは、完全に断念したというケースでも判断が付きそうですね! すごい! @kyubing様々です。また改めてコードにしてみようと思います。