Rubyのinstance_evalがブロック内のテキストを評価してレシーバーであるインスタンスの元で実行するメソッドであるということは、irbを叩いてなんとく理解できつつあるような気がします。しかし、逆に「何故そうなるのか?Why?」という疑問も増えてきました。
メソッド
以下のように定義したメソッド”aaa”はStringクラスのインスタンス”str”の特異メソッドになるようです。
str = "hoge"
str.instance_eval{def aaa; puts "a";end}
str.aaa
⇒"a"
しかし、このインスタンスメソッド”aaa”で返された”a”は文字列であるはずなので、Stringクラスかと思いきや、NilClassらしくて、この理由がよくわからないんですよね。
str.class
⇒String
str.aaa.class
⇒NilClass</del>
ちなみにFixnumクラスを返すはずのインスタンスメソッド”bbb”でも同じ結果です。
str.instance_eval{def bbb;puts 1;end}
str.bbb
⇒1
str.bbb.class
⇒NilClass</del>
追記
「putsの戻り値がnilだからでは」というコメントをいただきました。試してみると確かにその通りでした。失礼しました。
str = "hoge"
str.instance_eval{def aaa;"a";end}
str.instance_eval{def bbb;1;end}
str.aaa.class
⇒String
str.bbb.class
⇒Fixnum
定数
インスタンスの中で定数を定義してみます。正直、インスタンスの中で定数を定義することの意味がまだよくわかりませんが、一応可能なようです。これは特異定数というか、インスタンス定数という感じになるのでしょうか・・・?
str.instance_eval{AAA = "ccc"}
この定数”AAA”はどこで定義されてだろうか?
どうやって参照するのだろうか?
疑問が次から次へと出てきて止まりません。
クラス – class_evalとの違い –
さらにこのインスタンス”str”の中でクラスを作ってみたいと思います。
str.instance_eval{class BBB;end}
BBBクラスのインスタンスはつくることができます。
b = BBB.new
b.class
⇒BBB
Objectクラスのすぐ下にクラスが作られるんですね。Stringクラスのインスタンス”str”の中で作られるので、Stringクラスのサブクラスとして生成されるかと思っていました。
BBB.ancestors
⇒[BBB,Object,Kernel]
もう少し深く考えるとこの辺の挙動が理解できそうなのですが、今のところ???状態です。
ちなみにクラスはClassクラスのインスタンスでもあるので、instance_evalを用いるとこんな感じになります。
CCC = Class.new
#これでCCCクラスの生成。
CCC.class ⇒Class
#CCCはClassクラスのインスタンス
Classクラスのインスタンス、”CCC”にメソッドと定数を定義しています。
CCC.instance_eval{def ccc;puts "123456";end}
CCC.ccc
⇒"123456"
CCC.instance_eval{EEE = "789101112"}
CCC::EEE
⇒"789101112"
それぞれクラスメソッドとクラス定数として定義されているようです。クラスメソッドに定義されるのはClassクラスのインスタンス”CCC”の特異メソッド(=クラスCCCのクラスメソッド)として定義されているからです。Classクラスのインスタンスをレシーバーにする限り、instance_evalとclass_evalと違いは無いような気がする。どうなのだろう?