privateメソッドをレシーバを指定して呼ぶ方法

今までprivateメソッドは、定義元のクラスのコンテキストでしか実行できないと思っていました。なので、テストの時とかにはprivateメソッドをテストせずに、そのメソッドを使っているメソッドのテストで済ませていました。また、irbとか、script/console使う時にも凄い苦労した。
でも、あったんですね(汗)

priavteメソッドをコンテキスト外から使う方法

  • sendメソッドを使う

sendを使えばレシーバを指定して実行できます。

class Hoge
  private
  
  def hoge(x)
    x
  end
end

h = Hoge.new
h.send(:hoge, 1)  #=> 1


また、instance_evalを使うという方法もあります。これは、ブロック内がレシーバのコンテキストで評価されるために
できるテクニックです。

h = Hoge.new
h.instance_eval do 
  hoge(1)
end

#=> 1


ですが、以下にあるように、このテクニックは推奨されません。

nstance_evalメソッドは、Objectのパブリックメソッドで、文字列1つあるいはブロック1つをパラメタとして取り、そのパラメタをレシーバーのコンテキストで実行する。文字列あるいはブロックが実行されると、selfはinstance_evalのレシーバーに設定される。これは、ブロック内部からインスタンスの変数とプライベートメソッドにアクセスできるということを意味しているが、これはレシーバーのカプセル化を破ってしまうため、簡潔だが危険だ。


テスト等ではsendを使うのがお手軽でよさそうです。ちなみに、1.9では、sendメソッドでもprivateメソッドにはアクセスすることはできず、代わりに「__send__」メソッドを使います。

追記

記事の最後が間違っていました。

んにゃ、sendでも__send__でも呼べるのだが。Ruby 1.9.0でも最新版でもRuby 1.8でも同じ。
sendはかぶる可能性があるので__send__が無難。


最初は「__send__」でもアクセスできないようになって、「instance_eval」を使うしかなくなるみたいなことが言われていた。けど、できるんですね><
id:rubikitchさんありがとうございます!