今度こそinstance_evalとmodule_evalを理解してもらった(問題の答え)

(id:akm:20100226:1267134143) で出題された問題の答えです。

まずは前提から

irbを起動して、実際に試しながらやってみます。

obj = Object.new
#=> #<Object:0x101171ef0>

class A
end
#=> nil

objというObjectのインスタンスと、Aというクラスがあります。

block = Proc.new do
  def foo
    'foo'
  end
end
#=> #<Proc:0x000000010115e698@(irb):6>

という、fooメソッドを定義するblockがあります。


Ans1. objに特異メソッドとしてfooを定義するコード
obj.instance_eval(&block)   #--- 1.
#=> nil

obj.singleton_methods       #--- 2.
#=> ["foo"]

obj.foo
#=> "foo"
  1. (id:akm:20100226:1267134143)に特異メソッドの書き方が3つ挙げられていますが、ブロックを渡せそうなのはinstance_evalです。「&block」のように引数名の前に&をつけると、ブロックをオブジェクトとして引数に受け取ることができます。
  2. 特異メソッドが追加されたことを確認できました。(singleton_methodsは特異メソッドが調べられます。)
Ans2. Aにインスタンスメソッドとしてfooを定義するコード。
A.module_eval(&block)       #--- 1.
#=> nil

A.instance_methods(false)   #--- 2.
#=> ["foo"]

a = A.new
#=> #<A:0x101117c20>

a.foo
#=> "foo"
  1. インスタンスメソッドを追加したい場合は、module_evalを使います。(module_evalの別名のclass_evalでもOKです。)
  2. インスタンスメソッドが定義されたことを確認できました。(instance_methodsは、インスタンスメソッドを調べられます。)ちなみに引数にfalse を指定すると、そのモジュールで定義されているメソッドのみ返してくれます。