Pular para o conteúdo
janeiro 9, 2009 / cassiomarques

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.

17 Comentários

Deixe um comentário
  1. sermoa / fev 11 2009 4:25 pm

    Very nice, thanks!

    I had to change the Firefox command for OSX so my line 40 says

    `open -a /Applications/Firefox3.app #{report_file}`

    Ideally i’ll find a way to alias it so as to keep your file the same.

    By the way, for anyone who accidentally copies the line numbers in (like i did!) here’s a VIM switch to take them out again:

    :%s/^\s*\d*. //g

  2. cassiomarques / fev 11 2009 4:33 pm

    @sermoa,

    Nice, your comment will be useful for other people using OSX.

    About the line numbers problem whem copying: just at the beggining of the source code block there is a “view plain” link that you can click to show the raw code, without the line numbers :)

  3. Yi Wen / mar 5 2009 10:30 pm

    When a test fails, there are clickable fail at line numbers. Is it possible to click on it and return to vim with the line in question?

  4. cassiomarques / mar 6 2009 3:36 am

    @Yi Men,

    I just updated the post with a solution for your problem :) Take a look at it and see if it work for you.

  5. Yi Wen / mar 6 2009 5:02 am

    Thanks for the effort. But according to this: http://support.mozilla.com/tiki-view_forum_thread.php?locale=fr&comments_parentId=74068&forumId=1

    This approach won’t work for FF3. Actually it doesn’t work for me. FF3 kept reminding me of it doesn’t know how to handle vim links. :( Does it work for you?

  6. cassiomarques / mar 6 2009 5:14 am

    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.

  7. Yi Wen / mar 6 2009 8:03 am

    I tried mozex plugin. And it seems be able to actually the external app using its “Universal” handler. But I got the error message like this:
    mozex error: universal command: cannot run executable ‘/Applications/MacVim.app’ (args: file:///Users/ywen/.vim/bin/run_rspec.rb?37): [Exception… “Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIProcess.init]” nsresult: “0x80004005 (NS_ERROR_FAILURE)” location: “JS frame :: chrome://mozex/content/mozex.js :: mozexRunProgram :: line 1026” data: no]

    JS frame :: chrome://mozex/content/mozex.js :: mozexError :: line 1358
    JS frame :: chrome://mozex/content/mozex.js :: mozexRunProgram :: line 1031
    JS frame :: chrome://mozex/content/mozex.js :: mozexHandleMouseDown :: line 370

  8. Yi Wen / mar 6 2009 5:41 pm

    I downloaded FF2, installed mozex on it. replace regex in open_vim with /vim:\/\/(.+)\?(\d+)/, and it works just great. Now the last great thing about TextMate is ported to vim :)

  9. cassiomarques / mar 6 2009 5:50 pm

    @Yi Wen,

    Glad to know it worked! Now we’ll just have to wait for the FF3 bug fix :)

  10. Yi Wen / mar 6 2009 6:10 pm

    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?

  11. cassiomarques / mar 6 2009 6:15 pm

    Did u mean display an error message in Vim?

  12. Yi Wen / mar 6 2009 6:32 pm

    yeah

  13. cassiomarques / mar 6 2009 10:32 pm

    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:

    print “An error has occurred!” unless system(command)

  14. Yi Wen / mar 9 2009 8:34 pm

    A little bit of improvement, which will show the error in FF. Just like what TextMate would do in this situation:

    begin
    ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(argv, STDERR, STDOUT))
    rescue Exception => e
    File.open(“#{report_file}”, “w”) do |file|
    file.write e
    end
    end

  15. cassiomarques / mar 9 2009 8:41 pm

    Nice!

Trackbacks

  1. Run Rspec within vim and seeing result in your Firefox2 « Dynamic and Concise
  2. How can I run Ruby specs and/or tests in MacVim without locking up MacVim? | DeveloperQuestion.com

Deixar mensagem para Yi Wen Cancelar resposta