glennj's github pages

Logo

learnin'

View My GitHub Profile

Exercism CLI

I like to work at the command line. I like tools (like git for example) that are command subcommand args ... The Exercism CLI works like this.

It is used mainly to download an exercise, and to submit your solution.

Adding functionality to it

One of the downsides of the exercism download subcommand is that it prints the directory where the exercise was downloaded to, but it does not cd to it. If fact it cannot: a child program cannot alter the environment of the parent process.

But, we can write a shell function that wraps the exercism cli. This function can capture the output of the download and change to the new directory.

I’ll assume your shell is bash. You can add this function to your ~/.bashrc

exercism () {
    case "$1" in
        download)
            local out
            readarray -t out < <(command exercism "$@")
            printf '%s\n' "${out[@]}"
            if [[ -d "${out[-1]}" ]]; then
                cd "${out[-1]}" || return 1
            fi
            ;;
        *) command exercism "$@" ;;
    esac
}

If you are using the download subcommand, it will execute the exercism command, capture the output, and check if the last line is a directory. If it is, then cd do it.

Otherwise, it will pass on any arguments to the exercism command.

This demonstrates the shell wrapper technique: examine the arguments, do special actions, otherwise pass control back to the command you’re wrapping.

Running exercise tests

exercism has recently been upgraded with a test subcommand. This will run the unit tests for the exercise you’re currently working on.

It’s a fantastic addition that does “The Right Thing”™ to invoke the test suite for most tracks.

Common Lisp is one such excluded track. Unfortunately Common Lisp does not benefit because there is no single prescribed way to run the tests.

I chose Roswell as my Common Lisp interpreter manager. Here’s an extension of the exercism bash wrapper that adds functionality to the test subcommand.

exercism () {
    case "$1" in
        download)
            local out
            readarray -t out < <(command exercism "$@")
            printf '%s\n' "${out[@]}"
            if [[ -d "${out[-1]}" ]]; then
                cd "${out[-1]}" || return 1
            fi
            ;;
        test)
            if [[ $PWD =~ "$(command exercism workspace)/common-lisp/"([^/]+) ]]
            then
                local exercise=${BASH_REMATCH[1]}
                ros run --load "${exercise}-test" \
                        --eval "(uiop:quit (if (${exercise}-test:run-tests) 0 1))"
            else
                command exercism "$@"
            fi
            ;;
        *) command exercism "$@" ;;
    esac
}

That uses a shell regular expression to see if you’re current directory is a Common Lisp exercise. If so, it will say the ros incantation to run the tests. If not, it will run the default exercism test for wherever you are.

The Fish shell

I don’t use bash as my interactive shell, I use fish. It has superior completions and autosuggestions.

I took the above concepts about extending exercism and built a whole lot of new functionality in my fish wrapper around exercism.