Тестирование фрагментов кода Ruby с помощью eval () в Ruby 1.9 - PullRequest
1 голос
/ 09 сентября 2011

Я бы хотел использовать eval() в Ruby 1.9 для интерактивного тестирования маленьких кусочков рубинового кода. Давным-давно (около Ruby 1.4) я нашел в интернете аккуратный скрипт, обеспечивающий эту функциональность. Вот упрощенная и сокращенная версия:

line = ''
$stdout.sync = true
print "ruby> "
while true
    input = gets
    if input
        line = input 
    else
        break if line == ''
    end
    begin
        print eval(line).inspect, "\n"
    rescue ScriptError, StandardError
        $! = 'exception raised' unless $!
        print "ERROR: ", $!, "\n"
    end
    break if not input
    line = ''
    print "ruby> "
end

Я смог сделать что-то вроде:

ruby> str = "a:b:c"
"a:b:c"
ruby> str.split /:/
["a", "b", "c"]
ruby>

Этот скрипт прекрасно работает до Ruby 1.8, но больше не в 1.9 из-за измененной семантики eval(). Теперь я больше не могу создавать локальные переменные, такие как str. Вместо этого я получаю следующее очевидное сообщение:

ERROR: undefined local variable or method `str' for main:Object

Есть ли способ исправить или обойти это поведение eval()? Я читал кое-что о привязках, но я не уверен, как это сделать здесь.

Конечно, есть irb, но в этом инструменте я не могу использовать знак фунта, как в "abc#{var}def". Если я попробую, то irb закомментирует всю строку.

1 Ответ

4 голосов
/ 09 сентября 2011

Рабочий код:

$stdout.sync = true
while true
  print "ruby> "
  input = gets
  break unless input
  begin
    p eval(input, TOPLEVEL_BINDING)
  rescue ScriptError, StandardError
    puts "ERROR: #{$! || "exception raised"}"
  end
end

Я много чего изменил в коде, чтобы сделать его чистым, но смысл в этом eval. Он выполнялся внутри блока begin. Все локальные переменные, определенные eval, создавались внутри блока begin и уничтожались после завершения begin после каждой итерации. Константа TOPLEVEL_BINDING возвращает область действия верхнего уровня (вне всего). Это заставляет eval выполнять код в месте, которое не будет уничтожено (пока программа не завершится). Вы также можете получить область действия любого места, используя метод binding, и отправить его в качестве второго аргумента eval.

def get_a_binding
  x = 42
  return binding
end

x = 50
scope = get_a_binding
eval("print x", scope) #=> prints 42
...