If you don't know about Pry yet, you're missing out. It's a replacement REPL for Ruby with tons of features and many plugins, which makes it a perfect replacement for IRB, and as a result, for the standard Rails console.
A little while ago, the fine folks at Bugsnag wrote an article about their usage of Pry in production. I've been a Pry convert for quite some time as well, so today I'd like to share my own tips and tricks.
I think the Bugsnag article nails it pretty well, though I tweak some of the steps:
Use a dedicated machine: it's pretty easy for a console session to start eating up memory and resources, and you don't want that to disrupt your running application. The best machine is usually a job runner machine: all configured with the app, but not running any webserver.
Setup your Gemfile: instead of using pry-rails
, I prefer to use the pryrc
technique which I'll get back to in a minute. So my Gemfile
looks like this:
# Gemfile
gem 'pry', :require => false
gem 'pry-nav', :require => false
Use a shortcut: mine is called t1
. This is only for quick edits though, not for long running tasks. If you are going to leave a session running for a while, processing data, you definitely want to run it inside a screen
or tmux
session.
# ~/.bashrc
alias t1='ssh -t X.X.X.X -C "cd /data/APP/current && RAILS_ENV=production bundle exec pry"'
Since we are using a dedicated machine, we can have a shared pryrc
for everyone that offers some Rails goodies. We are not using pry-rails
, so we also need some of the code in ~/.pryrc
to do the Rails initialization:
# ~/.pryrc
# MISSING
The first benefit of using Pry is that you get an actual editor for your code. I tend to do a lot of support related tasks, and so I often need to run one off snippets of code. They are slightly more complicated than a one-liner, but at the same time quite straight forward to write and modify. So my usual workflow is this:
PRODUCTION [1] pry(main)> def find_ips; end
PRODUCTION [1] pry(main)> edit find_ips
Since Pry is configured with vim as the default editor, this drops me in vim where I can start writing my code:
require "set"
# Check all the IP addresses used by one user
def find_ips
list = Set.new
user = User.find 1234
user.comments.each {|comment| list << comment.ip}
list.each {|ip| puts ip}
end
The code is simple, but being able to work with it in a real editor is a big improvement. We can now run it, make adjustments, and iterate until we get it just right:
PRODUCTION [1] pry(main)> find_ips
X.X.X.X
X.X.X.X
X.X.X.X
PRODUCTION [1] pry(main)> edit find_ips
# do some changes
PRODUCTION [1] pry(main)> find_ips
X.X.X.X on Feb 1st, 2014 at 8:14:45 AM
X.X.X.X on Mar 2nd, 2014 at 10:01:32 PM
[...]
The other nice thing is that it makes it a lot easier to extract useful code. As I try to solve a particular problem, or extract data, I will often end up with a method 10 to 20 lines long that solves a particular support use case. If I want to save it for later, or integrate it into the main codebase, I can simply edit it again, and save it to a safe location from vim, or copy/paste it somewhere else. This would be a lot harder if all I had was a bunch of one-liners that only existed in the history of my session.
One of the most useful feature of Pry is it's ability to easily monkeypatch existing code. Let's say that we have a job that generates a CSV export of some reporting data. The export is great and all customers love it, but this one customer asked you to add another column to it. They have a somewhat narrow use case, and including the column for all customers would not necessarily be an improvement, so you decide to simply run the report once with this extra column, just for them. With Pry, this is super easy!
First, we take a look at the CSV export itself:
PRODUCTION [1] pry(main)> show-source Report#generate_csv
def generate_csv
[...]
documents.each {|doc| csv << doc.csv}
[...]
end
So we know that we need to modify the Document#csv
method. It's a simple method that returns an array. We are going to
patch the method: the modification will only exist within our Pry session and won't be written on disk. To do that, we use edit -p
instead of the standard edit
:
PRODUCTION [1] pry(main)> edit Document#csv -p
Add the extra data:
def csv
[ title,
content,
created_at,
updated_at,
custom_data ] # LINE ADDED
end
We can now generate the report for this customer, without affecting anyone else:
PRODUCTION [1] pry(main)> Report.find(1234).generate_csv
And we're done!
Pry is a perfect replacement for IRB, and can greatly improve your life when working on production applications. Just for the ability to edit code in vim, Pry is absolutely worth it. I definitely recommend spending some time installing it, configuring it, and getting it working on your application.
And if you've never used Pry before, take a look at this great talk: Ruby Conf 2013 - REPL driven development with Pry by Conrad Irwin. It will make you an instant convert.
That's it for today, cheers!