我可以使用TracePoint API访问
Ruby方法的参数:
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方法调用时,我收到错误.
"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方法调用之间的差异.
在Ruby的情况下,tp.self等于tp.binding.eval(“self”)是主要的但是在C情况下tp.self是“foo”而tp.binding.eval(“self”)是main.有没有办法将参数传递给使用TracePoint的方法用于Ruby和C定义的方法?
最佳答案 当你指出你的问题并且在
ruby documentation中记录时,tp.self返回一个跟踪对象,它有一个你正在寻找的方法方法.
我想你应该用
method = tp.self.method(tp.method_id)
代替
method = eval(“method(:#{tp.method_id})”“,tp.binding)
UPDATE.关于你最后一个段落的一些解释. tp.self在第一种情况下(当你调用foo时)指向main,因为你在主上下文中定义了foo方法,并且它在第二种情况下指向String对象,因为sub在那里被定义.但是tp.binding.eval(“self”)在两种情况下都返回main,因为它返回一个调用上下文(而不是你期望的’define’上下文),并且在两种情况下它都是main.
更新(回复评论)我认为这样做的唯一方法是使用补丁子和所有其他你感兴趣的方法.代码示例:
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,… vars.正如here所指出的那样,无法使其发挥作用.但是,您仍然可以使用块参数(在我的示例中为s).