Emacs 24 Rails Development Environment - From *scratch* to Productive in 5 Minutes

I'm a big fan of Emacs. My fellow employees at Viget enjoy a number of different editors including Textmate and Vim. I strongly believe that everyone should use an editor they feel productive in and most importantly, an editor they enjoy. So, I have no interest in engaging in any kind of editor holy war here.

I'd like to show you how you can go from a default installation of Emacs HEAD (24) to a workable Rails development environment in only a few minutes. I will make the following assumptions:

  1. You are running Mac OSX 10.6
  2. You have Xcode (< 4 beta) installed
  3. You have homebrew installed and working
  4. You have installed git using homebrew
  5. You are using rvm to manage your rubies and have a default configured.
  6. You have a basic understanding of Emacs

These instructions could likely be tweaked for different systems without too much fuss.

Let's begin!

Installing Emacs HEAD

To do this we'll use homebrew, observe:

$ brew install emacs --cocoa --use-git-head --HEAD
$ mv /usr/local/Cellar/emacs/HEAD/Emacs.app /Applications/
$ mkdir ~/.emacs.d
$ echo ";; emacs configuration" > ~/.emacs.d/init.el

This will leave you will a fresh copy of Emacs and a blank configuration file.

Configuring The Basics

At the risk of editorializing I'll say that Emacs doesn't have the most desirable configuration defaults. That's ok! It only takes a few lines of elisp to make a more pleasant experience.

So, go ahead and launch Emacs like you would any other application. You will be presented with the illustrious Emacs logo and some instructions on how to use the editor. If you are new to Emacs consider taking the Emacs tutorial presented in the default splash screen. Otherwise:

Navigate (C-x C-f) to ~/.emacs.d/init.el and insert the following after our singular comment at the top:

(push "/usr/local/bin" exec-path)

(setq make-backup-files nil)
(setq auto-save-default nil)
(setq-default tab-width 2)
(setq-default indent-tabs-mode nil)
(setq inhibit-startup-message t)

(fset 'yes-or-no-p 'y-or-n-p)

(delete-selection-mode t)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(blink-cursor-mode t)
(show-paren-mode t)
(column-number-mode t)
(set-fringe-style -1)
(tooltip-mode -1)

The most important bit of configuration is the first line. We are pushing "/usr/local/bin" onto the exec-path list giving Emacs the ability to use git (presuming it's installed via hombrew and the binary resides in /usr/local/bin).

In the interest of aesthetics I will add two additional lines to our configuration:

(set-frame-font "Menlo-16")
(load-theme 'tango)

The first line changes the default font while the second line uses one of Emacs 24's new features: built-in themes.

ELPA & el-get

Emacs 24 comes with ELPA package management built-in, however, the default repository of libraries excludes many we will need. So we'll configure Emacs to use the tromey repository.

el-get is a new package management library written by Dimitri Fontaine. It's great. It's also fundamental in making our quick configuration possible.

Install el-get in the following manner:

$ cd ~/.emacs.d
$ mkdir el-get
$ cd el-get
$ git clone git://github.com/dimitri/el-get.git

We can now add the following lines to our configuration to load ELPA w/ an alternate repository and el-get.

(require 'package)
(setq package-archives (cons '("tromey" . "http://tromey.com/elpa/") package-archives))
(package-initialize)

(add-to-list 'load-path "~/.emacs.d/el-get/el-get")
(require 'el-get)

At this point it's a good idea to evaluate the init.el buffer and refresh the elpa package list, in Emacs:

(M-x) eval-buffer
(M-x) package-refresh-contents

Package Installation

el-get can use a number of package retrieval protocols. Most relevant to us: ELPA and git. Drop the following configuration in your init.el:

(setq el-get-sources
      '((:name ruby-mode 
               :type elpa
               :load "ruby-mode.el")
        (:name inf-ruby  :type elpa)
        (:name ruby-compilation :type elpa)
        (:name css-mode :type elpa)
        (:name textmate
               :type git
               :url "git://github.com/defunkt/textmate.el"
               :load "textmate.el")
        (:name rvm
               :type git
               :url "http://github.com/djwhitt/rvm.el.git"
               :load "rvm.el"
               :compile ("rvm.el")
               :after (lambda() (rvm-use-default)))
        (:name rhtml
               :type git
               :url "https://github.com/eschulte/rhtml.git"
               :features rhtml-mode)
        (:name yaml-mode 
               :type git
               :url "http://github.com/yoshiki/yaml-mode.git"
               :features yaml-mode)))
(el-get 'sync)

Now evaluate the buffer again:

(M-x) eval-buffer

This will start pulling down and compiling our list of packages.

At this point you have all you need to hack on Ruby and Rails applications. There is, however, some simple configuration that will further enhance your Rails development experience.

Customizing Ruby and ERB Modes

If we look carefully at our previous el-get package list code, we notice that the entry for rvm has an :after parameter. This parameter takes whatever function passed and executes it after the package is loaded. This is the perfect place to add per-mode hooks to our configuration. In the instance of rvm the rvm-use-default command is run after the rvm library loads.

In the interest of keeping line length to a minimum, I will create hook functions for each mode I want to configure post-load. Insert the following functions into your init.el above the previous el-get-sources definition:

(defun ruby-mode-hook ()
  (autoload 'ruby-mode "ruby-mode" nil t)
  (add-to-list 'auto-mode-alist '("Capfile" . ruby-mode))
  (add-to-list 'auto-mode-alist '("Gemfile" . ruby-mode))
  (add-to-list 'auto-mode-alist '("Rakefile" . ruby-mode))
  (add-to-list 'auto-mode-alist '("\\.rake\\'" . ruby-mode))
  (add-to-list 'auto-mode-alist '("\\.rb\\'" . ruby-mode))
  (add-to-list 'auto-mode-alist '("\\.ru\\'" . ruby-mode))
  (add-hook 'ruby-mode-hook '(lambda ()
                               (setq ruby-deep-arglist t)
                               (setq ruby-deep-indent-paren nil)
                               (setq c-tab-always-indent nil)
                               (require 'inf-ruby)
                               (require 'ruby-compilation))))
(defun rhtml-mode-hook ()
  (autoload 'rhtml-mode "rhtml-mode" nil t)
  (add-to-list 'auto-mode-alist '("\\.erb\\'" . rhtml-mode))
  (add-to-list 'auto-mode-alist '("\\.rjs\\'" . rhtml-mode))
  (add-hook 'rhtml-mode '(lambda ()
                           (define-key rhtml-mode-map (kbd "M-s") 'save-buffer))))

(defun yaml-mode-hook ()
  (autoload 'yaml-mode "yaml-mode" nil t)
  (add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode))
  (add-to-list 'auto-mode-alist '("\\.yaml$" . yaml-mode)))

(defun css-mode-hook ()
  (autoload 'css-mode "css-mode" nil t)
  (add-hook 'css-mode-hook '(lambda ()
                              (setq css-indent-level 2)
                              (setq css-indent-offset 2))))

Now we can add :after parameters to ruby-mode, rhtml-mode, yaml-mode, and css-mode. Now our el-get package list looks more like this:

(setq el-get-sources
      '((:name ruby-mode 
               :type elpa
               :load "ruby-mode.el"
               :after (lambda () (ruby-mode-hook)))
        (:name inf-ruby  :type elpa)
        (:name ruby-compilation :type elpa)
        (:name css-mode 
               :type elpa 
               :after (lambda () (css-mode-hook)))
        (:name textmate
               :type git
               :url "git://github.com/defunkt/textmate.el"
               :load "textmate.el")
        (:name rvm
               :type git
               :url "http://github.com/djwhitt/rvm.el.git"
               :load "rvm.el"
               :compile ("rvm.el")
               :after (lambda() (rvm-use-default)))
        (:name rhtml
               :type git
               :url "https://github.com/eschulte/rhtml.git"
               :features rhtml-mode
               :after (lambda () (rhtml-mode-hook)))
        (:name yaml-mode 
               :type git
               :url "http://github.com/yoshiki/yaml-mode.git"
               :features yaml-mode
               :after (lambda () (yaml-mode-hook)))))

We've now got file extension detection and nice ruby indentation. At this point, you are finished. You can now develop your Ruby and Rails application in Emacs!

Bonus Round

The experienced Emacs Rails developer will notice that I haven't included rinari. While rinari is a great library filled will all manner of helpful shortcuts, I never found myself using any of them save for one: running a single test buffer. Using functionality from the textmate.el library, I wrote two simple functions for the purpose of running a single test buffer:

(defun is-rails-project ()
  (when (textma…
Matt Swasey

,
Posted in Article Category: #Code
on