binding.pry 和 Pry.start 有什么区别?

What's the difference between binding.pry and Pry.start?

require 'pry'

var = "variable"

class Gnar
    def self.gar
        @var = "lar!"
        # binding.pry
        # Pry.start(binding)
        # Pry.start
    end
end

Gnar.gar

当我取消注释 binding.pry 时,我发现自己处于 self == Gnar 的 Pry REPL 中。当我取消注释 Pry.start(binding) 时,Pry REPL 永远不会启动。当我取消注释 Pry.start 时,我发现自己处于 self == main.

的 Pry REPL 中

我不明白这种行为,我想真正掌握这个必不可少的 Ruby 调试工具的本质。我想关注 binding.pryPry.start(binding).

的行为

在一种情况下,我们在 binding 对象上调用 #pry 方法,在另一种情况下,我们在 Pry class 以 binding 作为参数。

让我们从binding.pry开始。根据the docs#pry定义如下:

def pry(object=nil, hash={})
  if object.nil? || Hash === object
    Pry.start(self, object || {})
  else
    Pry.start(object, hash)
  end
end

根据该方法定义,当我们在不带参数的对象上调用 #pry 时,我们调用 Pry.start(self, {}),其中 selfselfbinding上下文。

那么 Pry.start 发生了什么? The definition in the docs 很长,但我注意到 self 作为 Pry.starttarget 传入,然后在这一行中使用:

options[:target] = Pry.binding_for(target || toplevel_binding) #line 152

之前

# Enter the matrix
  driver.start(options) #line 169

考虑到所有这些,我的理解是 binding.pry 导致通过 driver.start(options)options[:target] = Pry.binding_for(binding) 开始的 REPL。 如果 target == binding 在这两种情况下,为什么 Pry.start(binding) 没有表现出相同的行为(即不启动 REPL)?我做了什么不正确的假设? 我有点理解 Pry.start 启动一个 REPL,默认 self == main target == toplevel_binding

从控制台中调用 Pry.start(binding) 应该与调用 binding.pry:

具有相似的效果
rails c
Loading development environment (Rails 5.1.6.2)
[1] pry(main)> class Gnar
[1] pry(main)*   def self.gar    
[1] pry(main)*     @var = "lar!"        
[1] pry(main)*     Pry.start(binding)
[1] pry(main)*   end  
[1] pry(main)* end  
=> :gar
[2] pry(main)> Gnar.gar

From: /Users/redacted/.gem/gems/pry-0.12.2/lib/pry/pry_instance.rb @ line 388 Pry#evaluate_ruby:

    383: def evaluate_ruby(code)
    384:   inject_sticky_locals!
    385:   exec_hook :before_eval, code, self
    386: 
    387:   result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
 => 388:   set_last_result(result, code)
    389: ensure
    390:   update_input_history(code)
    391:   exec_hook :after_eval, result, self
    392: end

[1] pry(#<Pry>)> 

Binding 对象将代码的执行上下文封装在特定位置。 Binding#pry 在绑定对象上启动 Pry REPL。

Pry.start 刚刚启动一个 Pry REPL。如果你不给它传递一个绑定,那么它只是一个没有任何执行上下文的 Pry REPL;如果传递给它的绑定的执行上下文是 Pry 实例,控制台会通过从 pry(main)(无上下文)更改为 pry(#<Pry>)> 来告诉您它是否具有上下文:

$ rails c
Loading development environment (Rails 5.1.6.2)
[1] pry(main)> Pry.start(binding)

From: /Users/redacted/.gem/gems/pry-0.12.2/lib/pry/pry_instance.rb @ line 388 Pry#evaluate_ruby:

    383: def evaluate_ruby(code)
    384:   inject_sticky_locals!
    385:   exec_hook :before_eval, code, self
    386: 
    387:   result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
 => 388:   set_last_result(result, code)
    389: ensure
    390:   update_input_history(code)
    391:   exec_hook :after_eval, result, self
    392: end

[1] pry(#<Pry>)> exit
=> nil
[2] pry(main)> binding.pry

From: /Users/redacted/.gem/gems/pry-0.12.2/lib/pry/pry_instance.rb @ line 388 Pry#evaluate_ruby:

    383: def evaluate_ruby(code)
    384:   inject_sticky_locals!
    385:   exec_hook :before_eval, code, self
    386: 
    387:   result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
 => 388:   set_last_result(result, code)
    389: ensure
    390:   update_input_history(code)
    391:   exec_hook :after_eval, result, self
    392: end

[1] pry(#<Pry>)> exit
=> nil
[3] pry(main)> exit

类似地,从 class 方法中调用 binding.pry 会在 class (pry(ClassName)) 的执行上下文中打开一个 Pry REPL。从 实例方法 中调用 binding.pry 会在该 实例 (pry(#<ClassName>) 的执行上下文中打开一个 Pry REPL .

rails c
Loading development environment (Rails 5.1.6.2)
[1] pry(main)> class A
[1] pry(main)*   def self.b
[1] pry(main)*     binding.pry
[1] pry(main)*   end  
[1] pry(main)*   
[1] pry(main)*   def c
[1] pry(main)*     binding.pry
[1] pry(main)*   end  
[1] pry(main)* end  
=> :c
[2] pry(main)> A.b

From: (pry) @ line 4 A.b:

    2: def self.b
    3:   binding.pry
 => 4: end

[1] pry(A)> exit
=> nil
[3] pry(main)> A.new.c

From: (pry) @ line 8 A#c:

    6: def c
    7:   binding.pry
 => 8: end

[1] pry(#<A>)> exit
=> nil

希望对您有所帮助。