Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have added an alias on RSpec using pry-byebug, just like this:

# 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

I would like that when using this alias, for example in this line of code:

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

For the session to start on the test line like this:

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

But at moment it start inside the alias:

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

To go to where I want I need to run this command: step 25. So, I was wondering if there is a way to have this command run automatically right after the session starts, but I can't find the answer.

Can you give me some tips to achieve this, please?

like image 440
Pedro Paiva Avatar asked Mar 20 '26 18:03

Pedro Paiva


1 Answers

Use a Pry before_session hook as described in the Pry wiki article on hooks.

A before_session hook is executed before dropping you into the REPL when binding.pry is called. Pry uses a before_session hook every time you invoke binding.pry to call whereami --quiet, like you have in your 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

The idea of using a before_session hook to solve your problem is to tell Pry that when it is invoked it should automatically call step 25. (or however many steps you need)

It gets a little bit tricky though because the step command from pry-byebug actually invokes a new Pry session. If you add a before_session hook that calls step, and step then invokes a new session, you'll get caught in a loop that steps through until the program finishes executing.

Another issue is that any invocation of binding.pry in your code that is executed after the hook is added will invoke step again, and you probably don't want it to step every time, just in one particular spot.

The fix for this is to add a hook that removes itself when executed:

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

Now the flow should be:

  1. The hook above is added
  2. binding.pry is invoked
  3. The hook above is executed, removing the hook and running step 1
  4. step invokes a new Pry session
  5. The hook above is no longer present and is not executed
  6. The REPL opens
  7. Any further binding.pry invocations do not execute the hook

Here is an example app to show how this would work:

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'

And here's its output demonstrating that it works as described:

$ 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 ⇒ 

Integrating this into your app should be pretty straightforward:

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
like image 115
anothermh Avatar answered Mar 23 '26 06:03

anothermh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!