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