mirror of
https://github.com/tests-always-included/mo.git
synced 2024-12-18 16:27:52 +00:00
Initial commit
This commit is contained in:
commit
1151ec0a9e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.swp
|
||||||
|
tests/*.diff
|
7
LICENSE.md
Normal file
7
LICENSE.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by contributors", in the same place and form as other third-party acknowledgments. Alternately, this acknowledgment may appear in the software itself, in the same form and location as other such third-party acknowledgments.
|
55
README.md
Normal file
55
README.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
Mo - Mustache Templates in Bash
|
||||||
|
===============================
|
||||||
|
|
||||||
|
[Mustache] templates are simple, logic-less templates. Because of their simplicity, they are able to be ported to many languages. The syntax is quite simple.
|
||||||
|
|
||||||
|
Hello, {{NAME}}.
|
||||||
|
|
||||||
|
I hope your {{TIME_PERIOD}} was fun.
|
||||||
|
|
||||||
|
Let's try using this with some data in bash. Save those lines to `trip.txt` and run a command like this:
|
||||||
|
|
||||||
|
NAME=Tyler TIME_PERIOD=weekend ./mo weekend-trip.txt
|
||||||
|
|
||||||
|
Your result?
|
||||||
|
|
||||||
|
Hello, Tyler.
|
||||||
|
|
||||||
|
I hope your weekend was fun.
|
||||||
|
|
||||||
|
This bash version supports conditionals, functions (both as filters and as values), as well as indexed arrays (for iteration). You are able to leverage these additional features by adding more information into the environment. It is easiest to do this when you source `mo`. See the [demo scripts](demo/) for further examples.
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Bash 3.x (the aim is to make it work on Macs)
|
||||||
|
* The "coreutils" package (`basename` and `cat`)
|
||||||
|
* ... that's it. Why? Because bash **can**!
|
||||||
|
|
||||||
|
|
||||||
|
Concessions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
I admit that implementing everything in bash just doesn't make a lot of sense. For example, the following things just don't work.
|
||||||
|
|
||||||
|
* Bash does not support nested structures like fancy objects. The best you can do are arrays. I'm not able to add super complex structures to bash - it's just a shell after all!
|
||||||
|
* There's no "top level" object that `{.}` refers to in `mo`. In other languages you can say the data for the template is a string. That's ok because using `{.}` when processing a top level scope is rare. Using `{.}` works great when iterating over an array.
|
||||||
|
* HTML encoding is not built into `mo`. The `{{{...}}}` and `{{...}}` tags both work. `echo '{{TEST}}' | TEST='<b>' mo` will give you "`<b>`" instead of "`>b<`".
|
||||||
|
* You must make sure the data is in the environment when `mo` runs. The easiest way to do that is to source `mo` in your shell script after setting up lots of other environment variables / functions.
|
||||||
|
* Associative arrays are not addressable via their index. You can't use `{{VARIABLE_NAME.INDEX_NAME}}` and expect it to work. Associative arrays aren't supported in Bash 3.
|
||||||
|
|
||||||
|
|
||||||
|
Developing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Check out the code and hack away. Please add tests to show off bugs before fixing them. New functionality should also be covered by a test.
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
This program is licensed under an MIT license with an additional non-advertising clause. See [LICENSE.md](LICENSE.md) for the full text.
|
||||||
|
|
||||||
|
|
||||||
|
[Mustache]: https://mustache.github.io/
|
23
demo/important-file
Executable file
23
demo/important-file
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
date-string() { date; }
|
||||||
|
wrapper() { echo -n "*** $1 ***"; }
|
||||||
|
|
||||||
|
IP=127.0.0.1
|
||||||
|
ALLOWED_HOSTS=( 192.168.0.1 192.168.0.2 192.168.0.3 )
|
||||||
|
|
||||||
|
cat <<EOF | . mo
|
||||||
|
# {{#wrapper}}OH SO IMPORTANT{{/wrapper}}
|
||||||
|
# This file automatically generated at {{date-string}}
|
||||||
|
home_ip={{IP}}
|
||||||
|
|
||||||
|
# ALLOWED HOSTS
|
||||||
|
{{#ALLOWED_HOSTS}}allowed_host={{.}}
|
||||||
|
{{/ALLOWED_HOSTS}}{{^ALLOWED_HOSTS}}# No allowed hosts
|
||||||
|
{{/ALLOWED_HOSTS}}
|
||||||
|
|
||||||
|
# DENIED HOSTS
|
||||||
|
{{#DENIED_HOSTS}}denied_host={{.}}
|
||||||
|
{{/DENIED_HOSTS}}{{^DENIED_HOSTS}}# No denied hosts
|
||||||
|
{{/DENIED_HOSTS}}
|
||||||
|
EOF
|
358
mo
Executable file
358
mo
Executable file
@ -0,0 +1,358 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Return 0 if the passed name is a function. Function names are captured
|
||||||
|
# at the start of the program and are stored in the $MUSTACHE_FUNCTIONS array.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Name to check if it's a function
|
||||||
|
#
|
||||||
|
# Return code:
|
||||||
|
# 0 if the name is a function, 1 otherwise
|
||||||
|
mustache-is-function() {
|
||||||
|
local NAME
|
||||||
|
|
||||||
|
for NAME in ${MUSTACHE_FUNCTIONS[@]}; do
|
||||||
|
if [[ "$NAME" == "$1" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Process a chunk of content some number of times.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Destination variable name for the modified content
|
||||||
|
# $2: Content to parse and reparse and reparse
|
||||||
|
# $3: Ending tag for the parser
|
||||||
|
# $4-*: Values to insert into the parsed content
|
||||||
|
mustache-loop() {
|
||||||
|
local CONTENT DEST_NAME END_TAG MODIFIED_CONTENT
|
||||||
|
|
||||||
|
DEST_NAME="$1"
|
||||||
|
CONTENT="$2"
|
||||||
|
END_TAG="$3"
|
||||||
|
shift 3
|
||||||
|
|
||||||
|
# This MUST loop at least once or assignment back to ${!DEST_NAME}
|
||||||
|
# will not work.
|
||||||
|
while [[ ${#@} -gt 0 ]]; do
|
||||||
|
mustache-parse MODIFIED_CONTENT "$CONTENT" "$END_TAG" "$1"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
local "$DEST_NAME" && mustache-indirect "$DEST_NAME" "$MODIFIED_CONTENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Parse a block of text
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Where to store content left after parsing
|
||||||
|
# $2: Block of text to change
|
||||||
|
# $3: Stop at this closing tag (eg "/NAME")
|
||||||
|
# $4: Current value (what {{.}} will mean)
|
||||||
|
mustache-parse() {
|
||||||
|
# Keep naming variables MUSTACHE_* here to not overwrite needed variables
|
||||||
|
# used in the string replacements
|
||||||
|
local MUSTACHE_CONTENT MUSTACHE_CURRENT MUSTACHE_END_TAG MUSTACHE_TAG
|
||||||
|
|
||||||
|
MUSTACHE_END_TAG="$3"
|
||||||
|
MUSTACHE_CURRENT="$4"
|
||||||
|
|
||||||
|
# Find open tags
|
||||||
|
mustache-split MUSTACHE_CONTENT "$2" '{{' '}}'
|
||||||
|
|
||||||
|
while [[ ${#MUSTACHE_CONTENT[@]} -gt 1 ]]; do
|
||||||
|
echo -n "${MUSTACHE_CONTENT[0]}"
|
||||||
|
mustache-trim MUSTACHE_TAG "${MUSTACHE_CONTENT[1]}"
|
||||||
|
MUSTACHE_CONTENT="${MUSTACHE_CONTENT[2]}"
|
||||||
|
|
||||||
|
case "$MUSTACHE_TAG" in
|
||||||
|
'#'*)
|
||||||
|
# Loop, if/then, or pass content through function
|
||||||
|
# Sets context
|
||||||
|
mustache-trim MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||||
|
|
||||||
|
if mustache-test "$MUSTACHE_TAG"; then
|
||||||
|
# Show / loop / pass through function
|
||||||
|
if mustache-is-function "$MUSTACHE_TAG"; then
|
||||||
|
# This is slower - need to parse twice to avoid
|
||||||
|
# subshells. First, pass content to function but
|
||||||
|
# the updated MUSTACHE_CONTENT is lost due to subshell.
|
||||||
|
$MUSTACHE_TAG "$(mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG")"
|
||||||
|
|
||||||
|
# Secondly, update MUSTACHE_CONTENT but do not output.
|
||||||
|
mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" > /dev/null 2>&1
|
||||||
|
elif mustache-is-array "$MUSTACHE_TAG"; then
|
||||||
|
eval 'mustache-loop MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" "${'"$MUSTACHE_TAG"'[@]}"'
|
||||||
|
else
|
||||||
|
mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" "$(mustache-show "$MUSTACHE_TAG")"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Do not show
|
||||||
|
mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" > /dev/null
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
'>'*)
|
||||||
|
# Load partial - get name of file relative to cwd
|
||||||
|
mustache-trim MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||||
|
|
||||||
|
# Execute in subshell to preserve current cwd
|
||||||
|
(
|
||||||
|
cd "$(dirname "$MUSTACHE_TAG")"
|
||||||
|
MUSTACHE_TAG=$(basename "$MUSTACHE_TAG")
|
||||||
|
MUSTACHE_TAG=$(cat "$MUSTACHE_TAG" 2>/dev/null)
|
||||||
|
mustache-parse MUSTACHE_TAG "$MUSTACHE_TAG" "" "$MUSTACHE_CURRENT"
|
||||||
|
echo -n $MUSTACHE_TAG
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
|
||||||
|
'/'*)
|
||||||
|
# Closing tag - If we hit MUSTACHE_END_TAG, we're done.
|
||||||
|
mustache-trim MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||||
|
|
||||||
|
if [[ "$MUSTACHE_TAG" == "$MUSTACHE_END_TAG" ]]; then
|
||||||
|
# Tag hit - done
|
||||||
|
local "$1" && mustache-indirect "$1" "$MUSTACHE_CONTENT"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the tag does not match, we ignore this tag
|
||||||
|
;;
|
||||||
|
|
||||||
|
'^'*)
|
||||||
|
# Display section if named thing does not exist
|
||||||
|
mustache-trim MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||||
|
|
||||||
|
if mustache-test "$MUSTACHE_TAG"; then
|
||||||
|
# Do not show
|
||||||
|
mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" > /dev/null 2>&1
|
||||||
|
else
|
||||||
|
# Show
|
||||||
|
mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
'!'*)
|
||||||
|
# Comment - ignore the tag entirely
|
||||||
|
;;
|
||||||
|
|
||||||
|
.)
|
||||||
|
# Current content (environment variable or function)
|
||||||
|
echo -n "$MUSTACHE_CURRENT"
|
||||||
|
;;
|
||||||
|
|
||||||
|
'{'*)
|
||||||
|
# Unescaped - split on }}} not }}
|
||||||
|
MUSTACHE_CONTENT="${MUSTACHE_TAG:1}"'}}'"$MUSTACHE_CONTENT"
|
||||||
|
mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '}}}'
|
||||||
|
mustache-trim MUSTACHE_TAG "${MUSTACHE_CONTENT[0]}"
|
||||||
|
MUSTACHE_CONTENT="${MUSTACHE_CONTENT[1]}"
|
||||||
|
|
||||||
|
# Now show the value
|
||||||
|
mustache-show "$MUSTACHE_TAG"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
# Normal environment variable or function call
|
||||||
|
mustache-show "$MUSTACHE_TAG"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '{{' '}}'
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -n "${MUSTACHE_CONTENT[0]}"
|
||||||
|
local "$1" && mustache-indirect "$1" ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Show an environment variable or the output of a function.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Name of environment variable or function
|
||||||
|
mustache-show() {
|
||||||
|
if mustache-is-function "$1"; then
|
||||||
|
$1
|
||||||
|
else
|
||||||
|
echo -n "${!1}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Returns 0 (success) if the named thing is a function or if it is a non-empty
|
||||||
|
# environment variable.
|
||||||
|
#
|
||||||
|
# Do not use unprefixed variables here if possible as this needs to check
|
||||||
|
# if any name exists in the environment
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Name of environment variable or function
|
||||||
|
#
|
||||||
|
# Return code:
|
||||||
|
# 0 if the name is not empty, 1 otherwise
|
||||||
|
mustache-test() {
|
||||||
|
# Test for functions
|
||||||
|
mustache-is-function "$1" && return 0
|
||||||
|
|
||||||
|
if mustache-is-array "$1"; then
|
||||||
|
# Arrays must have at least 1 element
|
||||||
|
eval '[[ ${#'"$1"'} -gt 0 ]]' && return 0
|
||||||
|
else
|
||||||
|
# Environment variables must not be empty
|
||||||
|
[[ ! -z "${!1}" ]] && return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Determine if a given environment variable exists and if it is an array.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Name of environment variable
|
||||||
|
#
|
||||||
|
# Return code:
|
||||||
|
# 0 if the name is not empty, 1 otherwise
|
||||||
|
mustache-is-array() {
|
||||||
|
local MUSTACHE_TEST
|
||||||
|
|
||||||
|
MUSTACHE_TEST=$(declare -p "$1" 2>/dev/null) || return 1
|
||||||
|
[[ "${MUSTACHE_TEST:0:10}" == "declare -a" ]] || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Trim leading and trailing whitespace from a string
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Name of variable to store trimmed string
|
||||||
|
# $2: The string
|
||||||
|
mustache-trim() {
|
||||||
|
local CR CURRENT MODIFIED NEEDLE NL TAB SPACE VAR
|
||||||
|
|
||||||
|
CR="$'\r'"
|
||||||
|
NL="$'\n'"
|
||||||
|
TAB="$'\t'"
|
||||||
|
SPACE=" "
|
||||||
|
CURRENT="$2"
|
||||||
|
LAST=""
|
||||||
|
|
||||||
|
while [[ "$CURRENT" != "$LAST" ]]; do
|
||||||
|
LAST="$CURRENT"
|
||||||
|
|
||||||
|
for VAR in CR NL TAB SPACE; do
|
||||||
|
NEEDLE="${!VAR}"
|
||||||
|
CURRENT="${CURRENT/#$NEEDLE}"
|
||||||
|
CURRENT="${CURRENT/%$NEEDLE}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
local "$1" && mustache-indirect "$1" "$CURRENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Split a larger string into an array
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Destination variable
|
||||||
|
# $2: String to split
|
||||||
|
# $3: Starting delimeter
|
||||||
|
# $4: Ending delimeter (optional)
|
||||||
|
mustache-split() {
|
||||||
|
local POS RESULT
|
||||||
|
|
||||||
|
RESULT=( "$2" )
|
||||||
|
mustache-find-string POS "${RESULT[0]}" "$3"
|
||||||
|
|
||||||
|
if [[ $POS -ne -1 ]]; then
|
||||||
|
# The first delimeter was found
|
||||||
|
RESULT[1]="${RESULT[0]:$POS + ${#3}}"
|
||||||
|
RESULT[0]="${RESULT[0]:0:$POS}"
|
||||||
|
|
||||||
|
if [[ ! -z "$4" ]]; then
|
||||||
|
mustache-find-string POS "${RESULT[1]}" "$4"
|
||||||
|
|
||||||
|
if [[ $POS -ne -1 ]]; then
|
||||||
|
# The second delimeter was found
|
||||||
|
RESULT[2]="${RESULT[1]:$POS + ${#4}}"
|
||||||
|
RESULT[1]="${RESULT[1]:0:$POS}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local "$1" && mustache-indirect-array "$1" "${RESULT[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find the first index of a substring
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Destination variable
|
||||||
|
# $2: Haystack
|
||||||
|
# $3: Needle
|
||||||
|
mustache-find-string() {
|
||||||
|
local POS STRING
|
||||||
|
|
||||||
|
STRING="${2%%$3*}"
|
||||||
|
[[ "$STRING" == "$2" ]] && POS=-1 || POS=${#STRING}
|
||||||
|
local "$1" && mustache-indirect "$1" $POS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Send a variable up to caller of a function
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Variable name
|
||||||
|
# $2: Value
|
||||||
|
mustache-indirect() {
|
||||||
|
unset -v "$1"
|
||||||
|
printf -v "$1" '%s' "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Send an array up to caller of a function
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Variable name
|
||||||
|
# $2-*: Array elements
|
||||||
|
mustache-indirect-array() {
|
||||||
|
unset -v "$1"
|
||||||
|
eval $1=\(\"\${@:2}\"\)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Return the content to parse. Can be a list of partials for files or
|
||||||
|
# the content from stdin.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1: Variable name to assign this content back as
|
||||||
|
# $2-*: File names (optional)
|
||||||
|
mustache-get-content() {
|
||||||
|
local CONTENT FILENAME TARGET
|
||||||
|
|
||||||
|
TARGET="$1"
|
||||||
|
shift
|
||||||
|
if [[ ${#@} -gt 0 ]]; then
|
||||||
|
CONTENT=""
|
||||||
|
|
||||||
|
for FILENAME in ${1+"$@"}; do
|
||||||
|
CONTENT="$CONTENT"'{{>'"$FILENAME"'}}'
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# Workaround to avoid newlines being gobbled by the subshell
|
||||||
|
CONTENT="$(cat -; echo .)"
|
||||||
|
CONTENT=${CONTENT:0: -1}
|
||||||
|
fi
|
||||||
|
|
||||||
|
local "$TARGET" && mustache-indirect "$TARGET" "$CONTENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Save the list of functions as an array
|
||||||
|
MUSTACHE_FUNCTIONS=$(declare -F)
|
||||||
|
MUSTACHE_FUNCTIONS=( ${MUSTACHE_FUNCTIONS//declare -f /} )
|
||||||
|
mustache-get-content MUSTACHE_CONTENT ${1+"$@"}
|
||||||
|
mustache-parse MUSTACHE_CONTENT "$MUSTACHE_CONTENT"
|
30
run-tests
Executable file
30
run-tests
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd "$(dirname $0)"
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
for TEST in tests/*.expected; do
|
||||||
|
BASE="${TEST%.expected}"
|
||||||
|
|
||||||
|
echo -n "$BASE ... "
|
||||||
|
(
|
||||||
|
. "${BASE}.env"
|
||||||
|
. ./mo "${BASE}.template"
|
||||||
|
) | diff -wU5 - "${TEST}" > "${BASE}.diff"
|
||||||
|
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo "FAIL"
|
||||||
|
FAIL=$(( $FAIL + 1 ))
|
||||||
|
else
|
||||||
|
echo "ok"
|
||||||
|
PASS=$(( $PASS + 1 ))
|
||||||
|
rm "${BASE}.diff"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pass: $PASS"
|
||||||
|
echo "Fail: $FAIL"
|
||||||
|
[[ $FAIL -gt 0 ]] && exit 1
|
1
tests/array.env
Normal file
1
tests/array.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
repo=( "resque" "hub" "rip" )
|
6
tests/array.expected
Normal file
6
tests/array.expected
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
<b>resque</b>
|
||||||
|
|
||||||
|
<b>hub</b>
|
||||||
|
|
||||||
|
<b>rip</b>
|
3
tests/array.template
Normal file
3
tests/array.template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{#repo}}
|
||||||
|
<b>{{.}}</b>
|
||||||
|
{{/repo}}
|
0
tests/comment-newline.env
Normal file
0
tests/comment-newline.env
Normal file
1
tests/comment-newline.expected
Normal file
1
tests/comment-newline.expected
Normal file
@ -0,0 +1 @@
|
|||||||
|
<h1>Today.</h1>
|
4
tests/comment-newline.template
Normal file
4
tests/comment-newline.template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<h1>Today{{! ignore me
|
||||||
|
and this can
|
||||||
|
run through multiple
|
||||||
|
lines}}.</h1>
|
0
tests/comment.env
Normal file
0
tests/comment.env
Normal file
1
tests/comment.expected
Normal file
1
tests/comment.expected
Normal file
@ -0,0 +1 @@
|
|||||||
|
<h1>Today.</h1>
|
1
tests/comment.template
Normal file
1
tests/comment.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
<h1>Today{{! ignore me }}.</h1>
|
1
tests/false-list.env
Normal file
1
tests/false-list.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
person=""
|
1
tests/false-list.expected
Normal file
1
tests/false-list.expected
Normal file
@ -0,0 +1 @@
|
|||||||
|
Shown.
|
4
tests/false-list.template
Normal file
4
tests/false-list.template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Shown.
|
||||||
|
{{#person}}
|
||||||
|
Never shown!
|
||||||
|
{{/person}}
|
4
tests/function.env
Normal file
4
tests/function.env
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
name=Willy
|
||||||
|
wrapped() {
|
||||||
|
echo "<b>$1</b>";
|
||||||
|
}
|
2
tests/function.expected
Normal file
2
tests/function.expected
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<b>
|
||||||
|
Willy is awesome.</b>
|
3
tests/function.template
Normal file
3
tests/function.template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{#wrapped}}
|
||||||
|
{{name}} is awesome.
|
||||||
|
{{/wrapped}}
|
1
tests/inverted.env
Normal file
1
tests/inverted.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
repo=()
|
3
tests/inverted.expected
Normal file
3
tests/inverted.expected
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
No repos :(
|
6
tests/inverted.template
Normal file
6
tests/inverted.template
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{{#repo}}
|
||||||
|
<b>{{.}}</b>
|
||||||
|
{{/repo}}
|
||||||
|
{{^repo}}
|
||||||
|
No repos :(
|
||||||
|
{{/repo}}
|
2
tests/miss.env
Normal file
2
tests/miss.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
name="Chris"
|
||||||
|
company="<b>GitHub</b>"
|
4
tests/miss.expected
Normal file
4
tests/miss.expected
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
* Chris
|
||||||
|
*
|
||||||
|
* <b>GitHub</b>
|
||||||
|
* <b>GitHub</b>
|
4
tests/miss.template
Normal file
4
tests/miss.template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
* {{name}}
|
||||||
|
* {{age}}
|
||||||
|
* {{company}}
|
||||||
|
* {{{company}}}
|
6
tests/mush.env
Normal file
6
tests/mush.env
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
USER=jwerle
|
||||||
|
GENDER=male
|
||||||
|
THING=apple
|
||||||
|
COLOR=red
|
||||||
|
PERSON=tobi
|
||||||
|
ADJECTIVE=cool
|
7
tests/mush.expected
Normal file
7
tests/mush.expected
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
jwerle is male
|
||||||
|
apple is red
|
||||||
|
tobi is cool
|
||||||
|
jwerle is friends with tobi
|
||||||
|
|
7
tests/mush.template
Normal file
7
tests/mush.template
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{{! this is a comment }}
|
||||||
|
|
||||||
|
{{USER}} is {{GENDER}}
|
||||||
|
{{THING}} is {{COLOR}}
|
||||||
|
{{PERSON}} is {{ADJECTIVE}}
|
||||||
|
{{USER}} is friends with {{PERSON}}
|
||||||
|
{{var}} {{value}}
|
1
tests/non-false.env
Normal file
1
tests/non-false.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
person=Jon
|
2
tests/non-false.expected
Normal file
2
tests/non-false.expected
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
Hi Jon!
|
3
tests/non-false.template
Normal file
3
tests/non-false.template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{#person}}
|
||||||
|
Hi {{.}}!
|
||||||
|
{{/person}}
|
1
tests/partial.env
Normal file
1
tests/partial.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
names=( "Tyler" )
|
3
tests/partial.expected
Normal file
3
tests/partial.expected
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<h2>Names</h2>
|
||||||
|
|
||||||
|
<strong>Tyler</strong>
|
1
tests/partial.partial
Normal file
1
tests/partial.partial
Normal file
@ -0,0 +1 @@
|
|||||||
|
<strong>{{.}}</strong>
|
4
tests/partial.template
Normal file
4
tests/partial.template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<h2>Names</h2>
|
||||||
|
{{#names}}
|
||||||
|
{{> partial.partial}}
|
||||||
|
{{/names}}
|
4
tests/typical.env
Normal file
4
tests/typical.env
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
NAME="Chris"
|
||||||
|
VALUE=10000
|
||||||
|
TAXED_VALUE=6000
|
||||||
|
IN_CA=true
|
4
tests/typical.expected
Normal file
4
tests/typical.expected
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Hello Chris
|
||||||
|
You have just won 10000 dollars!
|
||||||
|
|
||||||
|
Well, 6000 dollars, after taxes.
|
5
tests/typical.template
Normal file
5
tests/typical.template
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Hello {{NAME}}
|
||||||
|
You have just won {{VALUE}} dollars!
|
||||||
|
{{#IN_CA}}
|
||||||
|
Well, {{TAXED_VALUE}} dollars, after taxes.
|
||||||
|
{{/IN_CA}
|
Loading…
Reference in New Issue
Block a user