Extending Rails.vim with Custom Commands

Josh Steiner

Thanks to the always awesome Tim Pope, Rails.vim version 5 was released a month ago, and with it comes a new way to configure navigation commands. The new projections.json replaces editor.json, which in turn replaced Rnavcommand.

Projections allow you to define custom ways to navigate to files, open related files, or create files with boilerplate if the file does not exist.

Here are some examples for predefined navigation commands that you may recognize:

  1. Assuming we are in app/models/user.rb, :AV will open up spec/models/user_spec.rb in a vertical split.

  2. To create a new file app/controllers/users_controller.rb, simply enter :Rcontroller users! and you will be taken to a file that looks like this:

    class UsersController < ApplicationController
    end
    

The ! is the magic which force-creates a new file.

Now that we know how to use them, let’s define some custom commands.

The four primary ways to define projections are:

  1. Per app, in config/projections.json.
  2. Per bundled gem, in g:rails_gem_projections (requires bundler.vim).
  3. Inside a bundled gem, in lib/rails/projections.json (requires bundler.vim).
  4. Globally, in g:rails_projections.

Each projection can define a handful of options, including alternate (:A) and related (:R) files or a template for new files. More are described in the docs.

A complete example for Active Model Serializers navigations:

"app/serializers/*_serializer.rb": {
  "command": "serializer",
  "affinity": "model",
  "test": "spec/serializers/{}_spec.rb",
  "related": "app/models/{}.rb",
  "template": "class {camelcase|capitalize|colons}Serializer < ActiveModel::Serializer\nend"
}

This will allow you to jump back and forth between the serializer’s tests and model, as well as create a new serializer based on the filename.

Rails.vim will capture the input, which you may use in your file name definitions and template code with the following transformations:

{}: original
{camelcase|capitalize|colons}: Ruby class names
{plural}: pluralized
{singular}: singularized
{underscore|capitalize|blank}: humanized

While it is possible to create projections inside of gems, it seems strange to include editor specific code. Because we can’t count on every gem to include them, I created my own global and gem specific projections, defined by g:rails_projections and g:rails_gem_projections respectively. Since this is vimscript, they are defined in a butchered version of JSON by escaping line breaks.

Gem specific projections will only be availabile if the gem is bundled with the current project, and you have installed bundler.vim. The definitions for each are grouped by the gem’s name as seen in this example.

Project specific projections can be defined in config/projections.json with real JSON, like in these Backbone.js and Ember.js examples.

For a more in depth description of what projections can do, read more online or with :help rails-projections.

What’s next

If you found this useful, you might also enjoy: