Ruby の eval メソッドなどで引数として指定される Binding オブジェクトについて説明します。
Binding オブジェクトとは
Binding オブジェクトとは、変数・メソッドなどの環境情報を表すオブジェクトです。言い換えると、変数やメソッドが定義された文脈(スコープ)を表すものです。
例えば、あるメソッド内のローカル変数を外部から eval メソッドなどで参照したい時に、そのメソッドの Binding オブジェクトを指定するなどといった使い方をします。
binding メソッド
binding メソッドは、Binding オブジェクトを生成して返すカーネルメソッドです。Binding オブジェクトは、この binding メソッドからのみ生成されます。
Binding オブジェクトを使ったサンプル
メソッドの Binding オブジェクト
以下のような sample メソッドがあるとします。ローカル変数 a を定義し、 binding メソッドで sample メソッドの Binding オブジェクトを生成し返します。
1 2 3 4 |
def sample a = 1 binding ##fooの Binding オブジェクトを生成して返す end |
この時、以下のように eval メソッドで変数 a を表示しようとしても、変数 a はローカル変数なので undefined エラーとなります。
1 2 |
eval("p a") #=> undefined local variable or method `a' |
しかし、eval メソッドの第二引数に、sample メソッドのBinding オブジェクトを指定するとエラーとならず「1」と表示されます。
1 2 |
eval("p a", sample) #=> 1 |
クラスの Binding オブジェクト
以下のような Person クラスがあるとします。bind メソッドは、Person クラスの Binding オブジェクトを返すだけのメソッドです。
1 2 3 4 5 6 7 8 9 |
class Person @name def initialize(name) @name = name end def bind # Personの環境情報bindingを返すメソッド binding end end |
この Person クラスを元にした peter と jason というインスタンスを作成します。
そして、eval メソッドで インスタンス変数 @name を参照し表示するとします。
この時、第二引数に peter.bind を指定した場合は、peter オブジェクトの Binding オブジェクト が適用されるので、「Peter」と表示されます。一方、 jason.bind を指定した場合は、 「Jason」と表示されます。
1 2 3 4 5 6 7 8 |
peter = Person.new("Peter") jason = Person.new("Jason") eval("puts @name", peter.bind) #"Peter" eval("puts @name", jason.bind) #"Jason" |
また、第二引数を指定しない場合は、以下のようにエラーとなります。
1 2 |
eval("puts @name") #=>(eval):1: warning: class variable access from toplevel |
Binding オブジェクトの使いどころ
Binding オブジェクトは、eval メソッドの第二引数として使われ、メタプログラミングなどに利用されます。
メタプログラミングとは、簡単に言ってしまえば「コードでコードを生成し実行する」することを指すプログラミング技法の一種です。