こんにちは! フリーエンジニアの長瀬です。
みなさんはevalを使っていますか?
evalを使えば、evalに渡した文字をRubyプログラムとして実行させることができます。
この記事では、rubyのevalについて
・evalの使い方
・Bidingオブジェクトの指定
という基本的な内容から、
・instance_evalの使い方
・class_evalの使い方
・module_evalの使い方
といった応用的な内容についても解説していきます。
evalとは
evalとは何かを一言で説明すると「引数として与えた文字列をRubyプログラムとして実行するメソッド」です。
引数として与えた文字列を評価することができ、実行する文のエラーチェックに使うことができるので、異常発生を検知するときに使えます。
基本的なevalの使い方
evalの使い方
evalの基本的な使い方としては下記のように評価する式を引数として与えるだけです。
eval("puts 'Hello, everyone !'")
正式な構文は以下のようになっています。
eval(expr [,bindingObj, fname, fileno])
指定が必須なのは第1引数のみです。それ以外の引数指定は任意です。
第3引数fname, 第4引数filenoが与えられた場合、ファイル名fnameの行番号filenoに評価する式exprが書かれているかのように見せることができます。第2引数に指定するBindingオブジェクトは次の章で説明します。
例えば、スタックトレースに表示されるファイル名やソースファイルの行番号を自由に変更できます。
下記はサンプルコードです。sample.rbの10行目に「raise RuntimeError」が書かれているかのように見せることができます。
eval('raise RuntimeError', binding, 'sample.rb', 10) # sample.rb:10: RuntimeError (RuntimeError)
Bindingオブジェクトの指定
evalメソッドの第2引数には「Bindingオブジェクト」を指定することができます。
変数やメソッドにはそれらが有効な範囲(スコープ)があります。その定義されたスコープを表すオブジェクトがBindingオブジェクトです。
例えば、あるメソッド内で定義された変数をそのメソッドの外部から使用したいときに指定します。
下記のサンプルコードを使いながら、Bindingオブジェクトについて説明します。
def bindSample hoge = 100 binding # Bindingオブジェクトの生成 end eval("p hoge", bindSample)
[実行結果]
100
bindSampleメソッド内において、ローカル変数hogeを定義しています。
またbindingメソッドを実行することにより、bindSampleメソッドのBindingオブジェクトを生成して返します。
だからこそ、bindSampleメソッドの外部からローカル変数hogeを参照することができるわけです。
なので、たとえば以下のように、eval内でローカル変数を定義した場合evalで参照することができず、エラーを返します。
eval("hogehoge = 'this is hoge'") eval("p hogehoge")
[実行結果]
# undefined local variable or method `hoge'
ですが、すでに定義されている変数についてはevalから呼び出したり値を代入したりできます。
hogehoge = "this is hogehoge." eval("p hogehoge") eval("hogehoge = 'changed'") eval("p hogehoge")
[実行結果]
"this is hogehoge." "changed"
eval応用編
instance_evalの使い方
instance_evalメソッドは、渡されたブロックを実行するものです。以下がサンプルコードです。
class InstanceEvalSample def initialize @name = "hoge" end end x = InstanceEvalSample.new p x.instance_eval { @name }
[実行結果]
"hoge"
このように、インスタンス変数nameをブロック内から参照する事ができます。
class_evalの使い方
class_evalメソッドを使うと、ブロックをあたかもクラス定義やモジュール定義の中のコードであるかのように実行することができます。ブロックの戻り値がメソッドの戻り値となります。
class ClassEvalSample; end ClassEvalSample.class_eval { def hello; "Hello, everyone !"; end } puts ClassEvalSample.new.hello
[実行結果]
"Hello, everyone !"
helloメソッドは、ClassEvalSampleクラスの中で定義していないにも関わらず、その中で定義しているかのように実行できます。
module_evalの使い方
module_evalはclass_evalのエイリアス(名前は違うけど、機能は同じ)です。
なので、class_evalで紹介したサンプルコードをそのままmodule_evalに置き換えることができます。
class ClassEvalSample; end ClassEvalSample.module_eval { def hello; "Hello, everyone !"; end } puts ClassEvalSample.new.hello
[実行結果]
"Hello, everyone !"
まとめ
今回はevalの基本とその使い方について説明したのに加え、Bindingオブジェクトを指定する場合の使い方についても説明しました。
また、応用例としてinstance_eval、class_evalメソッド、module_evalについても紹介しました。evalと類似したメソッドでもありますので、evalの基本を理解した上で、上手く活用していきましょう!