【问题标题】:It's possible to start a binding.pry session passing a command?可以通过命令启动 binding.pry 会话吗?
【发布时间】:2020-09-19 13:37:15
【问题描述】:

我已经使用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。所以,我想知道是否有办法让这个命令在会话开始后立即自动运行,但我找不到答案。

请给我一些提示来实现这一点?

【问题讨论】:

    标签: ruby-on-rails ruby rspec rspec-rails


    【解决方案1】:

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

    before_session 钩子在调用 binding.pry 时将您放入 REPL 之前执行。每次您调用 binding.pry 来调用 whereami --quiet 时,Pry 都会使用 before_session 挂钩,就像您在帖子中所说的那样:

    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
    

    【讨论】:

    • 关于 steps 变量,我认为使它们更准确的唯一方法是在仅跳转到项目文件而不是 gem 文件的 pry 中添加一个命令。这样,步骤 25 将更改为这个新命令,并且只需要一个“步骤”。我将在不久的将来尝试做出这一贡献,并且有了这个和你的回答,我认为这个别名会很棒。谢谢!
    猜你喜欢
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 2013-12-12
    • 2016-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多