Easier Debugging With Pry

This article will show you how to make debugging easier with Pry


Published on:November 10, 2016

Introduction

In this article we will show you how to debug your Rails applications using Pry. Pry is an excellent tool for debugging both Ruby and Rails applications that features powerful commands, syntax highlighting, and much more. Let's get started.

Installation

There are actually 2 gems to consider when installing pry. If you developing plain old Ruby apps or building on something like Sinatra, you'll want to use the pry gem. If you are building a Rails application, pry-rails will work better for you. First, we will show you how to install the pry-rails gem, then we will have a short section on the pry gem.

Installing pry-rails

The pry-rails gem offers up a number of extra capabilities. First, Pry is automatically enabled whenever you launch a rails console. Second, pry-rails provides a number of extra pieces of functionality, which we will cover in more detail below. To install the pry-rails gem, open up your Gemfile and add in the line listed below.

Gemfile:

gem 'pry-rails', group: [:development, :test]

You'll note that we only include Pry for the development and test environments. This is because we don't want Pry running in the production environment since it can cause issues.

Now run a bundle install to install the gem.

Terminal Commands:

bundle install

Now that Pry is installed for your Rails app, you can skip the next section since it primarily deals with non Rails applications.

Installing Pry For Non-Rails Applications

To install Pry when you aren't using Rails, you'll need to install the pry gem. This can be accomplished with the following command:

Terminal Commands:

gem install pry

Once the Pry gem is installed, you can launch directly into the Pry environment by using the pry console command.

Terminal Commands:

pry

You can include pry in your script with require 'pry':

example_script.rb:

require 'pry'

puts 'requiring pry'
binding.pry

The binding.pry command above initiates the pry console. You can also simply run your script with the pry command and skip the require 'pry' statement.

Basic Usage

Pry lets you instantly stop the execution of your app at any point, and debug your app within the scope of where you stopped. To stop execution you simple add a line that says:


binding.pry

You will then be dropped to a Pry prompt. For example:


From: /home/richard/Documents/eCommerceExample/spec/controllers/categories_controller_spec.rb @ line 21 :

    16:     end
    17:
    18:     include_examples 'sets the proper global variables'
    19:
    20:     it "returns http success" do
 => 21:       binding.pry
    22:       expect(response).to have_http_status(:success)
    23:     end
    24:
    25:     it 'returns a list of categories' do
    26:       expect(assigns(:categories).size).to eq(1)

[1] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)>

In the example above, we have inserted the binding.pry function at line 21 in the spec/controllers/categories_controller_spec.rb test featured in our eCommerce Site From Scratch Part 5: Images, Authentication, And More! article. We can then perform a number of actions. For instance, we can examine variables:


[4] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> response.headers
=> {"X-Frame-Options"=>"SAMEORIGIN",
 "X-XSS-Protection"=>"1; mode=block",
 "X-Content-Type-Options"=>"nosniff",
 "Content-Type"=>"text/html; charset=utf-8"}
[5] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)>

In the example above, we examined the headers of the response we got back from performing a GET show on our categories controller. You can examine any variable in your scope, for example, I can look at the instance variable @categories that is returned and check whether it is what I expected it to be:


[5] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> assigns(:categories)
=> [#<Category:0x000000055d13a8
  id: 1,
  slug: nil,
  name: "Beauty, Movies & Industrial",
  deleted_at: nil,
  created_at: Thu, 03 Nov 2016 00:46:31 UTC +00:00,
  updated_at: Thu, 03 Nov 2016 00:46:31 UTC +00:00>]
[6] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)>

Note that you can exit pry at any time by using the exit-all command. You can also exit completely out of Ruby with the exit! command. Keep this in mind as we move forward.


[12] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> exit-all

Moving on, we can change scope, for example:


[40] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> cd Category
[41] pry(Category):1> ls --m --grep ordered
Category.methods: ordered
[42] pry(Category):1>

In the code above, we changed scope to the category model, we then performed an ls --m --grep ordered to find the ordered method. The --m parameter stands for method. The --grep parameter filters our results to the specified string. We can then show the method using show-method:


[42] pry(Category):1> show-method ordered

From: /home/richard/Documents/eCommerceExample/app/models/concerns/ordered.rb @ line 3:
Owner: #<Class:Category(id: integer, slug: string, name: string, deleted_at: datetime, created_at: datetime, updated_at: datetime)>
Visibility: public
Number of lines: 3

def self.ordered
  where(deleted_at: nil).order(name: :asc)
end
[43] pry(Category):1>

In the above code you'll see that we listed the source to the .ordered method. You can also temporarily change the method. For example:


[43] pry(Category):1> def self.ordered
[43] pry(Category):1*   puts 'not implemented'
[43] pry(Category):1* end
=> :ordered
[44] pry(Category):1> cd ..
[45] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> Category.ordered
not implemented
=> nil

Here, we changed the .ordered method to print 'not implemented'. We then switched back to the parent scope and called .ordered, which printed 'not implemented'. Note that you don't have to switch to the parent scope, you could have just as easily called .ordered directly:


[47] pry(Category):1> ordered
not implemented
=> nil

You can also use an editor to make more complex changes, first set the default editor you wish to use:


[2] pry(main)> Pry.config.editor = 'vim'
=> "vim"

Now we can use the edit method to edit the code in our favorite editor:


6] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> cd Category
[8] pry(Category):1> edit ordered

The vim editor will now open, allowing you to more easily edit the method.

If you are deep within your program, you can use nesting and jump-to easily get back out. For example:


[52] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> cd Category/ordered
not implemented
[53] pry(nil):2> nesting
Nesting status:
--
0. #<RSpec::ExampleGroups::CategoriesController::GETShow> (Pry top level)
1. Category
2. nil
[54] pry(nil):2> jump-to 0
[56] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)>

In the example above, we switched scope to Category#ordered, then we used jump-to to switch back to the parent scope.

If an error occurred, you can also show the most recent stacktrace with wtf?:


[17] pry(Category):1> raise 'foobar'
RuntimeError: foobar
from (pry):13:in `__binding__'
[18] pry(Category):1> wtf?
Exception: RuntimeError: foobar
--
0: (pry):13:in `__binding__'
1: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `eval'
2: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `evaluate_ruby'
3: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:323:in `handle_line'
4: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'
5: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `catch'
6: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `block in eval'
7: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `catch'
8: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `eval'
9: /home/richard/.rvm/gems/ruby-2.3.1/gems/pry-0.10.4/lib/pry/repl.rb:77:in `block in repl'
[19] pry(Category):1>

You can enter shell mode with shell-mode. For example:


[20] pry(Category):1> shell-mode
pry Category:/home/richard/Documents/eCommerceExample $

From there you can run shell commands by prefixing the command with a ., for example:


pry Category:/home/richard/Documents/eCommerceExample $ .ls
app  bin  config  config.ru  db  Gemfile  Gemfile.lock  lib  log  public  Rakefile  README.md  spec  test  tmp  vendor
pry Category:home/richard/Documents/eCommerceExample $ .cat config.ru
# This file is used by Rack-based servers to start the application.

require_relative 'config/environment'

run Rails.application
pry Category:/home/richard/Documents/eCommerceExample $

Rails Specific Methods

You can show a list of your models with show-models, for example:


[2] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> show-models
ActiveRecord::SchemaMigration
  version: string
ApplicationRecord
  Table doesn't exist
Category
  id: integer
  slug: string
  name: string
  deleted_at: datetime
  created_at: datetime
  updated_at: datetime
  has_many :products
Product
  id: integer
  category_id: integer
  slug: string
  name: string
  brand: string
  description: text
  price: decimal
  deleted_at: datetime
  created_at: datetime
  updated_at: datetime
  image_file_name: string
  image_content_type: string
  image_file_size: integer
  image_updated_at: datetime
  belongs_to :category
User
  id: integer
  email: string
  encrypted_password: string
  reset_password_token: string
  reset_password_sent_at: datetime
  remember_created_at: datetime
  sign_in_count: integer
  current_sign_in_at: datetime
  last_sign_in_at: datetime
  current_sign_in_ip: string
  last_sign_in_ip: string
  created_at: datetime
  updated_at: datetime

You can also show an individual model with show-model, for example:


[3] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> show-model User
User
  id: integer
  email: string
  encrypted_password: string
  reset_password_token: string
  reset_password_sent_at: datetime
  remember_created_at: datetime
  sign_in_count: integer
  current_sign_in_at: datetime
  last_sign_in_at: datetime
  current_sign_in_ip: string
  last_sign_in_ip: string
  created_at: datetime
  updated_at: datetime
[4] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)>

You can show the rails routes with show-routes, for example:


[5] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> show-routes
                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
                    root GET    /                              home#show
              categories GET    /categories(.:format)          categories#index
                category GET    /categories/:id(.:format)      categories#show
                 product GET    /products/:id(.:format)        products#show
          search_results GET    /search_results(.:format)      search_results#index
[6] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)>

You can find the routes for a given controller with find-route, for example:


[9] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> find-route CategoriesController
Routes for CategoriesController
--
index GET /categories(.:format)  [categories]
show GET /categories/:id(.:format)  [category]

You can also find a controller and method based on a specified path using recognize-path, for example:


[10] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> recognize-path /
{:controller=>"home", :action=>"show"}

To get help, simply type help at the Pry prompt. The full list of commands for Pry 0.10.4 is listed below.


[11] pry(#<RSpec::ExampleGroups::CategoriesController::GETShow>)> help
Help
  help               Show a list of commands or information about a specific command.

Context
  cd                 Move into a new context (object or scope).
  find-method        Recursively search for a method within a class/module or the current namespace.
  ls                 Show the list of vars and methods in the current scope.
  pry-backtrace      Show the backtrace for the pry session.
  raise-up           Raise an exception out of the current pry instance.
  reset              Reset the repl to a clean state.
  watch              Watch the value of an expression and print a notification whenever it changes.
  whereami           Show code surrounding the current context.
  wtf?               Show the backtrace of the most recent exception.

Editing
  /^\s*!\s*$/        Clear the input buffer.
  amend-line         Amend a line of input in multi-line mode.
  edit               Invoke the default editor on a file.
  hist               Show and replay readline history.
  play               Playback a string variable, method, line, or file as input.
  show-input         Show the contents of the input buffer for the current multi-line expression.

Introspection
  ri                 View ri documentation.
  show-doc           Show the documentation for a method or class.
  show-source        Show the source for a method or class.
  stat               View method information and set _file_ and _dir_ locals.

Gems
  gem-cd             Change working directory to specified gem's directory.
  gem-install        Install a gem and refresh the gem cache.
  gem-list           List and search installed gems.
  gem-open           Opens the working directory of the gem in your editor.

Commands
  import-set         Import a pry command set.
  install-command    Install a disabled command.

Aliases
  !!!                Alias for `exit-program`
  !!@                Alias for `exit-all`
  $                  Alias for `show-source`
  ?                  Alias for `show-doc`
  @                  Alias for `whereami`
  clipit             Alias for `gist --clip`
  file-mode          Alias for `shell-mode`
  find-routes        Alias for `find-route`
  history            Alias for `hist`
  quit               Alias for `exit`
  quit-program       Alias for `exit-program`
  reload-method      Alias for `reload-code`
  show-method        Alias for `show-source`

Input and output
  .<shell command>   All text following a '.' is forwarded to the shell.
  cat                Show code from a file, pry's input buffer, or the last exception.
  change-inspector   Change the current inspector proc.
  change-prompt      Change the current prompt.
  fix-indent         Correct the indentation for contents of the input buffer
  list-inspectors    List the inspector procs available for use.
  list-prompts       List the prompts available for use.
  save-file          Export to a file using content from the repl.
  shell-mode         Toggle shell mode. bring in pwd prompt and file completion.

Misc
  gist               Upload code, docs, history to https://gist.github.com/.
  pry-version        Show pry version.
  reload-code        Reload the source file that contains the specified code object.
  toggle-color       Toggle syntax highlighting.

Navigating pry
  !pry               Start a pry session on current self.
  disable-pry        Stops all future calls to pry and exits the current session.
  exit               Pop the previous binding.
  exit-all           End the current pry session.
  exit-program       End the current program.
  jump-to            Jump to a binding further up the stack.
  nesting            Show nesting information.
  switch-to          Start a new subsession on a binding in the current stack.

Rails
  find-route         See which urls match a given controller.
  recognize-path     See which route matches a url.
  show-middleware    Show all middleware (that rails knows about).
  show-model         Show the given model.
  show-models        Show all models.
  show-routes        Show all routes in match order.

Prompts
  simple-prompt      Toggle the simple prompt.

This is just scratching the surface of what Pry can do. Pry also features a robust plugin system and there are a number of plugins you can use. I hope that you enjoyed this brief introduction to Pry. If you have any questions or comments, please feel free to leave a comment below. Thanks for reading!

Resources