这是我理想的样子。用户:
a="hello"
,输出为
You just allocated "a" !
=> "Hello"
只要我能发出消息,订单就无关紧要了。
答案 0 :(得分:7)
不,没有直接的方法来实现这一点,因为在执行代码之前,Ruby字节码编译器会丢弃局部变量名。
唯一的指令YARV(MRI 1.9.2中使用的Ruby VM)提供的局部变量是getlocal
和setlocal
,它们都是对整数指标而不是变量名进行操作。以下是1.9.2来源中insns.def
的摘录:
/**********************************************************/
/* deal with variables */
/**********************************************************/
/**
@c variable
@e get local variable value (which is pointed by idx).
@j idx
*/
DEFINE_INSN
getlocal
(lindex_t idx)
()
(VALUE val)
{
val = *(GET_LFP() - idx);
}
/**
@c variable
@e set local variable value (which is pointed by idx) as val.
@j idx
*/
DEFINE_INSN
setlocal
(lindex_t idx)
(VALUE val)
()
{
(*(GET_LFP() - idx)) = val;
}
有可能破解MRI源(或使用set_trace_func
并潜入Binding
对象 - 请参阅sarnold的答案),以便在设置局部变量时通知您,但是没有这样做的任何高级方式都可能无法在不进入解释器内部的情况下检索这些局部变量的名称。
答案 1 :(得分:4)
我想出了一个基于set_trace_func
的解决方案。我实际上无法反击limitations Charlie points out,但我相信我所写的内容应该或多或少地与你所描述的一样:
#!/usr/bin/ruby
def hash_from_binding(bin)
h = Hash.new
bin.eval("local_variables").each do |i|
v = bin.eval(i)
v && h[i]=bin.eval(i)
end
bin.eval("instance_variables").each do |i|
v = bin.eval(i)
v && h[i]=bin.eval(i)
end
h
end
$old_binding = hash_from_binding(binding)
$new_binding = hash_from_binding(binding)
set_trace_func lambda {|event, file, line, id, bin, classname|
$old_binding = $new_binding
$new_binding = hash_from_binding(bin)
diff = $new_binding.reject {|k, v| $old_binding[k] == $new_binding[k]}
printf("%d:\n", line)
# $old_binding.each do |k,v|
# printf("%8s: %s\n", k, v)
# end
# $new_binding.each do |k,v|
# printf("%8s: %s\n", k, v)
# end
diff.each do |k,v|
printf("%8s: %s\n", k, v)
end
}
a = "hello"
b = "world"
c = "there"
d = nil
e = false
@a = "HELLO"
@b = "WORLD"
A="Hello"
B="World"
def foo
foo_a = "foo"
@foo_b = "foo"
end
foo
hash_from_binding(bin)
会将Binding
对象转换为Hash
。如果您不想要,可以删除instance_variables
部分。如果您不想要,可以删除local_variables
部分。 v && h[i]=bin.eval(i)
的复杂性是由于Binding对象中的奇怪 - 即使跟踪函数还没有“解析”所有内容,传递给跟踪函数的Binding
对象 知道将在范围中定义的所有变量。这很尴尬。这至少会过滤掉尚未赋值的变量。因此,它还会过滤掉分配值nil
或false
的变量。您可能对跟踪功能中的reject
操作感到满意,可以为您执行过滤工作。
set_trace_func
API将为每个解析的源代码行调用跟踪方法。 (面对不同的执行环境,这可能是一个极大的限制。)所以我编写了一个跟踪函数,将旧绑定对象与 new 绑定对象和报告进行比较已更改的变量定义。您还可以报告 new 的变量定义,但这会错过以下情况:
a = 1
a = 2
一个有趣的结果是,报告的绑定在函数调用中发生了巨大变化,因为新变量已经生效,旧变量从环境中删除。这可能会过度混淆输出,但是event
参数可能用于确定是否打印新的变量值。 (由于函数调用可能会修改“返回者”代码范围内的变量值,因此打印它们似乎都是安全的方法。)
当工具自行运行时,它会输出以下内容:
$ ./binding.rb
38:
39:
a: hello
40:
b: world
41:
c: there
42:
43:
44:
@a: HELLO
45:
@b: WORLD
46:
48:
48:
48:
53:
@a: HELLO
@b: WORLD
48:
49:
50:
foo_a: foo
50:
@foo_b: foo
$
这是我运行此工具时最复杂的Ruby代码,所以它可能会破坏一些非常重要的东西。