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.
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.
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:
gem install pry
Once the Pry gem is installed, you can launch directly into the Pry environment by using the pry
console command.
pry
You can include pry in your script with require 'pry'
:
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!