# Rubyのリフレクション解説(eval族のはなし) _published: 2009/01/14_ ![alt](http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shunsuk/20090114/1231934320) Rubyのリフレクションについてステキな記事を見つけてしまいました。 - [Ruby reflection | Khelll's Blog](http://www.khelll.com/blog/ruby/ruby-reflection/) ## 前提知識 Rubyでは何でもオブジェクトだと言われます。まずは、この辺について。 最初に思い浮かぶのはこれですね。 ```ruby 5.class #=> Fixnum "hello".class #=> String ``` class自体はどうでしょうか? ```ruby class Foo;end #=> nil Foo.class #=> Class ``` ここから2つのことが解ります。 `Foo` は定数であるということ。そして `Class` 型のオブジェクトを参照しているということ。 これはどうでしょう? ```ruby Foo = Class.new (irb):8 warning: already initialized constant Foo => Foo ``` 予想通りエラーになります。定数 `Foo` を再度初期化しようとしたからです。 `Foo` クラスを定義したい時にすべきことは。 1. クラス型のオブジェクトをつくる 2. つくったオブジェクトで定数 `Foo` を初期化する 今後、「オブジェクト」と言った時には、 `Class` 型も含めてあらゆるオブジェクトを指します。 では、次です。「シングルトンクラス (Singleton Class)」について。 簡単に言うと、すべてのシングルトンメソッド (singleton method) を保持するクラスです。 2つのクラスメソッドを定義してみましょう。クラスメソッドは `Class` 型のオブジェクトのシングルトンメソッドにすぎません。 ```ruby class Foo def self.hi ; p "hi" ;end def self.bye ; p "bye" ; end end Foo.singleton_methods #=> ["hi","bye"] ``` 下のように書き換えることもできます。 ```ruby class Foo class << self def hi ; p "hi" ; end def bye ; p "bye" ; end end end Foo.singleton_methods #=> ["hi","bye"] ``` 上記の「class << self」と書かれたインナークラスがシングルトンクラスです。 シングルトンクラス自体を返すメソッドを定義してみましょう。 ``` class Object def singleton_class class << self self end end end ``` 試してみます。 ```ruby Foo.singleton_methods #=> ["bye", "hi"] Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"] ``` お解りのように、シングルトンクラスは、すべてのシングルトンメソッドを保持したクラスなのです。 それでは、RubyのリフレクションAPIに話を戻しましょう。ここでは、 `eval` 、 `instance_eval` 、 `class_eval` の3つのメソッドを紹介します。 ## eval `eval` は、Rubyの文字列を評価するメソッドです。 ```ruby eval "3+4" #=> 7 eval "def multiply(x,y) ; x*y; end" multiply(4,7) #=> 28 ``` `eval` は `binding` のスコープでも動きます。 `binding` はコードの特定の場所で実行コンテキストをカプセル化するオブジェクトです。このコンテキストを再利用することができます。 `eval` で `binding` を使った例をみてみましょう。 ```ruby class Demo def initialize(n) @secret = n end def getBinding return binding()# a method defined in Kernel module end end k1 = Demo.new(99) #get the value of the instance variable @secrete stored in the binding of object k1 eval("@secret", k1.getBinding) #=> 99 ``` `proc` オブジェクトを使うこともできます。 ```ruby def greeting(name) lambda{|greetings| greetings.collect {|g| "#{g} #{name}"} } end greeter = greeting("dude") #=> #<Proc:0xb752b810@(irb):2> greeter.call ["hi","hello","hola"] #=> ["hi dude", "hello dude", "hola dude"] eval("name='khelll'",greeter) #=> "khelll" greeter.call ["hi","hello","hola"] #=> ["hi khelll", "hello khelll", "hola khelll"] ``` ## instance_eval `instance_eval` はオブジェクトのコンテキストで働きます。 ```ruby class Klass def initialize @secret = 99 end end k = Klass.new k.instance_eval { @secret } #=> 99 , notice the @ ``` シングルトンメソッドを定義するのに使うこともできます。 ```ruby Fixnum.instance_eval "def zero; 0 ;end" Fixnum.zero #=> 0 ``` 文字列の代わりに `block` を渡すことができます。 ```ruby Fixnum.instance_eval{ def ten ;10;end } Fixnum.ten #=> 10 ``` ## class_eval レシーバーのコンテキストの文字列やブロックを評価します。 ```ruby Foo.class_eval{@@x=1} #=> 1 Foo.class_eval{@@x} #=> 1 ``` インスタンスメソッドを定義します。 ```ruby Fixnum.class_eval "def number ; self ;end" 5.number #=> 5 ``` `instance_eval` と同じく、文字列の代わりに `block` を渡すことができます。 ```ruby Fixnum.class_eval{ def number;self;end} 7.number #=> 7 ``` `class_eval` を使うと、 private メソッドを動的に呼び出すことができます。例として、 `include` という private メソッドを呼び出してみます。 ```ruby module M; end String.include M #=> NoMethodError: private method `include' called for String:Class String.class_eval{include M} #=> you could do it with String.send(:include,M) ``` これまでの知識を使って、自作 `attr_accessor` メソッドをつくってみましょう。 `attr_access` というメソッドをつくります。 ```ruby class Class def attr_access(*attrs) attrs.each do |attr| class_eval %Q{ def #{attr} @#{attr} end def #{attr}=(value) @#{attr} = value end } end end end class Foo attr_access :a,:b end Foo.instance_methods(false) #=> ["b=", "a=", "b", "a"] ``` 同じような方法で、クラスの `attribute` のアクセサを定義することができます。 ```ruby class Class def cattr_access(*attrs) attrs.each do |attr| class_eval %Q{ def self.#{attr} @@#{attr} end def self.#{attr}=(value) @@#{attr} = value end } end end end ``` シングルトンクラスを使うこともできます。 ```ruby class Class def singleton_class class << self self end end def cattr_access(*attrs) attrs.each do |attr| singleton_class.class_eval %Q{ def #{attr} @@#{attr} end def #{attr}=(value) @@#{attr} = value end } end end end ``` どちらの場合でもOKです。 ```ruby class Foo ; cattr_access :cx,:cy end Foo.singleton_methods(false) #=> ["cy", "cy=", "cx", "cx="] ``` オブジェクト変数やクラス変数にアクセスする `attr_reader` や `attr_writer` の定義も試してみてください。