Программирование на языке Ruby.

11.3.1. Динамическая интерпретация кода.

Глобальная функция eval компилирует и исполняет строку, содержащую код на Ruby. Это очень мощный (и вместе с тем опасный) механизм, поскольку позволяет строить подлежащий исполнению код во время работы программы. Например, в следующем фрагменте считываются строки вида «имя = выражение», затем каждое выражение вычисляется, а результат сохраняется в хэше, индексированном именем переменной.

Parameters = {}.

ARGF.each do |line|

 name, expr = line.split(/\s*=\s*/, 2).

 parameters[name] = eval expr.

End.

Пусть на вход подаются следующие строки:

А = 1.

B = 2 + 3.

С = 'date'.

Тогда в результате мы получим такой хэш: {"а"=>1, "b"=>5,"с"=>"Mon Apr 30 21:17:47 CDT 2001\n"}. На этом примере демонстрируется также опасность вычисления с помощью eval строк, содержимое которых вы не контролируете; злонамеренный пользователь может подсунуть строку d= 'rm *' и стереть всю вашу дневную работу.

В Ruby есть еще три метода, которые интерпретируют код «на лету»: class_eval, module_eval и instance_eval. Первые два — синонимы, и все они выполняют одно и то же: интерпретируют строку или блок, но при этом изменяют значение псевдопеременной self так, что она указывает на объект, от имени которого эти методы вызваны. Наверное, чаще всего метод class_eval применяется для добавления методов в класс, на который у вас имеется только ссылка. Мы продемонстрируем это в коде метода hook_method в примере утилиты Trace в разделе 11.3.13. Другие примеры вы найдете в динамических библиотечных модулях, например delegate.rb.

Метод eval позволяет также вычислять локальные переменные в контексте, не принадлежащем их области видимости. Мы не рекомендуем легкомысленно относиться к этой возможности, но знать, что она существует, полезно.

Ruby ассоциирует локальные переменные с блоками, с определениями высокоуровневых конструкций (класса, модуля и метода) и с верхним уровнем программы (кодом, расположенным вне любых определений). С каждой из этих областей видимости ассоциируются привязки переменных и другие внутренние детали. Наверное, самым главным потребителем информации о привязках является программа irb — интерактивная оболочка для Ruby, которая пользуется привязками, чтобы отделить собственные переменные от тех, которые принадлежат вводимой программе.

Можно инкапсулировать текущую привязку в объект с помощью метода Kernel#binding. Тогда вы сможете передать привязку в виде второго параметра методу eval, установив контекст исполнения для интерпретируемого кода.

Def some_method.

 а = "local variable".

 return binding.

End.

The_binding = some_method.

Eval "a", the_binding # "local variable".

Интересно, что информация о наличии блока, ассоциированного с методом, сохраняется как часть привязки, поэтому возможны такие трюки:

Def some_method.

 return binding.

End.

The_binding = some_method { puts "hello" }.

Eval "yield", the_binding # hello.