Red Green Repeat Adventures of a Spec Driven Junkie

The Project Three Cs: Ruby

I really enjoy setting up projects through vagrant because I can achieve a philosophy for a project that I think of as The Project Three Cs:

Clean Build a system from the basics
Consistent Create a system the same way over and over
Convenient Easily run steps by anyone at anytime

When I rebuilt my blog to run jekyll server in a vagrant system instead of running the site on my local system, the vagrant system made blogging simpler for me precisely for the above reasons.

Motivation

Even if a project has great test coverage, if it is hard to setup the system to run the tests, the project is frustrating to work with. Making a project as easy as possible for others (including myself) to get up and running increases a project’s utility.

There is nothing more frustrating than spending time fighting a system to meet a project’s requirements. It does not help that similar requirements can be tricky to install (i.e. multiple versions of a programming language interpreter, programming libraries, system permissions, etc.)

I follow the Three Cs because that’s what makes it fun for me to get into a project. It’s a bit more overhead up front in a project, but it really saves effort later on.

Post-Install “Notes”

When I looked into applying the same Three Cs concept to one of my Ruby projects, I found I had to include in the README.md file these comments to the user:

  • login: vagrant ssh
  • change to vagrant shared folder: $ cd /vagrant
  • install bundler: $ gem install bundler
  • bundle required gems: $ bundle

The most likely person to need this information will be me, but the information is in the README.md, who reads that??? :-)

These notes are fine, but I asked myself: “Can I do better?” The answer is: yes!

Automating Post-Install Steps

When I look at the commands:

  • host $ vagrant ssh
  • guest $ cd /vagrant
  • guest $ gem install bundler
  • guest $ bundle

These commands are applicable to almost any Ruby project, they are not specific to the project.

This means we can automate these steps! Automated steps don’t have to be in the post-install notes!

So Familiar

Before, when I tried to install RVM via in the shell script option in the Vagrantfile, I ran into some problems.

As the project is using RVM to manage Ruby (so the system still works with the system installed Ruby,) the user needs to execute these post install commands, not root.

This problem seems really familiar. How did I solve it before?

Before, rvm.io gave instructions to use an external script and use the privileged: false option.

Let’s do that again here.

External Shell Script

First, let’s create the external shell script file named bundle.sh in the same directory as the Vagrantfile.

The file contents will be the post-install commands from earlier:

# bundle.sh
#!/usr/bin/env bash

cd /vagrant
gem install bundler
bundle

This is exactly the commands I would type post-install. The first line: #!/usr/bin/env bash figures out the environment version of bash to use (as some systems can have multiple bash install in different locations.) /usr/bin/env is a helper function to figure out what version to use for the current environment setup.

Configure Vagrantfile

Next, I will add to the Vagrantfile I have from before: first installing rvm, then Ruby. Only after installing those items, run the bundle.sh script.

Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"

config.vm.provision :shell, path: "install_rvm.sh", args: "stable" , privileged: false
config.vm.provision :shell, path: "install_ruby.sh", args: "2.2.5" , privileged: false
config.vm.provision :shell, path: "bundle.sh", privileged: false
end

For each of the scripts, use the privileged: false option so the vagrant user executes these commands, not the root user.

Setting Up Project

Now, to bring up a system with a project fully configured:

host $ git clone https://github.com/a-leung/ttt_ruby.git
host $ cd ttt_ruby
host $ vagrant up

These commands will do everything from: downloading the OS image, installing, configuring, getting the stable version of RVM, installing the specified version of Ruby, installing bundler, and getting all the project libraries.

Running Project Tests

Now the system is ready, let’s run tests (as all good projects will have!)

host $ vagrant ssh
guest $ cd /vagrant
guest $ rspec

We’ve gone from cloning the repository to running the project specs with these six simple commands:

  1. git clone <repository>
  2. cd <repository directory>
  3. vagrant up
  4. vagrant ssh
  5. cd /vagrant
  6. <run test suite command>

Setting up any project just got a lot easier:

  • Two of these commands are basic git commands to clone a repository.
  • The next three commands are basic vagrant commands to bring up a guest system, logging into it, and changing to the shared directory.
  • The last command is to run the test suite.

These fit the bill of a project’s Three Cs: clean, consistent, and convenient.

Conclusion

With one additional script to my original Jekyll Vagrant box, any Ruby project is setup and ready for work, after: vagrant up. Just vagrant ssh and then cd /vagrant. Then run anything needed for the project, i.e. rspec.

This is my philosophy for setting up any future project:

  • have a Clean environment for each project
  • each project is Consistent to setup
  • super Convenient to bring up and take down

I really enjoy having such automated setups. It helps me jump in and out of projects because it’s easy and they follow the three Cs: Clean, Consistent, and Convenient.