Rubyのカレントクラスという概念
書籍「メタプログラミングRuby」を読んでカレントクラスという概念を初めて知った。
Rubyはselfで参照できるカレントオブジェクトとは別に、常にカレントクラスを持っているとのこと。
defキーワードでメソッドを定義する時、このカレントクラスに対してインスタンスメソッドを定義する。
classキーワードでクラスをオープンすると、カレントクラスがそのクラスになるため、クラス定義内で定義したメソッドはそのクラスのインスタンスメソッドとなる。
class_evalでクラスをオープンした場合も同様で、レシーバのクラスがカレントクラスとなる。
class Hoge end Hoge.class_eval do # カレントクラスがHogeになる def meth end end Hoge.instance_methods(false) #=> [:meth]
class_evalはカレントオブジェクトも変更するので、
class_evalの動作は、「カレントオブジェクトとカレントクラスをレシーバのクラスに変更する」、と明確に定義することが出来るのだ。
ここら辺は今まで曖昧に理解していたので目から鱗だった。
ちなみにinstance_evalは、
「カレントオブジェクトをレシーバのオブジェクトに変更し、カレントクラスをレシーバのオブジェクトの特異クラスに変更する」。
そのためinstance_evalを使って、オブジェクトの特異メソッドを定義したり、クラスメソッドを定義することができる。
class Hoge end obj = Hoge.new obj2 = Hoge.new obj.instance_eval do # カレントクラスがobjの特異クラスになる def obj_meth puts 'obj' end end Hoge.instance_eval do # カレントクラスがHogeの特異クラスになる def hoge_meth puts 'hoge' end end obj.obj_meth #=> obj obj2.obj_meth #=> NoMethodErrorが発生 Hoge.hoge_meth #=> hoge
ところで、メソッドをネストした時のカレントクラスは意外なものになる。
class Hoge def meth1 def meth1_inner end end def self.meth2 def meth2_inner end end Hoge.new.meth1 # meth1_innerを定義する Hoge.meth3 # meth2_innerを定義する Hoge.instance_methods(false) #=> [:meth1, :meth1_inner, :meth2_inner] Hoge.singleton_methods #=> [:meth2]
meth1_innerはクロージャにならずに、Hogeクラスに定義される。
クラスメソッド内で定義したmeth2_innerも同様。
結局、カレントクラスについては、クラスをオープンした時にのみ変更されて、
たとえメソッド定義内であっても関係なく常にオープン中のクラスがカレントクラスになっていると考えれば良さそう。
カレントクラスは便利な概念なのでselfのようにプログラムから参照できたら良いのにと思っていたら、そういう提案が既にされていた。(http://redmine.ruby-lang.org/issues/4043)