First the test file.

Tests

Setting up our working directory

After the bats shebang and the loading of test_helper, the first block of code is:

setup() {
  mkdir -p "$RBENV_TEST_DIR"
  cd "$RBENV_TEST_DIR"
}

This is the implementation of our setup hook, which in this case makes a new directory and navigates into it.

Sad path- testing a response to incorrect usage

Our first test is:

@test "invocation without 2 arguments prints usage" {
  run rbenv-version-file-write
  assert_failure "Usage: rbenv version-file-write <file> <version>"
  run rbenv-version-file-write "one" ""
  assert_failure
}

This is a sad-path test. According to the test’s description, this command always expects two arguments.

We run the command with no setup and no arguments, and we assert that the command fails with an error message which tells the user to specify both a filename and a version number. We then run the command again with two arguments, one of which is an empty string, and again assert that the command fails.

Sad path- trying to set a version which isn’t installed

Next test:

@test "setting nonexistent version fails" {
  assert [ ! -e ".ruby-version" ]
  run rbenv-version-file-write ".ruby-version" "1.8.7"
  assert_failure "rbenv: version \`1.8.7' not installed"
  assert [ ! -e ".ruby-version" ]
}

In this test:

  • We first assert that the .ruby-version file does not exist, as a sanity check.
  • We then run the version-file-write command, passing that same .ruby-version file as argument #1 and the stringified version number “1.8.7” as argument #2.
  • We then assert that the command fails with an error message saying the requested version number is not installed.
  • Lastly, we assert that the .ruby-version file has not been created since we last checked for its existence.

Happy path- writing to a version file other than .ruby-version

Last test:

@test "writes value to arbitrary file" {
  mkdir -p "${RBENV_ROOT}/versions/1.8.7"
  assert [ ! -e "my-version" ]
  run rbenv-version-file-write "${PWD}/my-version" "1.8.7"
  assert_success ""
  assert [ "$(cat my-version)" = "1.8.7" ]
}

In this test:

  • As a setup step, we create a fake Ruby installation for Ruby v1.8.7, and we assert that a version file named my-version does not yet exist.
  • We then run the command with the my-version filename and version “1.8.7” as arguments.
  • This time we assert that the command was successful and that the contents of the newly-created version file is equal to “1.8.7”.

This 2nd assertion implicitly tests that the new version file also, in fact, exists.

That’s all for the tests, now on to the code itself.

Code

First up:

#!/usr/bin/env bash
# Usage: rbenv version-file-write <file> <version>

set -e
[ -n "$RBENV_DEBUG" ] && set -x

As usual, we have:

  • Bash shebang
  • “Usage” comments
  • set -e to set “exit-on-error” mode.
  • set -x to set “verbose” mode.

Setting variables for the version file and version

Next block of code:

RBENV_VERSION_FILE="$1"
RBENV_VERSION="$2"

Here we just store off the first two arguments (aka the version filename and the version number) in appropriately-named variables.

Handling missing arguments

Next block of code:

if [ -z "$RBENV_VERSION" ] || [ -z "$RBENV_VERSION_FILE" ]; then
  rbenv-help --usage version-file-write >&2
  exit 1
fi

If the user failed to provide either the version filename or the version number itself, then we print the usage instructions for this file and exit with an error return code.

Ensuring the version is installed

Next block of code:

# Make sure the specified version is installed.
rbenv-prefix "$RBENV_VERSION" >/dev/null

Here we validate that the Ruby version number that the user passed as argument #2 corresponds to a valid Ruby version that exists on the user’s machine.

If not, an error will be raised, and we’ll exit. We can see this error being raised in the rbenv-prefix command, at this block of code.

Writing the version number to the version file

Last block of code:

# Write the version out to disk.
echo "$RBENV_VERSION" > "$RBENV_VERSION_FILE"

Now that we’ve verified the Ruby version provided by the user is valid, we simply write that version to the file whose name the user also provided.

And that’s it for version-file-write! Next file.