开发者

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜