一人 Ruby Advent Calendar 2017 10日目の記事になります。
さて、今回は Ruby の private についていろいろ
Ruby で private メソッドを定義する
Ruby では private(という名前のメソッド)を使用することでメソッドを private として定義することができます。
class X def homu "homu" end # 任意のメソッドを private にする private :homu # 引数がなかった場合は以降に定義されるメソッドを private メソッドとして定義する private def mami # メソッド内であれば呼び出すことが出来る homu + mado end def mado "mado" end end x = X.new # private メソッドは呼び出せない # Error: private method `homu' called for #<X:0x00000000025cc1a8> (NoMethodError) p x.homu
こんな感じでクラス外からはメソッドを呼び出すことができません。
private メソッドを呼び出す
さてさて、private メソッドを定義することでクラス外から呼び出せない…と、思いきや実はクラス外からも簡単に呼び出すことができます。
#send を使う
class X private def homu "homu" end end x = X.new # send だと任意のメソッドで呼び出すことが出来る p x.send :homu # => "homu"
#instance_exec や #instance_eval を使う
class X private def homu "homu" end end x = X.new p x.instance_eval { # ブロック内で直接メソッド呼び出す homu } # => "homu"
特異メソッド経由で呼び出す
class X private def homu "homu" end end x = X.new # メソッドを再定義して def x.homu # 元のメソッドを呼び出す super() end p x.homu
どういうこと?
これは Ruby の private の仕様がx.func のように『x. をつけて呼び出せないようにする』となっているためです。
なので
x = X.new # インスタンスオブジェクトからメソッド呼び出す x.func
のように通常はインスタンスオブジェクト経由で呼び出す事ができません。
しかし、逆にいえば x.func のような形式ではなくて
# #send 経由で呼び出す x.send :func
のように #send 経由でメソッド呼び出したり、
# instance_eval 内ではコンテキストが x になるので # x. を付ける必要なく func を呼び出せる x.instance_eval { func }
みたいに #instance_exec を使うことで x. を付けることなくメソッドを呼び出す事ができます。
ちなみに次のように self. を付けて呼び出した場合はエラーになります。
class X def homu # OK homu # Error: private method `mado' called for #<X:0x0000000001934670> (NoMethodError) self.homu end private def mado "mado" end end
まとめ
privateメソッドにメソッド名を渡すとそのメソッドがprivateメソッドになるprivateメソッドに引数を渡さなかった場合は以降に定義されるメソッドがprivateになるprivateメソッドはx.funcのようにx.をつけて呼び出せないx.をつけなければ呼び出せるので簡単に外部から呼び出すことが出来る
このように Ruby では『private メソッドが外部から呼び出せない』とは限らないので広い意味での『private メソッド』を定義したい場合は工夫が必要になってきます。