【问题标题】:Can I access the source of a ruby block without &block?我可以在没有 &block 的情况下访问 ruby​​ 块的源代码吗?
【发布时间】:2020-01-23 16:58:36
【问题描述】:

我在 Ruby 2.5 下使用 Pry 来调试基类中的问题 (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,但这对我没有帮助,因为我没有可以在这里使用的方法参数。

【问题讨论】:

  • 作为一个 hacky 解决方法,您可以编辑此文件并将其更改为将块作为显式参数 (&blk) - 这样您就可以在其上调用 .to_ruby
  • 谢谢@maxpleaner。我想我解决了引发此问题的Net::HTTP 问题,但我仍然对检查块的最佳方法感兴趣,理想情况下无需编辑 gem 或发行版提供的 ruby​​ 文件。
  • 说得太早了!看起来我必须编辑我正在使用的 gem 的来源,这使得 Net::HTTP 调用...
  • 如果你真的被卡住了,别忘了你可以用你自己的方法来修补Net::HTTP的元素,这些方法可以拦截、检查、报告,以及用参数和结果做其他事情。这在调试难以导航的第三方库时非常方便。

标签: ruby debugging pry


【解决方案1】:

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 将当前执行堆栈返回为一个数组,其中包含 `method' 中 file:line 形式的字符串。

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

了解这些信息后,您可以使用 bar 调用打开文件并跟踪传递给它的块。

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

希望对您有所帮助。

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

【讨论】:

    【解决方案2】:

    所以,这里真正的问题是我没有阅读所有 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,我能够到达引发异常的块并检查我需要的局部变量。

    【讨论】:

      【解决方案3】:

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

      您的问题是双重的:您必须 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) }"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-14
        • 1970-01-01
        • 2017-04-01
        • 1970-01-01
        • 2017-10-30
        • 1970-01-01
        • 2017-02-04
        • 2015-03-08
        相关资源
        最近更新 更多