Running Rspec files from VIM, showing the results in Firefox

The Problem

In Gedit I was always using this script to execute the current Rspec file, when developing Rails apps. It was very useful to me since the script already includes all the necessary rspec dependencies in the load path and executes the declared specs wtihout running the dbtest:prepare Rake task. This makes the tests much faster, in special if all we want is to run the specs of a single Rspec file. Another thing that I like in the script is that it uses the Rspec HTML output, generating an HTML page which is shown in a new Firefox tab. This page shows even the code fragments for the failing specs, which is very useful for solving problems.
Since I moved back to VIM recently, I wanted to do the same using the rails.vim plugin. In rails.vim, when we have an open Rspec file and run :Rake this file is executed with Rspec. The downside is that all the test database is reloaded, making the tests unnecessarily slow. Another thing that I dislike is that VIM will put all specs execution output in a temporary view mode and wait until you press or run another command, going back immediately to the spec buffer. If you forget what was the error message or the failing spec, you will need to re-run the whole thing, since the output shown by VIM was temporary.

The Solution

The solution was to adapt the Gedit script, put it in an external Ruby file and create a new function to execute this script in my .vimrc. For this solution to work your VIM must have been compiled with the +ruby option, which enables a Ruby interface for VIM. To see if your VIM has the Ruby support, run :version and search for “+ruby”. If all you have is “-ruby” you’ll need to re-compile VIM with this option enabled. Take a look at the VIM Makefile and serch for something like enable ruby-interp.

This solution was tested using VIM 7.2 in a Ubuntu 8.04 box.

Create a new file with the following content and put it in ~/.vim/bin/run_rspec.rb

spec_dir=File.dirname(ARGV[0])
rails_project_dir="#{spec_dir}/../.."
report_file="#{rails_project_dir}/doc/rspec_report.html"
rspec_rails_plugin = File.join(rails_project_dir,'vendor','plugins','rspec','lib')
if File.directory?(rspec_rails_plugin)
$LOAD_PATH.unshift(rspec_rails_plugin)
end
require 'rubygems'
require 'spec'
require 'spec/runner/formatter/html_formatter'
module Spec
module Runner
module Formatter
class HtmlFormatter
def backtrace_line(line)
line.gsub(/([^:]*\.rb):(\d*)/) do
"<a href=\"vim://#{File.expand_path($1)}?#{$2}\">#{$1}:#{$2}</a> "
end
end
end
end
end
end
if File.exists? report_file
File.delete report_file
end
argv = [ARGV[0]]
argv << "--c"
argv << "--format"
argv << "html:#{report_file}"
::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(argv, STDERR, STDOUT))
if File.exists? report_file
`firefox #{report_file}`
end
&#91;/source&#93;
Then edit your <strong>.vimrc</strong> file, which should be located at <strong>$HOME/.vimrc</strong>, adding the following lines:
<pre>
<code>
" Run Rspec for the current spec file
function! RunRspec()
ruby &lt;&lt; EOF
buffer = VIM::Buffer.current
spec_file = VIM::Buffer.current.name
command = "ruby ~/.vim/bin/run_rspec.rb #{spec_file}"
print "Running Rspec for #{spec_file}. Results will be displayed in Firefox."
system(command)
EOF
endfunction
map &lt;F7&gt; :call RunRspec()&lt;cr&gt;
</code>
</pre>
Here I mapped the F7 key to execute the specs, but you can change the function to use any key combination you like. Now all you need to do is press F7 to run all the specs for the current Rspec file and present the output in a new Firefox tab.
UPDATED: <br>
Pay attention to the fact that each error shown in Firefox will have a clickable link, which will open the respective file in Vim and take you right to the line of the failing spec. For this to work, you'll need to make some configurations:
<ul>
<li>Open the address <strong>about:config</strong> in your Firefox</li>
<li>Firefox will show you a warning page, you can tell him that you know what you're doing...</li>
<li>Right-click anywhere on the page, choose "new option" and then "boolean". Put <strong>network.protocol-handler.external.vim</strong> as the option's name and set its value to <strong>true</strong></li>
<li>Again, right-click, choose "new option" and then "string". Put <strong>network.protocol-handler.app.vim</strong> as the option's name and <strong>~/.vim/bin/open_vim</strong> as its value</li>
<li>Now just close the about:config page</li>
</ul>
Now you'll need to create the script that will get called when you click an error line and will take care of opening your spec file in Vim (actually it's configured to use GVim, you can change it if you want). The file will be opened in an already running GVim instante, using a new tab. Create a file called <strong>open_vim</strong> inside <strong>$HOME/.vim/bin</strong>, with the following contents:
#!/usr/bin/ruby
regex = /(.+)\?(\d+)/
ARGV[0] =~ regex
system("gvim --remote-silent +#{$2} #{$1}")

Save and close it. Now give it execution permition by running chmod +x ~/.vim/bin/open_vim. The first time you click in an error line in Firefox, it will prompt you to confirm that you want to use open_vim as the program to open vim:// links. Just accept it and you’re good to go.

If you’ve made some changes to your development database and want them to exist in your test database before running the specs, just run rake db:test:prepare once and you’re good to go.

The forum thread you did mention says that if Firefox finds the program it’ll just do nothing. If it keeps saying that it doesn’t know how to handle vim links, then something is broken on the way you did the configurations. In fact I created this solution at work and it worked very well there, using Firefox 3 (don’t know which minor version).
Here at my laptop I can confirm that it’s not working too. Tomorrow I’ll take a look at which version I’m using at work.

I know I am asking too much. But if, let’s say, the file under test has a grammar error. The run will just silently fail (since the html file has not been not generated yet). Is there a way to display error message?

The piece of Ruby code inside .vimrc has a line system(command). This line is the one that actually runs the script run_spec.rb
If run_spec.rb returns an error, the return value for the system method will be “false”, otherwise it’l be “true”. We could use it and write something like this inside the .vimrc RunSpec() function: