Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Testing your gem against multiple rubies and rails versions with RVM

I recently wanted to make it easier for contributors to ActiveHash to test their changes against multiple versions of Rails, with multiple versions of Ruby. My stories looked like this:

As a contributor
I want to be able to run `bundle install`, then quickly run the suite spec suite against the latest released version of rails
So that I can develop quickly using a familiar workflow

As a gem maintainer
I want to be able to run the spec suite against 3 different versions of ruby, each with 5 different versions of rails
So that I can release the gem with confidence that I'm not going to break people's apps

In this post I’ll explain how I did that with a (relatively) simple shell script.

The final script looks like this:

#!/bin/sh

set -e

if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
  source "$HOME/.rvm/scripts/rvm"
elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
  source "/usr/local/rvm/scripts/rvm"
else
  printf "ERROR: An RVM installation was not found.n"
fi

function run {
  gem list --local bundler | grep bundler || gem install bundler --no-ri --no-rdoc

  echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.2...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.2 bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.5...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.5 bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.11...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.11 bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against the latest released version of activesupport / activerecord...'
  ACTIVE_HASH_ACTIVERECORD_VERSION="" bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against edge activesupport / activerecord...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=edge bundle update activerecord activesupport
  bundle exec rspec spec
}

rvm use ruby-1.8.7@active_hash --create
run

rvm use ree-1.8.7@active_hash --create
run

rvm use ruby-1.9.2@active_hash --create
run

echo 'Success!'

Here’s a breakdown of what’s happening:

First the script tells the shell to exit immediately if any of the commands in the script fail:

set -e

This is helpful for continuous integration servers especially, so they fail fast. Next, the script loads RVM, looking first in the home directory, then for a system install:

if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
  source "$HOME/.rvm/scripts/rvm"
elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
  source "/usr/local/rvm/scripts/rvm"
else
  printf "ERROR: An RVM installation was not found.n"
fi

The next section defines a function which, when invoked, runs the spec suite against 5 different versions of activerecord/activesupport:

function run {
  gem list --local bundler | grep bundler || gem install bundler --no-ri --no-rdoc

  echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.2...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.2 bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.5...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.5 bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.11...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.11 bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against the latest released version of activesupport / activerecord...'
  ACTIVE_HASH_ACTIVERECORD_VERSION="" bundle update activerecord
  bundle exec rspec spec

  echo 'Running bundle exec rspec spec against edge activesupport / activerecord...'
  ACTIVE_HASH_ACTIVERECORD_VERSION=edge bundle update activerecord activesupport
  bundle exec rspec spec
}

(I know, it’s not particularly DRY – pulling out the duplication into other functions is an exercise for the reader…)

This function sets an environment variable to describe the intended gem versions, then updates bundler and runs the suite. To make this work, I added the following to my Gemfile:

source :gemcutter
gemspec

group :development do
  activerecord_version = ENV['ACTIVE_HASH_ACTIVERECORD_VERSION']

  if activerecord_version == "edge"
    git "git://github.com/rails/rails.git" do
      gem "activerecord"
      gem "activesupport"
    end
  elsif activerecord_version && activerecord_version.strip != ""
    gem "activerecord", activerecord_version
  else
    gem "activerecord"
  end
  # other gems...
end

The Gemfile checks the ACTIVE_HASH_ACTIVERECORD_VERSION environment variable, and sets the correct gem version/git repo accordingly. If it gets a blank ACTIVE_HASH_ACTIVERECORD_VERSION it does not set the version in the Gemfile, so when the bash script calls bundle update activerecord it conveniently fetches the latest released version.

If you execute bundle update, it won’t have a ACTIVE_HASH_ACTIVERECORD_VERSION so it will fetch the latest, so the normal workflow still works as expected.

Finally, it runs the function in each of 3 rubies:

rvm use ruby-1.8.7@active_hash --create
run

rvm use ree-1.8.7@active_hash --create
run

rvm use ruby-1.9.2@active_hash --create
run

echo 'Success!'

RVM allows you to run a command with all of your installed rubies, but in this case I wanted to target 3 particular versions, and ensure that they work. RVM will fail if you don’t have the latest version of each ruby installed, and prompt you to install it. And since set -e was called at the top of the file, there’s no need to check the return code of any of the functions because the script will fail if any individual command fails.

If you are a gem maintainer, this is an easy way to ensure that your gem is highly compatible with standard Ruby / Rails setups.

Comments
  1. grosser says:

    Really like this, the set -e part is great, and just updating a single gem is also very fast / will not mess with all other gems in the Gemfile.lock.

    I normally keep different gemfiles for each scenario and run them via rvm ree exec rake ; rvm 1.9.2 exec rake

    task :rails2 do
    sh “cd spec/rails2 && rspec ../../spec”
    end

    task :default do
    sh “rake spec && rake rails2″
    end

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *