Horace Williams

Getting Started with Emacs for Ruby

Lately I’ve been gradually drawing a few students at Turing into the Emacs fold. Along the way I’ve been thinking about what is needed for a minimal but sufficient Emacs setup for working with Ruby code.

In this post I’ll try to document some of the tools and configuration I’ve found useful, with an aim toward keeping things accessible for a newcomer to Emacs. The key features I’ll focus on include:

  • Modernizing the Emacs UI using some sane defaults
  • Setting up basic Ruby syntax highlighting and syntax completion
  • Interactively evaluating code in Ruby buffers using Seeing Is Believing
  • Running a Ruby REPL from within emacs using inf-ruby-mode
  • Running Ruby tests from within emacs using ruby-test-mode

For now I won’t be digging into any Rails-specific features – hopefully I can cover this in another post, but this guide is focused on plain old Ruby.

Emacs Configuration 101

The Emacs configuration journey starts with a special directory on your machine located at ~/.emacs.d. When Emacs starts up, it will by default look for a file called init.el in this directory and use that to load any user-specific configuration you want to provide. (This is similar to how your shell loads any user configuration files at ~/.profile or ~/.bashrc, etc)

To follow along, go ahead and create this directory and file:

mkdir ~/.emacs.d
touch ~/.emacs.d/init.el

If your machine already has an Emacs configuration in this directory and you want to start from scratch, it’s safe to stash the existing directory by moving it:

mv ~/.emacs.d ~/.emacs.d.old

Your original config will be waiting for you should you decide to return to it.

Baby’s First Emacs Lisp

An astute reader will have noticed the extension of the init file we just created: .el. This stands for Emacs Lisp, the language in which Emacs is written and scripted. It turns out Emacs is really a big old Lisp interpreter that happens to have some neat features for manipulating text buffers tucked away in the corners. So are you saying we’ll be writing… Lisp? You bet your Free Software Foundation commemorative mousepad we are!

When Emacs boots, it will process our init.el as Emacs Lisp, so within this file we’ll be writing Elisp expressions to customize how the editor behaves.

Emacs: Out of the 1990’s

While there’s a certain endearing homeliness to it, the first impression of a fresh Emacs install is fairly dated:

/public/images/emacs_splash_screen.png

The default Emacs configuration includes some clunky things like menu bars and a loud “splash” screen. BetterDefaults is a popular Emacs package for improving this situation without getting too overboard with magical customizations.

Let’s start by pulling BetterDefaults into our new config. This will also give us a good opportunity to talk about…

Emacs Packages

A “package” is the standard unit for distributing a chunk of Emacs code. There are several free online package repositories out there, the most popular being GNU ELPA, ELPA, Marmalade, and Melpa.

Unfortunately, Emacs doesn’t come with a built-in “manifest”-driven solution for defining what packages to use (like we might encounter with a Gemfile or package.json in a Ruby or Node project).

Fortunately it’s pretty easy to add this functionality, so go ahead and add this Elisp to your init.el:

; list the repositories containing them
(setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
                         ("melpa" . "https://melpa.org/packages/")))

; activate all the packages (in particular autoloads)
(package-initialize)

; fetch the list of packages available
(unless package-archive-contents
  (package-refresh-contents))

; list the packages you want
(setq package-list '(better-defaults))

; install the missing packages
(dolist (package package-list)
  (unless (package-installed-p package)
    (package-install package)))

(require 'better-defaults)

This first Elisp snippet defines a list of archives to use when searching for packages and then initializes the package system.

Next we define a list of packages we want to install (so far just better-defaults). Then we check to see if any of them are missing and, if so, install them. From now on, we can simply add packages to our package-list and they should be automatically installed when we start Emacs – similar to adding a new gem to your Gemfile and running bundle.

The require expression on the last line loads the better-defaults package that we just installed, so you should see it taking effect.

Save this file and restart Emacs if it’s already running (remember: C-x C-c to quit), and you should see emacs pause briefly as it installs the Better Defaults package. You should also see a slightly cleaner interface now that the toolbars are removed.

A Bit More UI:

Next let’s get rid of the default Emacs splash screen and have it put us into an empty buffer instead. The initial-major-mode setting can take any of Emacs’ major modes – a lot of people use Org or Markdown mode for their scratch buffer, but since we’re targeting a Ruby setup here, we’ll use that.

(setq inhibit-splash-screen t
      initial-scratch-message nil
      initial-major-mode 'ruby-mode)

Additionally, it would be nice if it didn’t look so glaringly white. Let’s update our package list to pull in the ever-gentle-on-the-eyes Solarized Theme and load it in our init file:

(setq package-list '(better-defaults solarized-theme))

;....

(load-theme 'solarized-dark t)

Finally, we add a few more bits of snazz like showing line numbers and setting a default font (SourceCodePro is a free, open source monospaced font for editing code from Adobe.)

;; Show line numbers
(global-linum-mode)

;; Typography
(set-face-attribute 'default nil
                    :family "Source Code Pro"
                    :height 150
                    :weight 'normal
                    :width 'normal)

OS X Conveniences

I generally recommend people start out using Emacs in GUI mode (as opposed to the text-based terminal interface). Personally I still use this mode most often even a few years into using Emacs, but when starting out it’s especially helpful to have some of the familiar OS X keybindings for Copying, Pasting, Cmd-Tabbing, etc. On a Mac you’ll have this available if you installed using homebrew with the --with-cocoa flag (brew install emacs --with-cocoa).

It’s also convenient to be able to open your editor from the command line and pass it a file or directory to start with. To make this easy, you can add this function to your shell profile (~/.bashrc, ~/.bash_profile, etc):

em () {
  open -a /usr/local/Cellar/emacs/24.5/Emacs.app/Contents/MacOS/Emacs $*
}

Then from the command line you’ll be able to do things like em pizza.el to open that file in an Emacs Cocoa app window.

Editor Basics: Find-In-Project and Ctrl-P

Next let’s try to check off a couple more features from the “What would I miss if I started using Emacs from Atom/SublimeText/TextMate?” list. The 2 of these I find most essential are:

  • “Ctrl-P” or “Cmd-T”-style command to fuzzily open a file in the current project
  • Find-in-Project text search for finding an arbitrary string in the current project

We can get these features pretty easily using 3 popular packages: Helm, Helm Projectile and Helm ag. Helm is actually something of a “parent” package for these other 2 – it provides a generalized framework for doing the kind of slick “fuzzily-search some text in a list of stuff” interface that we all know and love. Then other packages like Helm Projectile and Helm ag can take advantage of this interaction to build neat tools like “find a file in my project” or “find some text in my project.”

Add these to your package-list and give them some basic keybindings as follows:

(setq package-list '(better-defaults
                     solarized-theme
                     helm
                     helm-projectile
                     helm-ag))

(global-set-key (kbd "M-x") #'helm-M-x)
(global-set-key (kbd "s-f") #'helm-projectile-ag)
(global-set-key (kbd "s-t") #'helm-projectile-find-file-dwim)

The s in the keybinds here stands for the “Super” modifier, which in the OS X Cocoa app should be your CMD Key. (The other modifier shortcuts we’ll be seing include C- for Control, M- for Meta or Option, and S- for shift).

These bindings give us the familiar Cmd-f for “search for text in this project” and Cmd-t for “find files in my project.” Finally we also over-write the default M-x keybinding to use helm’s interface for searching for Emacs commands to run. Now when we need to look for a less familiar Emacs interactive command, the Helm interface will help us by fuzzily searching and narrowing among the available commands.

Ruby Basics: Highlighting and Auto-matching

Now that we’ve tamed Emacs into a slightly more well-behaved general editing environment, we can dive into our Ruby setup!

Let’s start with a bit of basic auto-matching for paired characters (parens, quotes, def/class/if-end, etc). Add ruby-electric to your package list and tell emacs to require it automatically whenever we enter ruby mode like so:

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric))

;...

;; Autoclose paired syntax elements like parens, quotes, etc
(add-hook 'ruby-mode-hook 'ruby-electric-mode)

Sometimes Ruby code appears in other kinds of files that don’t end with the standard .rb extension. We can tell emacs to treat these as ruby files using this snippet:

(add-to-list 'auto-mode-alist
             '("\\.\\(?:cap\\|gemspec\\|irbrc\\|gemrc\\|rake\\|rb\\|ru\\|thor\\)\\'" . ruby-mode))
(add-to-list 'auto-mode-alist
             '("\\(?:Brewfile\\|Capfile\\|Gemfile\\(?:\\.[a-zA-Z0-9._-]+\\)?\\|[rR]akefile\\)\\'" . ruby-mode))

Ruby Version Manager

Most Ruby developers these days are using some kind of Version Manager to simplify the process of installing and jumping around between various ruby versions. This is a great feature to have, but it unfortunately adds another layer of indirection between Emacs and the Ruby installation living on our machine.

To further complicate things, the community hasn’t really standardized on any of the particular options, which means you’re likely using one of Rbenv, RVM, or Chruby. Depending on which of these you’re using, you’ll want to pull in the appropriate config below:

RVM (Using rvm.el)

Add the rvm package and invoke it using (rvm-use-default)

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric
                     rvm))
;...

(rvm-use-default)

RBENV (using rbenv.el )

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric
                     rbenv))
;...

(global-rbenv-mode)
(rbenv-use-global)

;; Optional -- if your RBENV installation is located somewhere besides
;; ~/.rbenv, you will need to configure this:
;;(setq rbenv-installation-dir "/usr/local/rbenv")

Chruby (using chruby.el)

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric
                     chruby))
;...

(chruby "2.2.2") ;;  or whichever version you want to use

Ruby Interactions: The Once and Future Workflow

Now that we have the groundwork out of the way we can tackle the good stuff. In this section we’ll look at several tools for working interactively with Ruby code from within Emacs.

Emacs comes from a rich history of interactive, REPL-driven Lisp development environments. Combined with being deeply programmable (thanks to Emacs Lisp), this makes it ideal for creating a smoothly interactive development environment. We want to reduce the time and effort required to get feedback from running our code as much as possible, and Emacs can help accomplish this.

In the context of Ruby code, I specifically want to be able to:

  • Arbitrarily evaluate Ruby code from the current buffer
  • Open an interactive ruby session (i.e. REPL – IRB or Pry) within Emacs
  • Run tests from within Emacs

Let’s check out some neat Emacs packages that make interactions like these possible.

Ruby Buffer Interaction – Seeing truly is believing

First, install the Gem:

gem install seeing_is_believing --version 3.0.0.beta.7

Then, add and configure the corresponding Emacs package:

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric
                     seeing-is-believing
                     chruby))

;; ...

(setq seeing-is-believing-prefix "C-.")
(add-hook 'ruby-mode-hook 'seeing-is-believing)
(require 'seeing-is-believing)

Restart Emacs and open up a sample Ruby file. Try experimenting with the following keybindings to see what Seeing Is Believing gives us:

  • C-. s - Run Seeing is Believing for the entire file
  • C-. c - Clear the Seeing is Believing output
  • C-. t - Tag a line to be “targeted” for evaluation by SiB
  • C-. x - Run only the “tagged” lines (those with trailing “# => ” markers)

Hopefully you’re seeing some output show up at the end of your Ruby source lines. Seeing is Believing is a Gem that runs a chunk of Ruby code and prints out (in an existing text buffer) the result of evaluating each line. For our purposes, this gives us a very powerful way to quickly interact with a chunk of code – directly from our Emacs buffer!

To learn more, check out the docs for the Seeing Is Believing Gem and seeing-is-believing.el.

inf-ruby – IRB from Emacs

Next stop is a neat package called inf-ruby. In the tradition of other Emacs Inferior Language Modes, it gives us an embedded IRB process running inside of Emacs, as well as some standard keybindings to interact with the REPL by sending code snippets to it from a buffer.

First, install and configure inf-ruby:

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric
                     seeing-is-believing
                     chruby
                     inf-ruby))
;; ...
(autoload 'inf-ruby-minor-mode "inf-ruby" "Run an inferior Ruby process" t)
(add-hook 'ruby-mode-hook 'inf-ruby-minor-mode)

Restart Emacs then open up a Ruby file somewhere. Try out the following:

  • Use C-c C-s to launch the inf-ruby process.
  • Use C-x o to switch to the inf-ruby pane and try running some random ruby snippets as you normally would from IRB or pry.
  • Go back to your Ruby buffer, select (by highlighting) a chunk of code, and use C-c C-r to push that Ruby code into the IRB session.
  • For example, try defining a class in your Ruby buffer, select the whole buffer, run C-c C-r, then swap over to the inf-ruby buffer and instantiate an instance of your class. Pretty cool!
  • Alternatively, use C-c M-r to run a selected chunk of code and automatically go to the ruby buffer
  • Finally, use helm-M-x (which we bound earlier to the default M-x keybinding) to search for “ruby send” and see what other default bindings inf-ruby gives us.
  • If you do a lot of work in Rails or Sinatra, check out the commands inf-ruby-console-rails and inf-ruby-console-racksh. Using these commands inf-ruby can start a console session in the environment of your web project.

Ruby TDD – ruby-test-mode.el

Now for the last item on our interactive workflow checklist – running tests from Emacs. At its core, TDD is about incorporating more feedback into our development workflow. To take full advantage of this, we want running tests to be as seamless as possible – no context switching of jumping out to a terminal, etc etc. We can accomplish this in Emacs using the ruby-test-mode package. First install and configure it in your init file:

(setq package-list '(better-defaults
                     solarized-theme
                     helm-projectile
                     helm-ag
                     ruby-electric
                     seeing-is-believing
                     chruby
                     inf-ruby
                     ruby-test-mode))
;;...
(require 'ruby-test-mode)
(add-hook 'ruby-mode-hook 'ruby-test-mode)

Restart Emacs, then open a Ruby test file. Experiment with the keybinding “C-c C-,” - it should allow you to run the tests from the current file into a second buffer called a compilation buffer. Tests from directly within emacs – pretty neat!

By default, ruby-test-mode will try to evaluate tests in the current buffer. It determines whether the current buffer contains tests based on whether its filename ends in _test.rb or _spec.rb – so if you don’t follow these conventions it may behave erratically for you.

If the current buffer is not a Ruby test, it will try to do one of the following:

  • If there is a visible test buffer in another window (for example, you have 2 windows open in a side-by-side split), it will run that one. This is great for putting a test and an implementation up side-by-side and being able to run the test from either window.
  • If none of these are available, it will try to re-run whatever test was last run, if there is one

Improving Ruby Test Mode Interaction

This setup is coming along pretty nicely, but I find dealing with all the compilation buffers created by ruby-test-mode a little cumbersome. By default it pulls them up in a new window, which potentially covers up something you were working on before, or at least takes up half of your frame. Then you have to manually swap over and kill the buffer if you want to get rid of it.

We can make this a little smoother by hooking into the compilation completion hook and setting up a keybinding to easily close the buffer:

(add-hook 'compilation-finish-functions
          (lambda (buf strg)
            (switch-to-buffer-other-window "*compilation*")
            (read-only-mode)
            (goto-char (point-max))
            (local-set-key (kbd "q")
                           (lambda () (interactive) (quit-restore-window)))))

Now when ruby-test-mode finishes our tests, we will automatically jump to the test buffer and scroll to the bottom.

Additionally, within the compliation buffer we’ll use the simple keybinding q to close the buffer and return to whatever frame configuration we had before. I find this makes a much more seamless TDD workflow. We can run tests, quickly check out the results, and quit out to return to what you were doing before – all without leaving Emacs.

Wrapup and Further Exploration

I’ve published a github repo containing the final product of this tutorial here. I’ll try to keep it (and this post) up to date as I uncover any problems or receive feedback. The biggest goal here was to keep things as simple and minimal as possible – the whole thing comes in around 80 lines of Elisp using 10 or so packages.

With this setup, the main things we accomplished include:

  • Drag Emacs into the modern era using some sane defaults and a bit of UI polishing
  • Create a reasonably beginner-friendly environment that incorporates 2 of the killer convenience features of more mainstream editors like Sublime or Atom (Ctrl-P and Find-in-project)
  • Assemble a simple toolset for interactive Ruby development – we can evaluate code in a buffer, start an embedded IRB process, and run our tests all from within Emacs

A few things that are obviously not included (and where to find them) include:

  • Rails-specific workflow features (check out rinari if you want to use emacs to work on Rails)
  • Version control integration (magit is the cadillac of in-editor VCS integrations)
  • Other language modes or integrations – if it runs on a computer there’s probably an Emacs mode for it – dig around and see what exists for your favorite languages