#!/usr/bin/env bash # # Copyright 2014 David Vazgenovich Shakaryan # Distributed under the terms of the MIT License. # # rbpm (ruby path mangler) # https://git.potato.am/rbpm.git/ # Manage multiple Ruby installations with no black magic. # # In order to prevent environment pollution, the output of this script # is a list of commands which must be sourced. This can be achieved # through a simple shell function: # # rbpm() { source <(/path/to/rbpm.sh "${@}"); } # # Placing the above function inside ~/.bashrc (or equivalent) will load # it upon starting your shell. RBPM_VERSION='0.2' : ${RUBIES_PATH:="${HOME}/.rubies"} _echo() { echo "echo '${@//\'/\'\\\'\'}'" } _export() { echo "export PATH='${PATH//\'/\'\\\'\'}'" echo 'hash -r' } _warn() { echo "echo 'rbpm: ${@//\'/\'\\\'\'}' 1>&2" } _die() { [[ -n "${@}" ]] && _warn "${@}" echo 'false' exit 1 } _populate_dirs() { [[ -d "${RUBIES_PATH}" ]] \ || _die "directory ${RUBIES_PATH} does not exist." # Cannot handle paths containing a newline. Only an idiot would # encounter this in practice. readarray -t dirs < <(shopt -s nullglob; \ printf '%s\0' "${RUBIES_PATH}"/* | sort -zV | xargs -0n1) [[ -n "${dirs}" ]] || _die "directory ${RUBIES_PATH} is empty." } _populate_selected() { local dir dirs IFS=':' read -a dirs <<< "${PATH}" for dir in "${dirs[@]}"; do if [[ "${dir}" == "${RUBIES_PATH}/"* ]]; then dir="${dir%/bin}" selected+=("${dir##*/}") fi done [[ "${#selected[@]}" -ne 0 ]] || return 1 [[ "${#selected[@]}" -eq 1 ]] \ || _warn 'warning: multiple rubies found in PATH.' } _clear() { local dir dirs cdirs IFS=':' read -a dirs <<< "${PATH}" for dir in "${dirs[@]}"; do if [[ "${dir}" == "${RUBIES_PATH}/"* ]]; then _echo "Removing ${dir} from PATH." else cdirs+=("${dir}") fi done [[ "${#cdirs[@]}" -ne "${#dirs[@]}" ]] || return 1 PATH="$(IFS=':'; echo "${cdirs[*]}")" } _add() { _echo "Adding ${1}/bin to PATH." PATH="${1}/bin:${PATH}" } rbpm_ls() { local dir dirs selected str _populate_dirs _populate_selected for dir in "${dirs[@]}"; do str=" ${dir##*/}" [[ "${dir##*/}" == "${selected}" ]] && str="${str/ /*}" _echo "${str}" done } rbpm_get() { local dir selected _populate_selected || _die 'no rubies found in PATH.' for dir in "${selected[@]}"; do _echo "${dir}" done } rbpm_set() { [[ -n "${1}" ]] || _die 'set command requires an argument.' local dir dirs match match_start _populate_dirs for dir in "${dirs[@]}"; do if [[ "${dir##*/}" == *"${1}"* ]]; then match="${dir}" # Match from start of version is preferred. We assume the version # is at the beginning of the directory name or following a hyphen. # This avoids surprising behaviour like `2.1' matching `2.2.1' over # `2.1.0'. if [[ "${dir##*/}" == "${1}"* || "${dir##*/}" == *-"${1}"* ]]; then match_start="${dir}" fi fi done [[ -n "${match}" ]] || _die 'no matching ruby found.' [[ -n "${match_start}" ]] && match="${match_start}" _clear _add "${match}" _export } rbpm_clear() { _clear || _die 'no rubies found in PATH.' _export } rbpm_version() { _echo "rbpm ${RBPM_VERSION}" } rbpm_help() { _echo 'Usage: rbpm [args]' _echo _echo 'Commands:' _echo ' ls List all available rubies.' _echo ' get Display currently selected ruby.' _echo ' set Select specified ruby.' _echo ' clear Unselect any selected rubies.' _echo ' version Display rbpm version.' } case "${1}" in 'ls') rbpm_ls ;; 'get') rbpm_get ;; 'set') rbpm_set "${2}" ;; 'clear') rbpm_clear ;; 'version') rbpm_version ;; *) rbpm_help ;; esac # vim: set sw=4 sts=4 ts=4 et :