If you are a Ruby developer, you likely use binding.pry frequently for debugging Rails and Ruby projects. But do you know what pry and binding actually are? In this post, I will walk you through binding and pry, and demonstrate why pry is such a powerful tool for debugging.

What is Binding and how does binding.pry work?

Read the blog post about binding in Ruby here.

What is Pry?

Pry is a powerful REPL (Read-Evaluate-Print Loop) for the Ruby language. It is significantly more capable than the standard IRB. Pry is an invaluable tool for debugging and exploring your codebase.

Using Binding with Pry

Calling binding.pry is essentially “prying” into the current binding or context of the code from your terminal. When you place binding.pry in your code, the execution context is captured and a REPL session is opened at that exact line during runtime.

Interestingly, pry can be used on any Ruby object, not just a binding object.

Let’s try using pry with a string object (remember, everything is an object in Ruby):

2.6.3 :001 > require 'pry'
 => true
2.6.3 :002 > "sathia".pry
[1] pry("sathia")> upcase
=> "SATHIA"
[2] pry("sathia")>

In the example above, I am calling pry on a String object. Inside the Pry console, I call upcase. This works because I am now operating within the context of that specific String instance.

Pry offers many other features beyond simple debugging:

Source Code Browsing

You can inspect the source code of any class or method directly from the console:

[3] pry("sathia")> show-source Array

From: /Users/satyanarayan/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/pp.rb @ line 329:
Class name: Array
Number of monkeypatches: 2. Use the `-a` option to display all available monkeypatches
Number of lines: 13

class Array # :nodoc:
  def pretty_print(q) # :nodoc:
    q.group(1, '[', ']') {
      q.seplist(self) {|v|
        q.pp v
      }
    }
  end

  def pretty_print_cycle(q) # :nodoc:
    q.text(empty? ? '[]' : '[...]')
  end
end

[4] pry("sathia")>

Documentation Browsing

You don’t have to visit external Ruby documentation; you can browse it directly in your console.

Ensure the pry-doc gem is installed on your system. You can install it directly from the Pry console with the following command:

gem-install pry-doc

[11] pry(Array):1> show-doc join

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: join(*arg1)
Number of lines: 12

Returns a string created by converting each element of the array to
a string, separated by the given separator.
If the separator is nil, it uses current $,.
If both the separator and $, are nil,
it uses an empty string.

   [ "a", "b", "c" ].join        #=> "abc"
   [ "a", "b", "c" ].join("-")   #=> "a-b-c"

For nested arrays, join is applied recursively:

   [ "a", [1, 2, [:x, :y]], "b" ].join("-")   #=> "a-1-2-x-y-b"

Pry provides commands like cd and ls to navigate objects and list their methods and variables.

2.6.3 :004 > pry
[1] pry(main)> cd Array
[2] pry(Array):1> ls
Object.methods: yaml_tag
Array.methods: []  try_convert
Array#methods:
  &    []      bsearch        compact!   difference  eql?        flatten   join     min          prepend             reject!               rindex     shift     sort!       to_h       values_at
  *    []=     bsearch_index  concat     dig         fetch       flatten!  keep_if  none?        pretty_print        repeated_combination  rotate     shuffle   sort_by!    to_s       zip
  +    all?    clear          count      drop        fill        hash      last     one?         pretty_print_cycle  repeated_permutation  rotate!    shuffle!  sum         transpose  |
  -    any?    collect        cycle      drop_while  filter      include?  length   pack         product             replace               sample     size      take        union
  <<   append  collect!       delete     each        filter!     index     map      permutation  push                reverse               select     slice     take_while  uniq
  <=>  assoc   combination    delete_at  each_index  find_index  insert    map!     place        rassoc              reverse!              select!    slice!    to_a        uniq!
  ==   at      compact        delete_if  empty?      first       inspect   max      pop          reject              reverse_each          shelljoin  sort      to_ary      unshift
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_

To list all instance variables, use ls -i. To see all available options for ls, use ls -h.

[10] pry(Array):1> ls -h
Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
       ls [-g] [-l]

ls shows you which methods, constants and variables are accessible to Pry. By
default it shows you the local variables defined in the current shell, and any
public methods or instance variables defined on the current object.
...

Command History

You can list the history of commands used in your current session:

[3] pry(Array):1> hist
 1: upcase
 2: show-source String
 3: show-source Array
 4: show-doc Array
 5: cd Array
 6: show-doc
 7: cd Array
 8: show-doc join
 9: gem-install pry-doc
10: cd Array
11: show-doc join
12: cd Array
13: ls
[4] pry(Array):1>

You can also use grep, tail, head, and replay with the hist command.

For example:

[4] pry(Array):1> hist --tail 3
12: cd Array
13: ls
14: hist
[5] pry(Array):1> hist --grep join
 8: show-doc join
11: show-doc join
[6] pry(Array):1> hist --replay 8
...

Accessing the Shell

You can open a Linux shell directly from the Pry prompt:

2.5.0 :008 > pry
[1] pry(main)> shell-mode
pry main:/Users/satyanarayan/Documents $ .ls
Books		complaint	friends		pdfs		videos
pry main:/Users/satyanarayan/Documents $

This allows you to navigate the file system and perform other terminal tasks without leaving your debugging session.

Rails-Specific Features

If you use the pry-rails gem instead of the standard pry, you gain access to powerful commands like show-routes and show-models.

# In your Gemfile
gem 'pry-rails', group: :development
  events git:(master)  rails c
[1] pry(main)> show-routes
                   Prefix Verb URI Pattern                                                                              Controller#Action
       rails_service_blob GET  /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
...
[2] pry(main)> show-models
ContactDetail
  id: integer
  contactable_type: string
  contactable_id: integer
  user_id: integer
  created_at: datetime
  updated_at: datetime
  belongs_to :contactable
  belongs_to :user