Testing Ruby code snippets with eval() in Ruby 1.9
I would like to use eval()
in Ruby 1.9 to test little pieces of ruby code in an interactive way. A long time ago (around Ruby 1.4) I found a neat script on the internet providing this functionality开发者_JS百科. Here is simplified and reduced version:
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
I was able to do something like:
ruby> str = "a:b:c"
"a:b:c"
ruby> str.split /:/
["a", "b", "c"]
ruby>
This script works fine up to Ruby 1.8, however not anymore in 1.9 due to the changed semantics of eval()
. Now I'm not able to make local variables like str
anymore. Instead I get the following obvious message:
ERROR: undefined local variable or method `str' for main:Object
Is there a way to fix or bypass this behaviour of eval()
? I've read something about bindings but I'm not sure how to do that here.
Of course there is irb
but in that tool I can't use the pound sign like in "abc#{var}def"
. If I try then irb
comments out the whole line.
Working code:
$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
I changed many thing in the code to make it clean, but the point is the scope of the eval
. It was being executed inside the begin
block. All local variables defined by the eval
were being created inside the begin
block and were being destroyed when the begin
ends, after each iteration. The constant TOPLEVEL_BINDING
returns the scope of the top level (outside everything). It makes the eval execute the code in a place that will not be destroyed (until the program ends). You can also get the scope of any place using the method binding
and send it as the second argument of eval
.
def get_a_binding
x = 42
return binding
end
x = 50
scope = get_a_binding
eval("print x", scope) #=> prints 42
精彩评论