rails.vim: navigating your Rails project
If you are a Ruby on Rails developer and a dedicated Vim user, then chances are
you've heard of (and are using) this marvelous plug-in called rails.vim. It's
made by Tim Pope, a creator or many great Vim plug-ins, so if you haven't
already, be sure to check out his repos on GitHub. So, what does this plug-in
do you ask? In general it makes Vim act and feel as if was made for programming
Rails. It is simply that powerful. I won't be going through all of the features
of the plug-in here, but I'll cover the stuff that I feel is most useful, and
hopefully some of this will blow your mind.
Navigating your project
In this first post, we'll have a look at how rails.vim makes it super easy to
navigate within your Ruby on Rails project. This is probably one of the most
basic features of the plug-in, but it's very powerful once you get used to it.
You might argue that you don't need any help to navigate your Rails source
files. Perhaps you use something like ctrlp or Command-T for fuzzy matching
your files. While I encourage using such methods for quickly opening your source
files, I simply find the commands rails.vim adds to be easier to use and more
semantic, so I urge you to give them a go anyway.
The plug-in actually enhances the way you navigate your project in multiple
ways, one way is to use the numerous Ex commands that it provides. Here's a list
of the commands I most commonly use:
-
:Rmodel
– Opens a model in the current buffer -
:Rview
– Opens a view in the current buffer -
:Rcontroller
– Opens a controller in the current buffer. -
:Rhelper
– Opens a helper in the current buffer. -
:Rmailer
– Opens a mailer in the current buffer -
:Renvironment
– Opens an environment file in the current buffer, or config/application.rb if none are specified. -
:Rcontroller
– Opens a controller in the current buffer
There are so many more of these, but this is a good set to start off with. As an
example of using one of these, if you wish to open the JoyfulUser model, your
command would look like this:
:Rmodel User
Or it could look like this:
:Rmodel user
Either variation works, rails.vim is clever enough to map class names to
filenames based on conventions, and thus opens the correct files. You can even
use namespaces such as Admin::User, which would be automatically mapped to the
file admin/user.rb.
These commands will also auto-complete either filenames or class names
(depending on your input, or filenames by default if you input nothing), and
they auto-complete semantically, meaning the :Rmodel
command will only suggest
names of files in your application's app/models directory, :Rcontroller
would only suggest files in app/controllers, and so forth. While you could
accomplish something similar using other fuzzy search tools, they just don't
have that semantic functionality in them that this plug-in provides.
But what if you don't want the files opened in your current buffer, but rather
in a new tab, a vertical split or a horizontal split? Of course, rails.vim has
you covered. All of the commands used for opening files have equivalents for
opening in new buffers, be it any kind of split or a new tab. They are easy to
remember and fast to type in, too. To open the User model in a vertical split,
you would run the command :RVmodel User
, to open the PagesController in a new
tab you would run :RTcontroller PagesController
and to open the
sessions/new.html.erb view in a horizontal split you would run `:Rview
sessions/new.html.er . Naturally, all of these commands also auto-complete just
like the “open in current buffer” versions.
By default, if given a filename (or class name) that doesn't exist, it prints
out an error message telling you that the specified model, controller or
whatever you asked for doesn't exist. It does, however, give you the opportunity
to automatically create the file that you want, with default content. To do
this, provide a bang (i.e. an exclamation mark) at the end of the argument
passed in. So that means, by running the command :Rmodel NonExistingModel!
,
rails.vim will create the file app/models/non_existing_model.rb with the
following default content:
class NonExistingModel
end
However, while this is really powerful, I usually prefer using the Rails
generator methods (in this example rails g model
). The reason for this is that
the plug-in will only create that file, whereas the generator usually creates
many more. For models, the generator would create a migration and a test or spec
file for the model as well, which makes it more powerful. And, not surprisingly,
rails.vim makes it even easier to run generators as well. More on that in a
later post.
I'd like to also mention another pretty neat feature with the Ex command
navigation provided, and that is its context-awareness. I actually found this
out by coincidence, although I'm sure it's documented somewhere. Let me explain
what I mean by the plug-in being context-aware. Say you're currently viewing a
controller (let's call it UsersController) and you want to check out the model
associated with that controller (by convention being the User model). You could
go ahead and call :Rmodel User
, but in fact, rails.vim makes it even easier
than that. Calling :Rmodel
with no arguments will actually look up the file
that is supposed to hold the User model. If it exists, it will navigate to it. I
am a big fan of splits and I find this extremely useful for times when I want
the controller and the view side-by-side, for instance. I would have the view
open, and then run :RVcontroller
with no arguments to get the associated
controller in a vertical split buffer. It's pure magic.
Line numbers and methods
These Ex commands provided by rails.vim also gives you the possibility to jump
directly to a line number or a method definition when opening a file. This is
helpful during debugging – when you know the line number from a backtrace – or
simply have a very large class and need to check or work with a specific method.
Say you have a PostsController and want to change the order of which it fetches
the posts for the index page. The code for this is located in the index
action
of the controller, so in order to get there fast you could either navigate to
the controller class and perhaps find the method manually, or you could navigate
directly to the method like this:
:Rcontroller posts#index
Notice how we separate the name of the controller and the action with a hash
sign (#). This resembles the RDoc conventions of preceding instance methods
with hash signs. However, it is not possible to navigate to class methods using
a period or double colons, the hash sign must be used there as well. Keep in
mind that the methods names cannot be auto-completed. Despite what you might
think, this does not alter your searches (in order to find the method), so your
previous search term is still kept intact.
As I mentioned, you can also jump directly to a line number in the target file.
To do this, you would use a single colon to separate the class or file name and
the line number. So if you wanted to jump to line 15, you would do run the
following command in the PostsController:
:Rcontroller posts:15
Knowing the path
As a side note, it might be worth mentioning that the plug-in manipulates the
“path” setting to include your project's source directories to accomplish these
things. This also means that you can use the :find
command to navigate to
files that might not have special commands mapped to them (like for instance
config/database.yml). However, I tend to just use ctrlp for that myself as the
file names are usually easy to identify right away.
Navigating using Vim's gf
command
That should get us warmed up on navigating a Rails project using rails.vim.
However, there are other ways to navigate as well, which are available because
the plug-in alters the behaviour of the gf
command. Let's say that you are
currently viewing the Category model in your app, which has_many :posts
. With
your cursor positioned over the line that associates these models, pressing the
gf
key combination takes you directly to the Post model. Posts may have many
comments, and with the cursor on the line saying has_many :comments
, pressing
gf
would jump to the Comment model. This is especially powerful when used
together with Vim's jump list. After having jumped from Post to Comment,
pressing ^o
would take you straight back to the Post model again, and pressing
it again would take you yet another step backwards, to the Category model.
Master this technique and you will be looked upon as a Vim wizard by your mates
and co-workers.
This doesn't just work with plain Ruby classes, no, rails.vim takes it further
than that. It can also be used to quickly jump to partials that are being
rendered in views. Since a post has many comments, we might have a show view
that has something like this in it:
<section class="comments">
<h3>Something on your mind?</h3>
<%= render "comments/form", comment: @post.comments.build %>
<%= render "comments/list", comments: @post.comments %>
</section>
Positioning your cursor over, say, the line with render "comments/form"
on it
and pressing gf
would navigate you to the file
app/views/comments/_form.html.erb. And again, this works extremely well
together with the jump list to jump back and forth between files. If you're
impressed by this feature now, just wait until you've tried it out for yourself.
Alternate and related files
So far we've been seeing how rails.vim can help you navigate to specific files
either by specifying them in an Ex command such as :Rmodel
or by using the
powerful gf
command in normal mode. There is a third way as well, which I am
quite sure I couldn't live without now that I've tried it, and that is using the
concept of alternate and relative files. This is yet another context-aware
functionality that the plug-in provides, and once you've got the gist of it
you're hooked.
The way it works is that rails.vim has a set of files that are considered
alternate or related to the currently active file. Alternate files are usually
test files, so the alternate file to app/models/user.rb would be
spec/models/user_spec.rb (or a similar unit test file from another testing
framework). There are some exceptions to this rule, though. Related files vary,
and may be dependent on the position of the cursor in the current file.
Following is a table showing some types of files together with their alternates
and related ones.
For instance, if you're currently located in a model, the alternate file would be the specs for that model. The related file would be the schema definition for that model, i.e. the line in db/schema.rb where the create_table
statement for that model's table is. From controllers, the alternate file is also the spec, and the related file will take you to the view for the action that the cursor is currently located inside or, if the cursor is not inside any action, to the associated view helper. From a view, the related file is the reverse of that of a controller, taking you to the action associated with that view, and the alternate file is, once again, the view's spec. Migrations have no specs, and alternate file is the previously defined migration (by timestamp), whereas the related file is next migration. This way, you can cycle through migrations, if you ever feel like doing such a thing. There are many other alternate/related file mappings that you can explore
So, how do you start using these mappings? The answer is simple: through a couple of Ex commands. To jump to an alternate file, use the command :A
. To jump to a related file, use :R
. The commands are easy. The hard part here is to remember how different types of files are mapped together, however, if you use it frequently you'll quickly get a hang of most of them. As with the other Ex commands, you can also append S, V or T to open the alternate or related file in a horizontal split, vertical split or new tab, respectively. I commonly use :AV
to open specs for a file in a vertical split beside my actual source files.
Conclusion
We've seen how rails.vim provides many ways to navigate around your Rails
project. It provides Ex commands for navigating to specific types of files, such
as models, controllers and views. We also took a look at the gf
command, which
has been enhanced to understand more easily what files should be jumped to based
on the context it is written in. Lastly, we saw how it's also context-aware in
the sense that the plug-in knows what file to navigate to files that are related
somehow to the currently open buffer.