2011年8月7日日曜日

rubyのリフレクション

eval

eval関数を使うと、指定した式を評価することができる。
式を文字列として指定するので、構文エラーとなるような文字列を指定したら例外が発生する。

使用例

# 計算を実行
eval("1 + 2")        # -> 3
eval("1 * 2")        # -> 2

# 変数を使用して計算の実行
num = 100
eval("num + num")    # -> 200

# 変数に値を設定
class Hoge
  attr_accessor :id
end

# idプロパティに値を設定
hoge = Hoge.new
eval("hoge.id=100")
puts hoge.id     # -> evalで設定された100が取得される

# インスタンス変数の宣言
eval("@hogehoge = 'hoge'")
puts @hogehoge   # -> @hogehogeが参照でき、'hoge'が取得できる。

# ローカル変数の宣言
# eval内で宣言されたローカル変数のスコープは、evalの処理を抜けたら参照できない。
eval("hogehoge = 12345; puts hogehoge")    # -> ここでは、ローカル変数を参照できる。

begin
  # evalを抜けているので参照すると、未定義ですよエラーが発生する
  puts hogehoge           
rescue => e
  puts e.message
end


インスタンスに対する操作

任意のインスタンス(クラス)のインスタンス変数やクラス変数の値参照や値設定方法

# インスタンス変数の操作
object = Object.new
# nameインスタンス変数は、存在していないのでfalse
object.instance_variable_defined?(:@name)
# nameインスタンス変数に値(instance_variable)を設定
object.instance_variable_set(:@name, "instance_variable")
# instance_variable_getが返される
object.instance_variable_get(:@name)
# nameインスタンス変数が追加されたのでtrue
object.instance_variable_defined?(:@name)
# nameインスタンス変数を削除
object.instance_eval { remove_instance_variable :@name }
# nameインスタンス変数は削除されたのでfalse
object.instance_variable_defined?(:@name)

# クラス変数に値をセット(宣言されていなければ追加される。)
# nameクラス変数は存在していないのでfalse
puts Object.class_variable_defined? :@@name
# nameクラス変数に値(class_variable)を設定
Object.class_variable_set(:@@name, "class_variable")
# class_variable_getが返される
puts Object.class_variable_get(:@@name)
# nameクラス変数が追加されたのでtrue
puts Object.class_variable_defined? :@@name
# nameクラス変数を削除
Object.class_eval { remove_class_variable :@@name }
# nameクラス変数は削除されたのでfalse
puts Object.class_variable_defined? :@@name

メソッドに対する操作

メソッド一覧の参照や、メソッドの実行ができる。また、動的にインスタンスにメソッドを追加することもできたりする。

object = Object.new
# public_methodsでも同じことができる。
object.methods
# 継承したメソッドを除外(自クラスで宣言したメソッドのみが出力される。)
# trueにすると、methodsと同じ動きになる。ちなみに引数省略した場合は、true
object.public_methods false

# 他にも、private_methodsやprotected_methodsがある。

# 特異メソッドの定義(このインスタンスのみがもつメソッド)
def object.hoge
  "fuga"
end
object.hoge
object.public_methods false

# メソッド呼び出し
hoge = "hogehoge"
hoge.send(:upcase)   # upcaseメソッドの呼び出し(メソッドは、シンボルとして指定する。)