我已经编写了一个简单的代码,该代码可以评估一段代码并将输出写入文件。这样可以减少我的一些工作,因为我需要每行中包含很多返回值的文件!
无论如何,我正在使用的代码是:
#!/usr/bin/ruby -w
def create(file, code)
f = code.strip.each_line.map { |cd| cd.strip.then { |c| [c, "# => #{binding.eval(c)}"] } }
max_length = f.map { |x| x[0].length }.max + 4
f.map { |v| v[0].ljust(max_length) << v[1] }.join("\n").tap { |data| File.write(file, data + "\n") }
end
puts create(
File.join(__dir__, 'p.rb'),
<<~'EOF'
foo = 1
bar = 2
baz, qux = 5, 3
EOF
)
在这种情况下,将写入文件p.rb。 p.rb的内容是:
foo = 1 # => 1
bar = 2 # => 2
baz, qux = 5, 3 # => [5, 3]
但是当我想要一个变量的值时就会发生问题。 例如:
puts create(
File.join(__dir__, 'p.rb'),
<<~'EOF'
baz, qux = 5, 3
[baz, qux]
EOF
)
输出:
/tmp/aa.rb:4:in `block (2 levels) in create': undefined local variable or method `baz' for main:Object (NameError)
from /tmp/aa.rb:4:in `eval'
from /tmp/aa.rb:4:in `block (2 levels) in create'
from /tmp/aa.rb:4:in `then'
from /tmp/aa.rb:4:in `block in create'
from /tmp/aa.rb:4:in `each_line'
from /tmp/aa.rb:4:in `each'
from /tmp/aa.rb:4:in `map'
from /tmp/aa.rb:4:in `create'
from /tmp/aa.rb:9:in `<main>'
以前,我曾在一些图形游戏中工作过,在读取配置文件后也做了这种事情,但是在那里,我曾经将变量定义为全局变量(只是在变量声明之前附加$),或者仅在顶级自我对象。
但是有没有办法解决我当前面临的问题?我可以定义绑定中的变量或类似的技巧吗?
答案 0 :(得分:3)
binding
都会返回一个新实例。您必须将eval
发送到相同的绑定,才能访问之前创建的局部变量:
def create(code, b = binding)
width = code.each_line.map(&:length).max
code.each_line.map do |line|
'%-*s #=> %s' % [width, line.chomp, b.eval(line)]
end
end
puts create <<~'RUBY'
baz, qux = 5, 3
baz
qux
RUBY
输出:
baz, qux = 5, 3 #=> [5, 3]
baz #=> 5
qux #=> 3
请注意,在上面的示例中,binding
将使该方法的局部变量可用于该块:
create 'local_variables'
#=> ["local_variables #=> [:code, :b, :width]"]
您可能想创建一个更受限制的评估环境,例如(复制Ruby的main)
def empty_binding
Object.allocate.instance_eval do
class << self
def to_s
'main'
end
alias inspect to_s
end
return binding
end
end
def create(code, b = empty_binding)
# ...
end