【问题标题】:Triggering Pry binding based on contents of stacktrace根据 stacktrace 的内容触发 Pry 绑定
【发布时间】:2015-03-11 22:39:18
【问题描述】:

我想在一个函数中启动一个 Pry 会话,但是由于我现在正在处理的项目中的一些元编程,Pry 在很多我不感兴趣的上下文中被触发。我该怎么做根据程序当前执行的堆栈跟踪触发 pry?

类似的,

binding.pry if stacktrace.include? "function_name"

【问题讨论】:

  • 你尝试这样做时发生了什么?
  • @theTinMan 我想stacktrace 没有定义。所以他会得到NameError: undefined local variable or method 'stacktrace' for main:Object

标签: ruby-on-rails ruby pry


【解决方案1】:

对于 Ruby 2.0+,您可以使用 Kernel#caller_locations 获取程序的当前堆栈跟踪。

来自the docs for Kernel#caller_locations

caller_locations(start=1, length=nil) → 数组或 nil

source caller_locations(range) → array or nil

返回当前执行堆栈——一个包含回溯的数组 位置对象。

请参阅Thread::Backtrace::Location 了解更多信息。

所以我们从文档中可以看出,这个方法返回一个Thread::Backtrace::Location对象的数组,你可以用它来判断是否调用binding.pryThread::Backtrace::Location 提供了一个名为base_label 的方法,它返回堆栈中当前位置的方法名称。您可以使用它来检查当前堆栈跟踪是否通过具有特定名称的方法运行。

使用示例:

def a
  caller_locations(0)
end
def b
  a
end
def c
  b
end

c.map(&:base_label)
#=> ["a", "b", "c", "<main>"]

所以在你的情况下,你会像这样使用它:

binding.pry if caller_locations.map(&:base_label).include? function_name

如果您使用的是旧版本的 Ruby (Kernel#caller_locations 不可用,您必须改用 Kernel#caller

caller(start=1, length=nil) → array or nil

调用者(范围)→数组或nil

返回当前的执行栈——一个包含字符串的数组 表单文件:行或文件:行:在`方法'中。

可选的start参数决定了初始栈的个数 要从堆栈顶部省略的条目。

第二个可选长度参数可用于限制多少 条目从堆栈中返回。

如果 start 大于当前执行的大小,则返回 nil 堆栈。

您可以选择传递一个范围,该范围将返回一个包含 指定范围内的条目。

def a(skip)
  caller(skip)
end
def b(skip)
  a(skip)
end
def c(skip)
  b(skip)
end
c(0)   #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10:in `<main>'"]
c(1)   #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11:in `<main>'"]
c(2)   #=> ["prog:8:in `c'", "prog:12:in `<main>'"]
c(3)   #=> ["prog:13:in `<main>'"]
c(4)   #=> []
c(5)   #=> nil

您会注意到Kernel#caller_locationsKernel#caller 之间的唯一区别是Kernel#caller 返回一个字符串数组,而不是Thread::Backtrace::Location 对象数组。这意味着您必须使用诸如正则表达式之类的东西来匹配方法名称,而不是像我们使用Kernel#caller_locations 那样只使用Array#include?。在你的情况下:

binding.pry if caller.any?{|stack_entry| stack_entry =~ /in `#{function_name}'$/}

有关在 Ruby 中获取堆栈跟踪的更多信息,请参阅how to get a stack trace object in Ruby?

【讨论】:

  • 我最终选择了binding.pry if caller.any?{|stack_entry| stack_entry =~ /[actual name of function]/},因为我可以对其进行硬编码(我只是在调试)。 $ 符号的用途是什么?我猜“额外”的东西是允许在正则表达式内进行字符串插值?
  • @AndrewLngdn 注意caller 返回的所有字符串都是"file:line: in `method'" 的形式吗?好吧,/in `#{function_name}'$/ 匹配所有以in `method'" 结尾的字符串。 (Example / Full Explanation) 它比/function_name/ 更严格一些,它匹配字符串中任何位置的函数名,即使在文件名之类的地方也是如此。插值是用#{expression} 完成的,其他字符只是正则表达式的一部分。
【解决方案2】:

试试

binding.pry if caller.any?{|fn_name| fn_name.include?('function_name')}

参考:How to get a stack trace object in Ruby?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-08-18
    • 2011-02-05
    • 2019-10-11
    • 2019-01-28
    • 2011-07-13
    • 2011-11-10
    • 1970-01-01
    • 2018-02-02
    相关资源
    最近更新 更多