Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 207 lines (190 sloc) 7.27 KB
#!/bin/bash
{
# this block disables xtrace for lines from this file,
# while keeping it enabled for user-land reactive code
shopt -s expand_aliases
alias _suppress_xtrace='{ local old_opts="$-"; set $X_OPT; } 2> /dev/null'
alias _restore_xtrace='_restore_opt $old_opts x'
function _restore_opt() {
# restore the given opt to its previous state
local prev_opts="$1"
local opt_to_restore="$2"
if [[ "$prev_opts" == *$opt_to_restore* ]]; then
# opt was previously enabled, to re-enable it
set -$opt_to_restore
else
# opt was previously disabled, to re-disable it
set +$opt_to_restore
fi
}
if [[ "$CHARMS_REACTIVE_TRACE" != 'true' ]]; then
X_OPT="+x"
else
# we are debugging charms.reactive.sh, so enable xtrace internally
X_OPT="-x"
fi
# save options as of import
import_opts="$-"
set +x
} 2> /dev/null # suppress xtrace during import (braces to hide entire block from xtracing)
export PATH=$PATH:$CHARM_DIR/bin
export PYTHONPATH=${PYTHONPATH-}:$CHARM_DIR/lib
if [[ "$0" == "$BASH_SOURCE" ]]; then
echo 'This file contains helpers for developing reactive charms in Bash.'
echo
echo 'Usage:'
echo
echo ' source `which reactive.sh`'
echo
echo ' @when db.ready cache.ready'
echo ' function write_configuration() {'
echo ' db_dsn=$(relation_call --flag db.ready uri)'
echo ' cache_uri=$(relation_call --flag cache.ready uri)'
echo ' chlp render_template db_dsn=$db_dsn cache_uri=$cache_uri'
echo ' }'
echo
echo ' @when_not db.ready cache.ready'
echo ' function report_blocked() {'
echo ' status-set blocked "Waiting for db and/or cache"'
echo ' }'
echo
echo ' reactive_handler_main'
echo
echo 'Helpers available:'
echo
echo ' @hook PATTERN Run decorated function if hook matches'
echo ' @when STATES Run decorated function if states are active'
echo ' @when_not STATES Run decorated function if states not active'
echo ' @when_file_changed FILENAME Run decorated function if file(s) changed'
echo ' @only_once Run decorated function only once'
echo ' set_flag Set / activate a flag'
echo ' clear_flag Remove a flag'
echo ' relation_call Call a method on a relation by flag or relation name'
echo ' all_flags_set Check all flags are active'
echo ' any_flags_set Check any flags are active'
echo ' name_relation_get Get a relation value by relation name (vs ID)'
exit 0
fi
REACTIVE_ACTION="${1-'--test'}"
REACTIVE_ARGS="${2-}"
declare -A REACTIVE_HANDLERS
declare -A REACTIVE_TESTS
function _get_decorated() {
# find the name of the "decorated" function, given
# that there may be more decorators between us and it
filename=${BASH_SOURCE[2]}
lineno=$((BASH_LINENO[1]+1))
last_decorator=yes
while sed "${lineno}q;d" $filename | grep -qE "^\s*@"; do
# there are other decorators between us and the function
last_decorator=no
((lineno++))
done
func=$(sed "${lineno}q;d" $filename | sed "s/\(function\)\? *\(.*\) *() *{/\2/")
if [[ "$old_opts" == *x* && $last_decorator == yes ]]; then
# pseudo-xtrace function definition when xtrace is active, since
# the @decorator line will be logged anyway (can't suppress it)
>&2 echo "+ function $func() { ... }"
fi
}
function @decorator() {
_suppress_xtrace
_get_decorated
handler_id="$filename:$lineno:$func"
# this "decorates" a function using source-code introspection, registering
# the "decorated" function as a reactive handler with a set of preconditions
# register handler_ids
if [ ! ${REACTIVE_HANDLERS[$func]+_} ]; then
REACTIVE_HANDLERS[$func]="$handler_id"
fi
# register tests
test_name="$1"; shift
test_args=$(printf '"%s" ' "$@")
if [ ! ${REACTIVE_TESTS[$func]+_} ]; then
REACTIVE_TESTS[$func]="'$func' '$handler_id' '$test_name' '$test_args'"
else
REACTIVE_TESTS[$func]="${REACTIVE_TESTS[$func]} '$test_name' '$test_args'"
fi
_restore_xtrace
}
function reactive_handler_main() {
_suppress_xtrace
if [[ "$REACTIVE_ACTION" == "--test" ]]; then
>&2 echo "Running reactive_handler_main for $(basename $0) (test)"
to_invoke=$(charms.reactive test "${REACTIVE_TESTS[@]}")
if [[ -n "$to_invoke" ]]; then
echo $to_invoke
>&2 echo "Will invoke: $to_invoke"
>&2 echo "End reactive_handler_main (test)"
exit 0
else
>&2 echo "Nothing to invoke"
>&2 echo "End reactive_handler_main (test)"
exit 1
fi
elif [[ "$REACTIVE_ACTION" == "--invoke" ]]; then
>&2 echo "Running reactive_handler_main for $(basename $0) (invoke)"
invoked=()
for handler in ${REACTIVE_ARGS//,/ }; do
>&2 echo "Invoking bash reactive handler: $handler"
eval "_restore_xtrace; $handler; _suppress_xtrace"
invoked+=("${REACTIVE_HANDLERS[$handler]}")
done
charms.reactive mark_invoked "${invoked[@]}"
>&2 echo "End reactive_handler_main (invoke)"
fi
_restore_xtrace
}
# some helpers and syntactic sugar
alias @hook='@decorator hook'
alias @when='@decorator when'
alias @when_all='@decorator when_all'
alias @when_any='@decorator when_any'
alias @when_not='@decorator when_not'
alias @when_none='@decorator when_none'
alias @when_not_all='@decorator when_not_all'
alias @when_file_changed='@decorator when_file_changed'
alias @only_once='@decorator only_once'
alias set_flag='charms.reactive set_flag'
alias clear_flag='charms.reactive clear_flag'
alias relation_call='charms.reactive relation_call' # Call a method on a named relation
alias all_flags_set='charms.reactive all_flags_set'
alias any_flags_set='charms.reactive any_flags_set'
# DEPRECATED
alias set_state='charms.reactive set_flag'
alias remove_state='charms.reactive clear_flag'
alias all_states='charms.reactive all_flags_set'
alias any_states='charms.reactive any_flags_set'
# DEPRECATED: Use `relation_call --flag <flag> <method>` instead
function state_relation_call() {
_suppress_xtrace
# Call a method on the relation implementation associated with the
# given active state.
flag=$1
method=$2
shift; shift
charms.reactive relation_call --flag $flag $method "$@"
_restore_xtrace
}
function name_relation_get() {
_suppress_xtrace
# Helper function around relation-get that accepts a relation name in place
# of a relation ID.
#
# Usage: name_relation_get <relation_name> <unit_name> [<key>] [--format=<format>]
# Note: The --format option must come at the end, if present
relation_name="$1"
unit_name="$2"
key="$3"
if [[ -z "$key" || "$key" == --* ]]; then
key="-"
shift; shift
else
shift; shift; shift
fi
relid="$(charms.reactive relation_id "$relation_name" "$unit_name")"
relation-get -r "$relid" "$key" "$unit_name" "$@"
_restore_xtrace
}
# re-enable xtrace for user code, if it was enabled
_restore_opt $import_opts x