Ruby and Emacs Tip: Advanced Pry Integration
Tags: ruby, emacs, pry, elisp
Published: 2018-08-27 12:28:03

---
title: Ruby and Emacs Tip: Advanced Pry Integration
published: true
description: Seamlessly integrate Pry into Emacs
tags: ruby,emacs,pry,elisp
---

[Pry](https://github.com/pry/pry) is one of those tools that you'll despise until you actually _learn_ what it can do for you. It's a window to the Ruby language, a land where things have extreme late-binding and can change at any time.

With Pry, you can easily spelunk anything: edit, view, and navigate through source code; debug, play lines of code, and run shell commands. Doing the same with "puts debugging" or metaprogramming requires repetitive typing, tedious plumbing, third-party gems ([method_source](https://github.com/banister/method_source), anyone?) and won't give you syntax highlighting. You can run Ruby REPLs in [Emacs](https://www.gnu.org/software/emacs/) (including Pry) with [inf-ruby](https://github.com/nonsequitur/inf-ruby), a package co-authored by Yukihiro Matsumoto, Ruby's BDFL. But there's so much more power to unlock!

We will rely on Pry's [editor integration](https://github.com/pry/pry/wiki/Editor-integration), so the good news is that you can apply the same principles to your favorite text editor, as long as it supports a client-server architecture. [Neovim](https://github.com/neovim/neovim) is one such example. Emacs, however, has a special trick up its sleeve: elisp.

## Goals

This post will guide you through integrating Pry into Emacs and ironing out a few annoyances that might arise during the process. Also, it will give you an idea of how to create Pry commands that reach back into Emacs via _elisp_. Note that it won't teach you how to setup or use Emacs. Among others you will:

- Run Pry from within Emacs,
- Seamlessly integrate Pry into Emacs,
- Edit and reload files without leaving Pry (nor Emacs!),
- Page through Pry's output,
- View a Ruby file in Emacs through Pry,
- Open a gem in Dired through Pry.


## Emacs server 

With `emacsclient`, you can interact with a running Emacs session from the outside. For example:

```sh
$ emacsclient my_file.rb
```

The above command opens `my_file.rb` in your current Emacs session. Note that before attempting to use `emacsclient`, you'll need to start a server process with `M-x server-start` (press `M-x` in Emacs, type `server-start`, and press `return`).

I want things to be as automated as possible, so I created an _elisp_ function to start the server for me when I open Emacs:

```elisp
(defun run-server ()
  "Runs the Emacs server if it is not running"
  (require 'server)
  (unless (server-running-p)
    (server-start)))

(run-server)
```

The above code can be saved in your `~/.emacs.d/init.el` config file.

## Default editor configuration

The next step is to set `emacsclient` as your default editor. Save the following  lines in your shell config file (`.bash_profile` for bash or `.zshenv` for zsh):

```sh
export EDITOR=emacsclient
export VISUAL=$EDITOR
```

If you use a Mac computer and a GUI Emacs, Emacs won't inherit your shell environment variables. To overcome this issue, I recommend installing the `exec-path-from-shell` package. Be sure to add MELPA to your package archives:

```elisp
(setq package-archives
      '(("melpa" . "http://melpa.milkbox.net/packages/")
        ("gnu" . "http://elpa.gnu.org/packages/")))

(package-initialize)
```

Then run:

- `M-x package-refresh-contents` to refresh the packages,
- `M-x package-install`, `exec-path-from-shell`, and press `return` to install the package.

Finally, save this snippet in your `init.el` file:

```elisp
(defun copy-shell-environment-variables ()
  (when (memq window-system '(mac ns))
    (exec-path-from-shell-initialize)))

(copy-shell-environment-variables)
```

But there's a gotcha: I prefer `nvim` when I'm working on the terminal (which is rare these days because I run most of my shell commands within Emacs). For this reason, I set both editor variables via elisp and leave my shell variables unchanged:

```elisp
(setenv "VISUAL" "emacsclient")
(setenv "EDITOR" (getenv "VISUAL"))
```

## Trying it out

Run `M-x shell` (Emacs' shell), find an existing file, and run the following command:

```sh
$ $EDITOR existing-file
```

Wow, you are still in Emacs, and the file opened right before your eyes. Meanwhile, the Pry buffer is blocked with the message "_Waiting for Emacs..._". It's waiting for you to edit the file, save it, and press `C-x #` (`server-edit`) to terminate the connection, which closes the file and unblocks the shell. 

## Configuring Pry

You need a few settings to make Pry play nice with Emacs. Save these lines in your `~/.pryrc` file:

```ruby
if ENV['INSIDE_EMACS']
  Pry.config.correct_indent = false
  Pry.config.pager = false
end

Pry.config.editor = ENV['VISUAL']
```

- `Pry.config.correct_indent = false` fixes an annoying issue with the command prompt.
- `Pry.config.pager = false` tells Pry not to use `less` as a pager. Why? Because Pry runs under _comint.el_, a library that allows an ordinary Emacs buffer to communicate with an inferior shell process - in our case, a Pry REPL. It doesn't support interactive programs such as less, but on the other hand, the buffer itself is a pager.
- Finally, we tell Pry to use whatever is set to the `VISUAL` environment variable as its editor.

## Running Pry from within Emacs

Nothing special to do. Just ensure you've installed the `inf-ruby` package and that your project is configured to use Pry. `inf-ruby` provides many commands to spin up a Ruby REPL, like `inf-ruby-console-rails` for example. For Rails projects, you can install the `pry-rails` gem and call it a day. The `projectile-rails` package, which I thoroughly recommend, has a convenient shortcut for this: `C-c p r` (`projectile-rails-console`).

## Editing and reloading code

This is where things start to get fun. Let's suppose you're not getting an expected return value out of a certain gem. The name of the troublesome method is `MyGem.do_thing`, which is called by `MyApp.do_thing`. Let's edit `MyGem.do_thing`:

```ruby
[0] pry(main)> edit MyGem.do_thing
Waiting for Emacs...
```

And insert a debug statement:

```ruby
class MyGem
  def self.do_thing(a, b)
    binding.pry
    # ...
  end
end
```

Now save the file with `C-x C-s` and press `C-x #` to terminate the `emacsclient` connection. Boom! Pry will load your new changes and you'll be taken back to the Pry prompt! Finally, run `MyApp.do_thing`:

```ruby
[1] pry(main)> MyApp.do_thing(c)

From: /Users/thiago/.asdf/installs/ruby/2.4.2/lib/ruby/gems/2.4.0/gems/my_gem-5.1.3/lib/my_gem.rb @ line 48 MyGem#do_thing:

    47: def do_thing(a, b)
 => 48:   require 'pry'; binding.pry
```

Awesome, you've hit the breakpoint, so you might want to inspect the state of your application to figure out what's wrong. Now you can debug the world without leaving Emacs! Pry's [edit](https://github.com/pry/pry/wiki/Editor-integration#using-the-edit-command) command is extremely handy because you don't need to know where the gem or the method are stored on disk.

## Paging through source code

In Pry, you can read the source code of anything with the `show-source` command (or `$`). Let's take a look at `ActiveRecord::Base#establish_connection`:

```ruby
[6] pry(main)> show-source ActiveRecord::Base.establish_connection

From: /Users/thiago/.asdf/installs/ruby/2.4.2/lib/ruby/gems/2.4.0/gems/activerecord-5.1.3/lib/active_record/connection_handling.rb @ line 47:
Owner: ActiveRecord::ConnectionHandling
Visibility: public
Number of lines: 13

def establish_connection(config = nil)
  raise "Anonymous class is not allowed." unless name

  config ||= DEFAULT_ENV.call.to_sym
  spec_name = self == Base ? "primary" : name
  self.connection_specification_name = spec_name

  resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
  spec = resolver.resolve(config).symbolize_keys
  spec[:name] = spec_name

  connection_handler.establish_connection(spec)
end
```

This snippet fits into a single screen, but what to do when it doesn't? You can press `return` (`comint-send-input`) to run `show-source` and either:

- Scroll back up with `M-v` until reaching the start of the output, then `C-v` to scroll down, or;
- Run `C-c C-r` (`comint-show-output`) to make the cursor jump to the start of the output, then `C-v` to scroll down. 

The second option is a clear winner. However, I've gotten tired of always running `comint-show-output`, so I came up with my own automation. I've created the following function and mapped it to `<C-return>`:

```elisp
;; Save this code in init.el
(defun comint-send-input-stay-on-line ()
  (interactive)
  (call-interactively 'comint-send-input)
  (run-with-timer 0.05
                  nil
                  (lambda ()  (call-interactively 'comint-show-output))))

(define-key comint-mode-map (kbd "<C-return>") 'comint-send-input-stay-on-line)
```

It runs `comint-show-output` right after `comint-send-input`. There's an interval of 0.05 seconds between the commands to avoid a race condition, time enough for the output to be available before you can actually return to it.

The cool thing is that this shortcut works with any comint prompt, even `M-x shell`. I'm not sure if there's an easier solution to this problem, so if you know of any please leave it up in the comments :)

## Viewing a file through Pry

`show-source` is cool but it's not enough. Often times I want to open the corresponding Ruby file in another buffer, where I have `enh-ruby-mode` and a bunch of other tools at my disposal. The default `edit` command is a no-go because it blocks the Pry prompt and waits for an edit to be made. Also, the buffer usually gets closed in the end.

Luckily, we can use `edit -n` to bypass the blocking behavior. For example:

```ruby
[7] pry(main)> edit -n Foo.bar
```

The above command will open the file for the `Foo` module with the cursor pointed at the `bar` class method.

## Open a gem in Dired through Pry

Opening a gem is something I need to do surprisingly often. Sometimes I want to look into the files or just search within the gem's source code. And I'll be happy if I can avoid a trip to GitHub, which also requires going through the trouble of pointing the gem at the specific version that my app is using. To showcase what a custom command looks like, I also don't want to use the `bundle open` command.

Here's a supposed Pry command to open a gem in Dired:

```ruby
# `ggem` is a mnemonic to "go to gem"
[8] pry(main)> ggem graphql-batch
```

> Note that Pry commands have a special syntax. They are not calls to Ruby methods.


Let's apply divide and conquer to make it work. First, we need a way to open a directory in Emacs. Go to a terminal and type in the following command:

```sh
$ emacsclient -e '(dired-jump nil "~")'
```

Flip back to Emacs, and you will see a Dired window with the cursor positioned at your home directory. Awesome! The `-e` flag to `emacsclient` allows us to run an arbitrary string of _elisp_ code, so we've run the `dired-jump` function and passed our home directory (`~`) as the second argument.

Now let's create a Ruby method to wrap this call. Save it in `~/.pryrc`:

```ruby
# The function accepts an arbitrary path
def emacs_open_in_dired(path)
  system %{emacsclient -e '(dired-jump nil "#{path}")'}
end
```

Next, we need to find the gem's directory. In the above example, the gem is `graphql-batch`. Here's a method that will return the directory as a `Pathname` object:

```ruby
def gem_dir(gem_name)
  gem_dir = Gem::Specification.find_by_name(gem_name).gem_dir 
  Pathname(gem_dir)
end
```

Now that we've gathered all the pieces, let's create the Pry command:

```ruby
# This is a shorthand to create and add a command at the same time
Pry::Commands.create_command 'ggem' do
  description 'Open a gem dir in Emacs dired'
  banner <<-'BANNER'
  Usage: ggem GEM_NAME

  Example: ggem propono
  BANNER

  def process
    gem_name = args.join('')

    # Opens the gem's lib folder in Dired
    emacs_open_in_dired gem_dir(gem_name) / 'lib'
  rescue Gem::MissingSpecError
    # do nothing
  end
end
```

Great, the command should now be working! You can use it with any gem, as long as it's declared in your Gemfile!

## Conclusion

I hope this post was useful to you. There's so much you can do with these tools that I haven't even scratched the surface! Emacs is a great editor, and the combination of Ruby and elisp gives you almost endless possibilities. With `inf-ruby` you can also do cool things such as sending Ruby code from a buffer to the REPL process, so I encourage you to explore it in detail!

If Pry isn't already the _heart_ of your Ruby workflow, I recommend you make the leap. If you work with Ruby, it's a life-changing tool. In the next post, I will show you how to run RSpec tests productively.