可以通过命令启动 binding.pry 会话吗?

It's possible to start a binding.pry session passing a command?

我使用 pry-byebug 在 RSpec 上添加了一个别名,就像这样:

# frozen_string_literal: true

RSpec.configure do |rspec|
  rspec.alias_example_group_to :pcontext, pry: true
  rspec.alias_example_group_to :pdescribe, pry: true
  rspec.alias_example_to :pit, pry: true

  rspec.before(:example, pry: true) do |_|
    # rubocop:disable Lint/Debugger
    binding.pry
    # rubocop:enable Lint/Debugger
  end
end

我希望在使用这个别名时,例如在这行代码中:

pit { expect(published_post.published?).to be_truthy }

要在测试线上开始这样的会话:

...
=> 16:     pit { expect(published_post.published?).to be_truthy }

但此刻它在别名内开始:

From: /opt/hobbyonrails/spec/support/alias.rb:12 :

       7: 
       8:   rspec.before(:example, pry: true) do |_|
       9:     # rubocop:disable Lint/Debugger
      10:     binding.pry
      11:     # rubocop:enable Lint/Debugger
 => 12:   end
    13: end

要到达我想去的地方,我需要 运行 这个命令:step 25。所以,我想知道是否有办法在会话开始后立即自动执行此命令 运行,但我找不到答案。

你能给我一些实现这个的技巧吗?

使用 Pry before_session 钩子,如 the Pry wiki article on hooks 中所述。

binding.pry 被调用时,在将您放入 REPL 之前执行 before_session 挂钩。每次您调用 binding.pry 调用 whereami --quiet 时,Pry 都会使用 before_session 挂钩,就像您在 post:

中所做的那样
From: /opt/hobbyonrails/spec/support/alias.rb:12 :

       7: 
       8:   rspec.before(:example, pry: true) do |_|
       9:     # rubocop:disable Lint/Debugger
      10:     binding.pry
      11:     # rubocop:enable Lint/Debugger
 => 12:   end
    13: end

使用 before_session 钩子来解决您的问题的想法是告诉 Pry,当它被调用时,它应该自动调用 step 25。 (或者你需要多少步骤)

它变得有点棘手,因为来自 pry-byebug 的 step 命令实际上调​​用了一个新的 Pry 会话。如果您添加一个调用 stepbefore_session 挂钩,然后 step 调用一个新会话,您将陷入一个循环,该循环逐步执行直到程序完成执行。

另一个问题是,在添加钩子后执行的代码中对 binding.pry 的任何调用都会再次调用 step,您可能不希望它 step 每次,只在一个特定的地方。

解决这个问题的方法是添加一个在执行时自行删除的挂钩:

Pry.hooks.add_hook(:before_session, 'step') do |output, binding, pry|
  Pry.hooks.delete_hook(:before_session, 'step')
  pry.run_command('step 1')
end

现在的流程应该是:

  1. 添加了上面的钩子
  2. binding.pry 被调用
  3. 执行上面的钩子,移除钩子和运行 step 1
  4. step 调用一个新的 Pry 会话
  5. 上面的钩子不再存在,也没有执行
  6. REPL 打开
  7. 任何进一步的 binding.pry 调用都不会执行挂钩

这是一个示例应用程序,展示了它是如何工作的:

require 'pry'
require 'pry-byebug'

Pry.hooks.add_hook(:before_session, 'step') do |output, binding, pry|
  Pry.hooks.delete_hook(:before_session, 'step')
  pry.run_command('step 1')
end

binding.pry
puts 'Pry will run `whereami` and point at this line with => first'
puts 'But when the hook runs it will step to and point at this line with =>'
puts 'And it will stop before it reaches this line'

binding.pry
puts 'And after this second binding, there will be no hook and thus no step'
puts 'And it will stop before it reaches this line'

这是它的输出,证明它按描述工作:

$ ruby foo.rb

From: /Users/foo/foo.rb:10 :

     5:   Pry.hooks.delete_hook(:before_session, 'step')
     6:   pry.run_command('step 1')
     7: end
     8:
     9: binding.pry
 => 10: puts 'Pry will run `whereami` and point at this line with => first'
    11: puts 'But when the hook runs it will step to and point at this line with =>'
    12: puts 'And it will stop before it reaches this line'
    13:
    14: binding.pry
    15: puts 'And after this second binding, there will be no hook and thus no step'

Pry will run `whereami` and point at this line with => first

From: /Users/foo/foo.rb:11 :

     6:   pry.run_command('step 1')
     7: end
     8:
     9: binding.pry
    10: puts 'Pry will run `whereami` and point at this line with => first'
 => 11: puts 'But when the hook runs it will step to and point at this line with =>'
    12: puts 'And it will stop before it reaches this line'
    13:
    14: binding.pry
    15: puts 'And after this second binding, there will be no hook and thus no step'
    16: puts 'And it will stop before it reaches this line'

foo|(main):1 ⇒ exit
But when the hook runs it will step to and point at this line with =>
And it will stop before it reaches this line

From: /Users/foo/foo.rb:15 :

    10: puts 'Pry will run `whereami` and point at this line with => first'
    11: puts 'But when the hook runs it will step to and point at this line with =>'
    12: puts 'And it will stop before it reaches this line'
    13:
    14: binding.pry
 => 15: puts 'And after this second binding, there will be no hook and thus no step'
    16: puts 'And it will stop before it reaches this line'

foo|(main):1 ⇒ 

将其集成到您的应用中应该非常简单:

rspec.before(:example, pry: true) do |_|
  # rubocop:disable Lint/Debugger
  # You should find a good way to set this value as it seems likely to change
  steps = 25

  Pry.hooks.add_hook(:before_session, 'step') do |output, binding, pry|
    Pry.hooks.delete_hook(:before_session, 'step')
    pry.run_command("step #{steps}")
  end

  binding.pry
  # rubocop:enable Lint/Debugger
end