読者です 読者をやめる 読者になる 読者になる

メタプログラミングRuby その7 [学習メモ]

プログラミング 学習メモ Ruby

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

4.4 instance_eval

class 定義の際に明確に指定しない限り class のインスタンス変数や private メソッドにはアクセスできないことになっている。
しかし instance_eval にブロックを渡すことでこれらの壁を越えることができる。

class Test
  def initialize
    @v = 1
  end
end

obj = Test.new

obj.instance_eval do
  @v
# -> 1
end

このようにいともたやすくカプセル化を破壊することができる。

4.5 呼び出し可能オブジェクト
何気なく使っていると気づかないが、ブロックはオブジェクトではない。
ブロックを変数に保存するためには Proc オブジェクトに変換する必要がある。

inc = Proc.new { |x| x + 1 }
inc.call(2)
# -> 3

& オペレータを使うと、ブロックを Proc に変換し、他のメソッドに渡すことが出来るようになる。

def cal(a, b)
  yield(a, b)
end

def do_cal(a, b, &operation)
  cal(a, b, &operation)
end

do_cal(2, 3) { |x, y| x + y }
# => 5

また、 Proc オブジェクトに & オペレータをつけることでブロックに戻すことも出来る。

p = Proc.new { |x| x > 2 }
[1, 2, 3].map &p
# => [false, false, true]

4.5.3 Method オブジェクト
ブロックとメソッドは構造が殆ど同じで、それはこれらが互いに変換可能であることを意味している。
前の章で見た define_method はブロックからメソッドを作成するメソッドだった。
また、メソッドをブロックのようにオブジェクトとして取得することも出来る。

class MyClass
  def initialize(value)
    @x = value
  end

  def my_method
    @x
  end
end

obj = MyClass.new(1)
m = obj.method :my_method
m.call
# => 1

メソッドがオブジェクトに出来るとは知らなかった。
この辺りは Lisp っぽい使い方が出来そうだ。