02 Feb 2014
- The default keybindings have been updated based on the feedback from Asok, the author of the awesmoe
This post outlines some of the Emacs extensions (open source, of course) which can significantly ease the life of a Rails developer. While Rails is, by design, quite a productive framework, having the dev environment properly setup can multiply developer efficiency by an order of magnitude. Although almost all of this information is available online elsewhere, I nevertheless wanted to summarize my explorations in form of a basic guide to easily configure extensions I have found to be useful, during the course of my Rails projects. While the primary audience is a forgetful me from the future, I hope developers new to Rails or Emacs (or both) will find this helpful to get up and running with Emacs and Rails, without having to wade through reams of documentation right upfront. I have tried my best to link the appropriate authoritative resources, which may be helpful for people looking forward to customizing and extending the setup.
While the setup below has been tested only in Linux Elementary, it should work for other Posix environments as well. Please ensure that you have got atleast Emacs 24 and can download packages through
package.el. If you are not familiar with package management in emacs Bozhidar Batsov has presented a great introduction. Adding the following snippet to your
~/.emacs should provide access to marmalade and melpa package archives:
(require 'package) (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/")) (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t) (package-initialize)
ruby-mode bundled with Emacs works well with syntax highlighting, it does not automatically handle syntax checking. Luckily we have flymake, which is a generic on the fly syntax checking system. We can install flymake-ruby which facilitates syntax checking for ruby through our package manager. Just hit
M-x package-install and type in
Once flymake-ruby is installed, we just have to hook it up with the ruby-mode.
(require 'flymake-ruby) (add-hook 'ruby-mode-hook 'flymake-ruby-load)
And voila, no syntax errors go unnoticed ever again:
The default indentation system attempts to align the arguments of a function with the opening bracket vertically.
function_call (arg1, arg2);
While this is subjective, but if you, like me, find this behaviour erratic - the following will make emacs indent code inside parenthesis similar to elsewhere.
(setq ruby-deep-indent-paren nil)
Ruby shell inside emacs
inf-ruby provides a REPL buffer connected to a Ruby subprocess. It is available through the package manager. Once installed you would probably want to bind it to a convenient shortcut. The following would bind it to
C-c r r.
(global-set-key (kbd "C-c r r") 'inf-ruby)
Integration with RVM
If you use RVM for managing ruby versions, you would want to use rvm.el. Once installed you will just have to call
rvm-activate-corresponding-ruby and rvm.el will automatically pick up your ruby version and gemset from .rvmrc file.
The following will bind the aforementioned command to
C-c r a
(global-set-key (kbd "C-c r a") 'rvm-activate-corresponding-ruby)
It seamlessly integrates with
inf-ruby so if you invoke
inf-ruby after the previous step, you will get the version of ruby and gemset you expect.
Project management with projectile
So far the steps had nothing to do with Rails and were equally useful for vanilla ruby projects. However for managing large Rails applications some basic project management facilities might come in handy.
While there are a plethora of project management utilities for emacs, One that is particularly simple and easy to install is projectile. It is available through the package manager. Once installed it can be configured as a global mode as follows:
or it can be hooked into particular modes:
(add-hook 'ruby-mode-hook 'projectile-on)
I would also strongly recommend using the ido-mode which, among other things, provides various enhancements to make navigation between files and buffers easy. The default behaviour of
C-x C-f and
C-x C-b are changed to a more interactive version, which shows a list of matched options while typing right inside the minibuffer. flx-ido is an extension which further enhances the matching capabilities of ido to perform fuzzy matching (a-la sublime text) which can be a great productivity boost, particularly if you are a bit sloppy with keyboard.
ido is built into emacs and
flx-ido is available through package manager.
flx-ido-mode activates the ido mode augmenting with flexible matching.
You may want to use following snippet to display ido completions vertically instead of horizontally, as is the default behaviour.
;; Display ido results vertically, rather than horizontally (setq ido-decorations (quote ("\n-> " "" "\n " "\n ..." "[" "]" " [No match]" " [Matched]" " [Not readable]" " [Too big]" " [Confirm]"))) (defun ido-disable-line-truncation () (set (make-local-variable 'truncate-lines) nil)) (add-hook 'ido-minibuffer-setup-hook 'ido-disable-line-truncation) (defun ido-define-keys () ;; C-n/p is more intuitive in vertical layout (define-key ido-completion-map (kbd "C-n") 'ido-next-match) (define-key ido-completion-map (kbd "C-p") 'ido-prev-match)) (add-hook 'ido-setup-hook 'ido-define-keys)
Projectile integrates with ido and uses it as its indexing method. For projectile to recognize the project root, you just have to drop an empty .projectile file there. Once that is done, you can simply perform a fuzzy search for files using
C-c C-p f and directories using
C-c C-p d
Projectile Rails builds upon
projectile to provide project management facilities specifically tailored for Rails applications. It is available through package manager and can be hooked up with projectile using :
(add-hook 'projectile-mode-hook 'projectile-rails-on)
Projectile Rails adds a large number of keybindings to ease navigation across files in a rails project, running rake tasks, invoking console etc. Following is the list of commands and associated bindings taken from the home page of projectile rails.
|projectile-rails-find-model||C-c r m||Find a model using
|projectile-rails-find-current-model||C-c r M||Go to a model connected with the current resource.|
|projectile-rails-find-controller||C-c r c||Find a controller using
|projectile-rails-find-current-controller||C-c r C||Go to a controller connected with the current resource.|
|projectile-rails-find-view||C-c r v||Find a template or partial using
|projectile-rails-find-current-view||C-c r V||Go to a view connected with the current resource.|
|projectile-rails-find-helper||C-c r h||Find a helper using
|projectile-rails-find-current-helper||C-c r H||Go to a helper connected with the current resource.|
|projectile-rails-find-lib||C-c r l||Find a lib using
|projectile-rails-find-feature||C-c r f||Find a feature using
|projectile-rails-find-spec||C-c r p||Find a spec using
|projectile-rails-find-current-spec||C-c r P||Go to a spec connected with the current resource.|
|projectile-rails-find-migration||C-c r n||Find a migration using
|projectile-rails-find-current-migration||C-c r N||Go to a migration connected with the current resource.|
|projectile-rails-find-stylesheet||C-c r s||Find a stylesheet using
|projectile-rails-find-log||C-c r o||Find a log file and enable
|projectile-rails-find-initializer||C-c r i||Find an initializer file using
|projectile-rails-find-environment||C-c r e||Find an environment file using
|projectile-rails-find-locale||C-c r a||Find a locale file using
|projectile-rails-find-mailer||C-c r @||Find a mailer file using
|projectile-rails-find-layout||C-c r y||Find a layout file using
|projectile-rails-console||C-c r ! c, C-c r r||Run
|projectile-rails-server||C-c r ! s, C-c r R||Run
|projectile-rails-rake||C-c r ! r||Select a rake task to run using
|projectile-rails-generate||C-c r ! g||Run
|projectile-rails-extract-region||C-c r x||Extract the selected region to a partial.|
|projectile-rails-goto-file-at-point||C-c r RET, C-c r g f||Go to a file at point. Depending on the context that might be a constant, template or partial, or a gem.|
|projectile-rails-goto-gemfile||C-c r g g||Go to
|projectile-rails-goto-routes||C-c r g r||Go to
|projectile-rails-goto-schema||C-c r g d||Go to
|projectile-rails-goto-spec-helper||C-c r g l||Go to
rails console uses
inf-ruby, so integration with rvm is smooth and effortless.
Intelligent Code navigation and Completion with Robe
Robe is a code assistance tool that uses a Ruby REPL subprocess with your application or gem code loaded, to provide information about loaded classes and modules, and where each method is defined.
Robe is available via package manager. Once installed it can be hooked into ruby mode.
(require 'robe) (add-hook 'ruby-mode-hook 'robe-mode)
If you are using rvm you may want to instruct robe to auto-trigger
(defadvice inf-ruby-console-auto (before activate-rvm-for-robe activate) (rvm-activate-corresponding-ruby))
robe-jump bound by default to
M-. can be used to jump to definitions of various classes and methods. While it is not ideal, in most of the scenarios, the expected entry is present in the first few suggestions.
For intelligent completion robe can be integrated with company mode. Company mode is a modern and modular completion system for emacs which accepts a multitude of pluggable back-ends and front-ends to provide rich completions on the fly. Robe provides a backend to the completion in form of company-robe. This can be configured as follows :
(global-company-mode t) (push 'company-robe company-backends)
Now all you have to do is hit
robe-start when you navigate into your project. After that at any point
if you trigger
company-robe you should be greeted with an intellisense like method definitions.
Completion will start automatically after you type a few letters. Use M-n, M-p, <tab> and <return> to complete. Search through the completions with C-s, C-r and C-o. Even the documentations are available right from the editor (Press F1 at any menu entry).
One of the caveats is that robe requires
pry-doc to be in Gemfile. This is a problem when working with third party projects and I don't have a workaround for that yet.
Also Ruby being a dynamic language which strives to push the limits on flexibility, perfect completion is simply not possible. Many of known caveats are listed here and some are being actively being worked upon. But nevertheless, I have found the setup to be quite useful for day to day development and hope you do too.
I hope that this quick detour into the world of emacs-extensions has motivated you to atleast try out Emacs. How far you go down the rabbit hole is of-course for you to decide. As always, any criticism, suggestions and comments are welcome.