Jamie Lawrence

Using Guard and Vagrant for Rails development

Background

I do all my development in a Vagrant VM and it’s mostly a seamless experience — except when using I was using guard. guard listens to file changes and runs the matching specs — it’s invaluable when doing any test-driven development. But when guard was running inside Vagrant it wouldn’t receive the filesystem events (in my case: OS X host, Ubuntu 12.04 LTS guest) which meant it would fall back to the polling adapter. This had to poll the directory structure and would eat ~25% of my CPU and a resulting impact on the battery life. Not good.

I struggled on with judicious use of focus: true statements in my specs and changing the polling latency to about 10secs guard -p -l 10. It worked but it should have been better. guard uses the listen gem to actually handle the filesystem events and there was a suggestion that we could listen to the host filesystem and forward those events to the guest over TCP. I actually had a go at this implementation but my multi-threaded Ruby foo let me down in the time I had and then listen transitioned to a Celluoid architecture. Thankfully, Tim Kurvers stepped in with an implementation for listen that would forward these events and receive them on the other end. But that was only part of the story.

Now that listen worked, we needed to bring that ability into guard. So I stepped up with a fairly simple implementation which added a -o / --listen-on option to guard, and followed that up with a listen CLI to make forwarding those events a simple process. The result is that from guard 2.5.0 you can do the following…

Sending filesystem events to the Vagrant guest OS

First, make sure that you forward a local port to your guest machine. In your Vagrantfile, put something like:

config.vm.network :forwarded_port, guest: 4000, host: 4000

Next, we need to start listen on the host OS (OS X, in my case), in our project directory /Users/jamie/projects/myproject:

listen -f 127.0.0.1:4000

You can specify the -v option to print some activity information and -d to change the directory it listens to

Make Guard use the network events

Now, we start guard with the -o option and tell it what address:port to listen to

guard -o "10.0.2.2:4000" -w "/Users/jamie/projects/myproject/"

(find out the correct ip address to use). We also need to specify the -w option with the host directory since the events are being sent with the full host pathname of the files.

Now we have our filesystem events in guard, with low CPU-use and low-latency — a decent boost for doing test-driven development. Guard works as well in a VM as it does outside Vagrant.

Personal Reflection

I don’t often get to contribute to open source projects for a variety of reasons. Usually I just don’t have the right combination of ability/time/desire/pain to get stuck in but it’s great be able to help when I can. It’s definitely worth watching your favourite issues, even if you can’t fix it yourself. Someone else might have a go and there will always be some testing, documentation or ‘mopping-up’ work to be done.