问题描述
我可以使用TracePoint API来访问Ruby方法的参数:
I'm able to get access to a Ruby method's arguments using the TracePoint API:
def foo(foo_arg)
end
trace = TracePoint.trace(:call, :c_call) do |tp|
tp.disable
case tp.method_id
when :foo, :sub
method = eval("method(:#{tp.method_id})", tp.binding)
method.parameters.each do |p|
puts "#{p.last}: #{tp.binding.local_variable_get(p.last)}"
end
end
tp.enable
end
trace.enable
foo(10)
# => foo_arg: 10
但是,当我使用c方法调用尝试此操作时,会出现错误.
However when I try this with a c method call, I get an error.
"foo".sub(/(f)/) { $1.upcase }
script.rb:20:in `method': undefined method `sub' for class `Object' (NameError)
from script.rb:20:in `<main>'
from script.rb:8:in `eval'
from script.rb:8:in `block in <main>'
from script.rb:20:in `<main>'
这似乎是由于使用C方法调用和常规Ruby方法调用返回的绑定之间存在差异而发生的.
This looks like it happens because of a discrepancy between the binding returned when using a C method call and regular Ruby method call.
在Ruby中,tp.self
等于tp.binding.eval("self")
是main
,但是在C情况下,tp.self
是"foo"
,而tp.binding.eval("self")
是main
.有没有办法将使用TracePoint的参数传递给Ruby和C定义的方法?
In the Ruby case tp.self
is equal to tp.binding.eval("self")
is main
however in the C case tp.self
is "foo"
and tp.binding.eval("self")
is main
. Is there a way to get the arguments passed into a method using TracePoint for both Ruby and C defined methods?
推荐答案
在您指出问题并记录在红宝石文档,tp.self
返回一个跟踪的对象,该对象具有您要查找的method
方法.我认为您应该使用
As you point in your question and as it documented in ruby documentation, tp.self
returns a traced object, which have a method
method you are looking for.I think you should use
method = tp.self.method(tp.method_id)
代替
method = eval("method(:#{tp.method_id})", tp.binding)
更新.有关您所讨论的最后一段的一些解释.在第一种情况下(调用foo
时)tp.self
指向main
,因为您在主上下文中定义了foo
方法,而在第二种情况下它又指向了String
对象,因为在此定义了sub
.但是tp.binding.eval("self")
在两种情况下都会返回main
,因为它会返回调用上下文(而不是您期望的定义"上下文),并且在两种情况下均为main
.
UPDATE. Some explanation regarding your last paragraph in question. tp.self
in first case (when you call foo
) is point to main
, because you define foo
method in main context and it points to String
object in second case because sub
is defined there. But tp.binding.eval("self")
returns main
in both cases because it returns a calling context (not a 'define' context as you expect) and in both cases it is main
.
更新(回复评论),我认为唯一的方法就是猴子补丁sub
和您感兴趣的所有其他方法.代码示例:
UPDATE (in reply to comment) I think that the only way to do this is to monkey patch sub
and all other methods that you are interesting for. Code example:
class String
alias_method :old_sub, :sub
def sub(*args, &block)
old_sub(*args, &block)
end
end
trace = TracePoint.trace(:call, :c_call) do |tp|
tp.disable
case tp.method_id
when :sub
method = tp.self.method(tp.method_id)
puts method.parameters.inspect
end
tp.enable
end
trace.enable
"foo".sub(/(f)/) { |s| s.upcase }
一个很大的缺点是您不能在原始块中使用$1, $2, ...
var.如所指出的,此处在哪里无法使其正常工作.但是,您仍然可以使用块参数(在我的示例中为s
).
One big drawback is that you can't use $1, $2, ...
vars in your original blocks. As pointed here where is no way to make it works. However you can still use block parameters (s
in my example).
这篇关于使用Ruby的TracePoint获取方法参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!