BasicObject#instance_eval
で理解不足の部分があったのでメモっておきます。
Ruby 1.9.3p448
で実行しています。
こんな感じのクラスを作りました。
class Klass # なんらかの処理の後に実行する def proc_after_run(&block) @proc = block end def run # なんかの処理 puts '--- running... ---' # … # … # 定義済み proc を呼び出す call_proc end private def call_proc env = Object.new unless @proc.nil? env.instance_eval do @proc.call end end end end
ブロックを定義して実行してみましょう。
oh... エラーが起きたようです。
#=> NoMethodError: undefined method `call' for nil:NilClass from /Users/takayuki_atkwsk/hogehoge/klass.rb:16:in `block in call_proc'
nil に対して call メソッドを呼んでいます。あれ?@proc
にセットしたじゃないか。
と思い込んでいたのですが、大事なことを忘れていました。
オブジェクトのコンテキストで評価するとは評価中の self をそのオブジェクトにして実行するということです。
instance method BasicObject#instance_eval
上の例で言えば、env
のコンテキストで@proc.call
が評価されています。この中ではself
はenv
ですので
ブロックの中の@proc
はenv
に属しています。もちろんenv
では@proc
を定義していないので
nil となり上記のエラーとなった、と理解しました。
instance_eval
にはもう一つルールがあって、
ただし、ローカル変数だけは、文字列 expr の評価では instance_eval の外側のスコープと、ブロックの評価ではそのブロックの外側のスコープと、共有します。
instance method BasicObject#instance_eval
ということで書き換え...。
class Klass # 省略... private def call_proc env = Object.new unless @proc.nil? procboj = @proc # <- append env.instance_eval do procobj.call # <- modify end end end end
実行してみましょう。
期待した動作になりましたね。Rubyの理解度がちょっと増しました。