ruby - 使用 Ruby 的 TracePoint 获取方法参数

标签 ruby metaprogramming

我能够使用 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 方法调用时返回的绑定(bind)之间存在差异。

在 Ruby 中 tp.self 等于 tp.binding.eval("self")main 但是在 C 中tp.self"foo" 并且 tp.binding.eval("self")main。对于 Ruby 和 C 定义的方法,有没有办法使用 TracePoint 将参数传递到方法中?

最佳答案

正如您在问题中指出的那样以及在 ruby documentation 中记录的那样, tp.self 返回一个跟踪对象,它有一个你要找的method 方法。 我认为你应该使用

method = tp.self.method(tp.method_id)

代替

method = eval("method(:#{tp.method_id})", tp.binding)

更新。关于您所讨论的最后一段的一些解释。 tp.self 在第一种情况下(当你调用 foo 时)指向 main,因为你定义了 foo 方法在主上下文中,它指向第二种情况下的 String 对象,因为 sub 是在那里定义的。但是 tp.binding.eval("self") 在这两种情况下都返回 main 因为它返回一个调用上下文(不是你期望的“定义”上下文)并且在这两种情况下情况下它是 main

更新(回复评论) 我认为唯一的方法是猴子补丁 sub 和您感兴趣的所有其他方法。代码示例:

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 }

一个很大的缺点是您不能在原始 block 中使用 $1, $2, ... 变量。正如指出的那样here哪里没有办法让它工作。但是,您仍然可以使用 block 参数(在我的示例中为 s)。

关于ruby - 使用 Ruby 的 TracePoint 获取方法参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30584454/

相关文章:

ruby - 重命名由 gem 添加的方法

mysql - 在 Ubuntu 14 上安装 mysql2 gem 失败

Ruby:来自散列的元编程方法

ruby-on-rails - 对 Rails 测试完全感到困惑。哪些工具适用于哪些工作?

ruby-on-rails - `materialize' : Could not find

c++ - T::* 在模板参数中是什么意思?

c++ - 检测习语 : why does the condition have to be a `using` directive?

vbscript - 在 VBScript 中对方法调用进行字符串化

ruby-on-rails - 传递给 Controller ​​的模型 Rub​​y Rails 验证失败

ruby-on-rails - 如何向 2 个收件人发送电子邮件并且两个收件人在收件人字段中看到对方?