Книга: Программирование на языке Ruby
11.3.1. Динамическая интерпретация кода
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 2001n"}
. На этом примере демонстрируется также опасность вычисления с помощью 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
- 11.3.1. Динамическая интерпретация кода
- 11.3.2. Метод const_get
- 11.3.3. Динамическое создание экземпляра класса, заданного своим именем
- 11.3.4. Получение и установка переменных экземпляра
- 11.3.5. Метод define_method
- 11.3.6. Метод const_missing
- 11.3.7. Удаление определений
- 11.3.8. Получение списка определенных сущностей
- 11.3.9. Просмотр стека вызовов
- 11.3.10. Мониторинг выполнения программы
- 11.3.11. Обход пространства объектов
- 11.3.12. Обработка вызовов несуществующих методов
- 11.3.13. Отслеживание изменений в определении класса или объекта
- 11.3.14. Определение чистильщиков для объектов
- 11.3. Динамические механизмы
- 11.3.3. Динамическое создание экземпляра класса, заданного своим именем
- Глава 5 Агрессивные формы кода и борьба с ними
- Стиль написания исходного кода
- Анализ CIL-кода
- Исправление ранее написанного кода
- 8.9.3. Динамическая модель системы
- Преобразование WSDL-кода в программный код агента для клиента
- Вызов окна программного кода
- Строки кода и комментарии
- ДИНАМИЧЕСКАЯ НАВИГАЦИЯ
- 2.4.3. Генерация кода в Power Builder