我可以在没有 &block 的情况下访问 ruby 块的源代码吗?

Can I access the source of a ruby block without &block?

我在 Ruby 2.5 下使用 Pry 调试基础 class (Net::HTTP)

中的问题

我收到由 HTTP 404 响应引起的异常,我想检查发出的请求的正文。为此,我想检查传递给 start 的块,但是 start 方法没有参数,它是使用 yield:

调用的
Frame type: method

From: /usr/share/ruby/net/http.rb @ line 910 Net::HTTP#start:

    905: def start  # :yield: http
    906:   raise IOError, 'HTTP session already opened' if @started
    907:   if block_given?
    908:     begin
    909:       do_start
 => 910:       return yield(self)

使用 Pry 是否有任何方法可以查看块的来源,如果该块未在 &block 参数中传递?

看到 Printing the source code of a Ruby block 但它对我没有帮助,因为我没有方法参数可以在这里使用。

TL;DR 你可以尝试利用 Kernel#caller.


让我们考虑一个名为 foo.rb:

的文件

(本例使用byebug,但与binging.pry的流程几乎相同)

1: require 'byebug'
2: 
3: def bar
4:   byebug
5: 
6:   yield
7: end
8: 
9: bar { 'some string' }

当我们用ruby foo.rb运行这个文件时,我们会在byebug语句处停止。

[1, 9] in foo.rb
   1: require 'byebug'
   2: 
   3: def bar
   4:   byebug
   5: 
=> 6:   yield
   7: end
   8: 
   9: bar { 'some string' }

然后我们可以执行caller,然后会打印类似下面的内容:

(故意减少和格式化输出)

(byebug) caller
[
  "~/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/byebug-11.0.1/lib/byebug/processors/command_processor.rb:97:in `process_commands'",
  "~/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/byebug-11.0.1/lib/byebug/processors/command_processor.rb:55:in `at_line'",
  "~/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/byebug-11.0.1/lib/byebug/context.rb:98:in `at_line'",
  "foo.rb:6:in `bar'",
  "foo.rb:9:in `<main>'"
]

如您所见,caller returns 当前执行堆栈作为一个数组,包含 `method' 中 file:line 形式的字符串。

此数组中的最后一个字符串指定调用 bar 的位置。

了解此信息后,您可以使用该 bar 调用打开文件并跟踪哪个块被传递到其中。

foo.rb:9 在这种特殊情况下)

希望对您有所帮助。

作为奖励,Tenderlove - I am a puts debugger 有一篇很棒的文章,您可以在其中找到每个 Ruby 调试问题的解决方案。

所以,这里真正的问题是我没有阅读所有 Pry 帮助(特别是 rescue-pry)并且不知道 up 命令...

Usage: up [OPTIONS]
  Go up to the caller's context. Accepts optional numeric parameter for how many frames to move up.
  Also accepts a string (regex) instead of numeric; for jumping to nearest parent method frame which matches the regex.
  e.g: up      #=> Move up 1 stack frame.
  e.g: up 3    #=> Move up 2 stack frames.
  e.g: up meth #=> Jump to nearest parent stack frame whose method matches /meth/ regex, i.e `my_method`.

    -h, --help      Show this message.

通过在引发异常的 pry 会话中执行 up,我能够到达引发异常的块并检查我需要的局部变量。

Using Pry is there any way to view the source of a block, if that block is not passed in a &block parameter?

您的问题是双重的:您必须 1) 获取隐式块和 2) 打印其源代码。

对于 1),您只需在方法的上下文中调用 Proc.new 即可将其块参数作为 proc。

所以代替:

def foo(&block)
  # ...
end

您还可以通过以下方式检索区块:

def foo
  block = Proc.new
  # ...
end

对于 2) 你可以使用 sourcify gem:

require 'sourcify'

prc = Proc.new { |i| i * 2 }
prc.to_source
#=> "proc { |i| (i * 2) }"

应用于您的问题:

# foo.rb
require 'pry'
require 'sourcify'

def foo
  binding.pry
end

foo { |i| i * 2 }
$ ruby foo.rb

From: foo.rb @ line 6 Object#foo:

    4: def foo
 => 5:   binding.pry
    6: end

[1] pry(main)> block = Proc.new
#=> #<Proc:0x00007f9373b1c368@foo.rb:9>

[2] pry(main)> block.call(123)
#=> 246

[3] pry(main)> block.to_source
#=> "proc { |i| (i * 2) }"