В чем разница между binding.pry и Pry.start? - PullRequest
1 голос
/ 27 сентября 2019
require 'pry'

var = "variable"

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

Gnar.gar

Когда я раскомментирую binding.pry, я попадаю в Pry REPL с self == Gnar.Когда я раскомментирую Pry.start(binding), Pry REPL никогда не запускается.Когда я раскомментирую Pry.start, я попадаю в Pry REPL с self == main.

. Я не понимаю этого поведения и хочу по-настоящему понять все эти важные инструменты отладки Ruby.Я хочу сосредоточиться на поведении binding.pry и Pry.start(binding).

В одном случае мы вызываем метод #pry для объекта binding, а в другом - .start метод класса Pry с binding в качестве аргумента.

Давайте начнем с binding.pry.Согласно документы , #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, {}) где self - это то, что self находится в контексте binding.

Так что же происходит в Pry.start? Определение в документе довольно длинное, но я замечаю, что self передается как target из Pry.start, который затем используется в этой строке:

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

до

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

Учитывая все это, я понимаю, что binding.pry приводит к REPL, начинающемуся с driver.start(options) с options[:target] = Pry.binding_for(binding). Почему Pry.start(binding) не демонстрирует одинаковое поведение (т.е. не запускает REPL), если target == binding в обоих случаях?Какие неверные предположения я делаю? Я понимаю, что Pry.start запускает REPL с self == main по умолчанию target == toplevel_binding.

1 Ответ

1 голос
/ 27 сентября 2019

Вызов 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 поверх объекта Binding.

Pry.start просто запускает Pry REPL.Если вы не передадите ему привязку, то это просто Pry REPL без контекста выполнения;консоль сообщает вам, имеет ли он контекст, изменив его с pry(main) (без контекста) на pry(#<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

Аналогично,вызов binding.pry из метода класса открывает Pry REPL в контексте выполнения этого класса (pry(ClassName)).Вызов binding.pry из метода экземпляра открывает Pry REPL в контексте выполнения этого instance (pry(#<ClassName>).

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

Надеюсь, чтопомогает.

...