mirror of
https://github.com/tests-always-included/mo.git
synced 2026-04-08 08:50:38 +02:00
Compare commits
14 Commits
search-pat
...
2.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c332c9188 | ||
|
|
67ba8bae2c | ||
|
|
1d2617dde1 | ||
|
|
dcd9d7738b | ||
|
|
65f12277e6 | ||
|
|
8cd67fb908 | ||
|
|
bdb795bddf | ||
|
|
1f39b0f568 | ||
|
|
0e6247e9e9 | ||
|
|
891b6a5de2 | ||
|
|
3828588512 | ||
|
|
929ffc5b88 | ||
|
|
5b8cf24068 | ||
|
|
fcedd32155 |
105
API.md
105
API.md
@@ -10,70 +10,48 @@ This documentation is generated automatically from the source of [mo] thanks to
|
||||
Public: Template parser function. Writes templates to stdout.
|
||||
|
||||
* $0 - Name of the mo file, used for getting the help message.
|
||||
* --allow-function-arguments - Permit functions in templates to be called with additional arguments. This puts template data directly in to the path of an eval statement. Use with caution. Not listed in the help because it only makes sense when mo is sourced.
|
||||
* --fail-not-set - (`-u`) Fail upon expansion of an unset variable. Default behavior is to silently ignore and expand into empty string.
|
||||
* --false - (`-e`) Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY environment variable instead to a non-empty value to enable this behavior.
|
||||
* --help - (`-h)` Display a help message.
|
||||
* --source=FILE - (`-s=FILE`) Source a file into the environment before processing template files.
|
||||
* --path=PATH - (`-p=PATH`) Colon-separated list of paths to search for templates. They are relative to where `mo` was executed.
|
||||
* -- - Used to indicate the end of options. You may use this when filenames start with hyphens.
|
||||
* $@ - Filenames to parse.
|
||||
|
||||
Options:
|
||||
|
||||
--allow-function-arguments
|
||||
|
||||
Permit functions in templates to be called with additional arguments. This puts template data directly in to the path of an eval statement. Use with caution. Not listed in the help because it only makes sense when mo is sourced.
|
||||
|
||||
-u, --fail-not-set
|
||||
|
||||
Fail upon expansion of an unset variable. Default behavior is to silently ignore and expand into empty string.
|
||||
|
||||
-x, --fail-on-function
|
||||
|
||||
Fail when a function used by a template returns an error status code. Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a non-empty value to enable this behavior.
|
||||
|
||||
-e, --false
|
||||
|
||||
Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY environment variable instead to a non-empty value to enable this behavior.
|
||||
|
||||
-h, --help
|
||||
|
||||
Display a help message.
|
||||
|
||||
-s=FILE, --source=FILE
|
||||
|
||||
Source a file into the environment before processing template files. This can be used multiple times.
|
||||
|
||||
--
|
||||
|
||||
Used to indicate the end of options. You may optionally use this when filenames may start with two hyphens.
|
||||
|
||||
Mo uses the following environment variables:
|
||||
|
||||
* MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows functions referenced in templates to receive additional options and arguments. This puts the content from the template directly into an eval statement. Use with extreme care.
|
||||
|
||||
* MO_FUNCTION_ARGS - Arguments passed to the function
|
||||
* MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort with an error.
|
||||
* MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env variable will be aborted with an error.
|
||||
|
||||
* MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be treated as an empty value for the purposes of conditionals.
|
||||
|
||||
* MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a help message.
|
||||
|
||||
* MO_SEARCH_PATH - Colon-separated list of folders to search for templates. They are relative to where `mo` was executed.
|
||||
|
||||
Returns true (0) when there are no errors. Sometimes returns (1) when there are errors and sometimes those errors are consumed. It greatly depends on the error and your options.
|
||||
|
||||
|
||||
`files`
|
||||
-------
|
||||
|
||||
After we encounter two hyphens together, all the rest of the arguments are files.
|
||||
|
||||
|
||||
`MO_ALLOW_FUNCTION_ARGUMENTS`
|
||||
-----------------------------
|
||||
|
||||
shellcheck disable=SC2030
|
||||
|
||||
|
||||
`MO_FAIL_ON_UNSET`
|
||||
------------------
|
||||
|
||||
shellcheck disable=SC2030
|
||||
|
||||
|
||||
`MO_FALSE_IS_EMPTY`
|
||||
-------------------
|
||||
|
||||
shellcheck disable=SC2030
|
||||
|
||||
|
||||
`doubleHyphens`
|
||||
---------------
|
||||
|
||||
Set a flag indicating we've encountered double hyphens
|
||||
|
||||
|
||||
`files`
|
||||
-------
|
||||
|
||||
Every arg that is not a flag or a option should be a file
|
||||
|
||||
|
||||
`moProcessSearchPath()`
|
||||
-----------------------
|
||||
|
||||
Internal: Change relative paths into absolute paths
|
||||
Returns nothing.
|
||||
|
||||
|
||||
`moCallFunction()`
|
||||
@@ -81,9 +59,10 @@ Internal: Change relative paths into absolute paths
|
||||
|
||||
Internal: Call a function.
|
||||
|
||||
* $1 - Function to call
|
||||
* $2 - Content to pass
|
||||
* $3 - Additional arguments as a single string
|
||||
* $1 - Variable for output
|
||||
* $2 - Function to call
|
||||
* $3 - Content to pass
|
||||
* $4 - Additional arguments as a single string
|
||||
|
||||
This can be dangerous, especially if you are using tags like {{someFunction ; rm -rf / }}
|
||||
|
||||
@@ -275,7 +254,7 @@ Returns nothing.
|
||||
Internal: Read a file into a variable.
|
||||
|
||||
* $1 - Variable name to receive the file's content
|
||||
* $2 - Filename to load
|
||||
* $2 - Filename to load - if empty, defaults to /dev/stdin
|
||||
|
||||
Returns nothing.
|
||||
|
||||
@@ -331,12 +310,6 @@ Prefix all variables.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
`IFS`
|
||||
-----
|
||||
|
||||
Search the path for the file
|
||||
|
||||
|
||||
`moShow()`
|
||||
----------
|
||||
|
||||
@@ -457,4 +430,4 @@ Save the original command's path for usage later
|
||||
|
||||
|
||||
[mo]: ./mo
|
||||
[tomdoc.sh]: https://github.com/mlafeldt/tomdoc.sh
|
||||
[tomdoc.sh]: https://github.com/tests-always-included/tomdoc.sh
|
||||
|
||||
@@ -27,7 +27,7 @@ Requirements
|
||||
* The "coreutils" package (`basename` and `cat`)
|
||||
* ... that's it. Why? Because bash **can**!
|
||||
|
||||
If you intend to develop this and run the official specs, you also need Node.js.
|
||||
If you intend to develop this and run the official specs, you also need node.js.
|
||||
|
||||
|
||||
Installation
|
||||
|
||||
29
demo/function-args
Executable file
29
demo/function-args
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This sources a simple script with the env. variables needed for the template.
|
||||
|
||||
cd "$(dirname "$0")" # Go to the script's directory
|
||||
source ../mo
|
||||
|
||||
export NAME="Alex"
|
||||
export ARRAY=( AAA BBB CCC )
|
||||
|
||||
# Include an external template
|
||||
INCLUDE() {
|
||||
cat "$MO_FUNCTION_ARGS"
|
||||
}
|
||||
|
||||
# Print section title
|
||||
TITLE() {
|
||||
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
||||
echo "$MO_FUNCTION_ARGS"
|
||||
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
||||
}
|
||||
|
||||
cat <<EOF | mo -u
|
||||
{{TITLE Part 1}}
|
||||
{{INCLUDE function-args-part1}}
|
||||
|
||||
{{TITLE Part 2}}
|
||||
{{INCLUDE function-args-part2}}
|
||||
EOF
|
||||
1
demo/function-args-part1
Normal file
1
demo/function-args-part1
Normal file
@@ -0,0 +1 @@
|
||||
Hello, my name is {{NAME}}.
|
||||
3
demo/function-args-part2
Normal file
3
demo/function-args-part2
Normal file
@@ -0,0 +1,3 @@
|
||||
{{#ARRAY}}
|
||||
* {{.}}
|
||||
{{/ARRAY}}
|
||||
@@ -1,3 +0,0 @@
|
||||
* Child1
|
||||
{{> deeper/child2 }}
|
||||
{{> ../relative-templates/deeper/child2 }}
|
||||
@@ -1 +0,0 @@
|
||||
* Child3
|
||||
@@ -1,2 +0,0 @@
|
||||
* Child2
|
||||
{{> ../child3 }}
|
||||
@@ -1,7 +0,0 @@
|
||||
This is "file"
|
||||
|
||||
Child1 in different ways
|
||||
{{> child1 }}
|
||||
{{> ./child1 }}
|
||||
{{> ../relative-templates/child1 }}
|
||||
{{> deeper/../child1 }}
|
||||
12
demo/writing-out-braces
Executable file
12
demo/writing-out-braces
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")" # Go to the script's directory
|
||||
|
||||
|
||||
export OPEN="{{"
|
||||
export CLOSE="}}"
|
||||
cat <<'EOF' | mo
|
||||
You can use environment variables to write output that has double braces.
|
||||
|
||||
{{OPEN}}sampleTag{{CLOSE}}
|
||||
EOF
|
||||
20
diagnostic
20
diagnostic
@@ -10,11 +10,12 @@
|
||||
# diagnose what's not working and fix those low-level functions first.
|
||||
|
||||
PARENT_PID=$$
|
||||
cd "$(dirname "$0")"
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
rm -f diagnostic.test
|
||||
rm -f diagnostic.partial
|
||||
|
||||
# Load mo's functions
|
||||
# shellcheck disable=SC1091
|
||||
. ./mo
|
||||
|
||||
fail() {
|
||||
@@ -75,21 +76,20 @@ echo "ok"
|
||||
|
||||
echo -n "moIsArray ... "
|
||||
(
|
||||
TEST=1
|
||||
moIsArray TEST && fail "Wrongly said number was an array"
|
||||
export TEST_NUM=1
|
||||
moIsArray TEST_NUM && fail "Wrongly said number was an array"
|
||||
)
|
||||
(
|
||||
TEST=()
|
||||
moIsArray TEST || fail "Wrongly said array was not an array"
|
||||
export TEST_ARR=()
|
||||
moIsArray TEST_ARR || fail "Wrongly said array was not an array"
|
||||
)
|
||||
(
|
||||
# shellcheck disable=SC2034
|
||||
TEST=""
|
||||
moIsArray TEST && fail "Wrongly said string was an array"
|
||||
export TEST_EMPTY=""
|
||||
moIsArray TEST_EMPTY && fail "Wrongly said string was an array"
|
||||
)
|
||||
(
|
||||
unset TEST
|
||||
moIsArray TEST && fail "Wrongly said undefined was an array"
|
||||
unset TEST_UNSET
|
||||
moIsArray TEST_UNSET && fail "Wrongly said undefined was an array"
|
||||
)
|
||||
echo "ok"
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This requires tomdoc.sh to be in your PATH.
|
||||
# https://github.com/mlafeldt/tomdoc.sh
|
||||
# https://github.com/tests-always-included/tomdoc.sh
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
|
||||
cat <<'EOF'
|
||||
API / Function Documentation
|
||||
@@ -13,8 +13,8 @@ This documentation is generated automatically from the source of [mo] thanks to
|
||||
|
||||
|
||||
EOF
|
||||
tomdoc.sh -m mo
|
||||
sed 's/# shellcheck.*//' mo | tomdoc.sh -m
|
||||
cat <<'EOF'
|
||||
[mo]: ./mo
|
||||
[tomdoc.sh]: https://github.com/mlafeldt/tomdoc.sh
|
||||
[tomdoc.sh]: https://github.com/tests-always-included/tomdoc.sh
|
||||
EOF
|
||||
|
||||
230
mo
230
mo
@@ -16,15 +16,16 @@
|
||||
#/ Options:
|
||||
#/
|
||||
#/ -u, --fail-not-set
|
||||
#/ - Fail upon expansion of an unset variable.
|
||||
#/ Fail upon expansion of an unset variable.
|
||||
#/ -x, --fail-on-function
|
||||
#/ Fail when a function returns a non-zero status code.
|
||||
#/ -e, --false
|
||||
#/ - Treat the string "false" as empty for conditionals.
|
||||
#/ Treat the string "false" as empty for conditionals.
|
||||
#/ -h, --help
|
||||
#/ - This message.
|
||||
#/ This message.
|
||||
#/ -s=FILE, --source=FILE
|
||||
#/ - Load FILE into the environment before processing templates.
|
||||
#/ -p=PATH, --path=PATH
|
||||
#/ - Set a colon-delimited list of folders to search for templates.
|
||||
#/ Load FILE into the environment before processing templates.
|
||||
#/ Can be used multiple times.
|
||||
#
|
||||
# Mo is under a MIT style licence with an additional non-advertising clause.
|
||||
# See LICENSE.md for the full text.
|
||||
@@ -37,50 +38,69 @@
|
||||
# Public: Template parser function. Writes templates to stdout.
|
||||
#
|
||||
# $0 - Name of the mo file, used for getting the help message.
|
||||
# --allow-function-arguments - Permit functions in templates to be called with
|
||||
# additional arguments. This puts template data directly in to the path
|
||||
# of an eval statement. Use with caution. Not listed in the help
|
||||
# because it only makes sense when mo is sourced.
|
||||
# --fail-not-set - (`-u`) Fail upon expansion of an unset variable. Default
|
||||
# behavior is to silently ignore and expand into empty string.
|
||||
# --false - (`-e`) Treat "false" as an empty value. You may set the
|
||||
# MO_FALSE_IS_EMPTY environment variable instead to a non-empty value
|
||||
# to enable this behavior.
|
||||
# --help - (`-h)` Display a help message.
|
||||
# --source=FILE - (`-s=FILE`) Source a file into the environment before processing
|
||||
# template files.
|
||||
# --path=PATH - (`-p=PATH`) Colon-separated list of paths to search for templates.
|
||||
# They are relative to where `mo` was executed.
|
||||
# -- - Used to indicate the end of options. You may use this when filenames
|
||||
# start with hyphens.
|
||||
# $@ - Filenames to parse.
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# --allow-function-arguments
|
||||
#
|
||||
# Permit functions in templates to be called with additional arguments. This
|
||||
# puts template data directly in to the path of an eval statement. Use with
|
||||
# caution. Not listed in the help because it only makes sense when mo is
|
||||
# sourced.
|
||||
#
|
||||
# -u, --fail-not-set
|
||||
#
|
||||
# Fail upon expansion of an unset variable. Default behavior is to silently
|
||||
# ignore and expand into empty string.
|
||||
#
|
||||
# -x, --fail-on-function
|
||||
#
|
||||
# Fail when a function used by a template returns an error status code.
|
||||
# Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a
|
||||
# non-empty value to enable this behavior.
|
||||
#
|
||||
# -e, --false
|
||||
#
|
||||
# Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY
|
||||
# environment variable instead to a non-empty value to enable this behavior.
|
||||
#
|
||||
# -h, --help
|
||||
#
|
||||
# Display a help message.
|
||||
#
|
||||
# -s=FILE, --source=FILE
|
||||
#
|
||||
# Source a file into the environment before processing template files.
|
||||
# This can be used multiple times.
|
||||
#
|
||||
# --
|
||||
#
|
||||
# Used to indicate the end of options. You may optionally use this when
|
||||
# filenames may start with two hyphens.
|
||||
#
|
||||
# Mo uses the following environment variables:
|
||||
#
|
||||
# MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
|
||||
# functions referenced in templates to receive additional options and
|
||||
# arguments. This puts the content from the template directly into an
|
||||
# eval statement. Use with extreme care.
|
||||
#
|
||||
# functions referenced in templates to receive additional
|
||||
# options and arguments. This puts the content from the
|
||||
# template directly into an eval statement. Use with extreme
|
||||
# care.
|
||||
# MO_FUNCTION_ARGS - Arguments passed to the function
|
||||
# MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
|
||||
# with an error.
|
||||
# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
|
||||
# variable will be aborted with an error.
|
||||
#
|
||||
# variable will be aborted with an error.
|
||||
# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be
|
||||
# treated as an empty value for the purposes of conditionals.
|
||||
#
|
||||
# treated as an empty value for the purposes of conditionals.
|
||||
# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
|
||||
# help message.
|
||||
# help message.
|
||||
#
|
||||
# MO_SEARCH_PATH - Colon-separated list of folders to search for templates.
|
||||
# They are relative to where `mo` was executed.
|
||||
#
|
||||
# Returns true (0) when there are no errors. Sometimes returns (1) when there
|
||||
# are errors and sometimes those errors are consumed. It greatly depends on the
|
||||
# error and your options.
|
||||
# Returns nothing.
|
||||
mo() (
|
||||
# This function executes in a subshell so IFS is reset.
|
||||
# Namespace this variable so we don't conflict with desired values.
|
||||
local moContent f2source files doubleHyphens paths
|
||||
local moContent f2source files doubleHyphens
|
||||
|
||||
IFS=$' \n\t'
|
||||
files=()
|
||||
@@ -89,8 +109,8 @@ mo() (
|
||||
if [[ $# -gt 0 ]]; then
|
||||
for arg in "$@"; do
|
||||
if $doubleHyphens; then
|
||||
# After we encounter two hyphens together, all the rest
|
||||
# of the arguments are files.
|
||||
#: After we encounter two hyphens together, all the rest
|
||||
#: of the arguments are files.
|
||||
files=("${files[@]}" "$arg")
|
||||
else
|
||||
case "$arg" in
|
||||
@@ -109,13 +129,22 @@ mo() (
|
||||
MO_FAIL_ON_UNSET=true
|
||||
;;
|
||||
|
||||
-x | --fail-on-function)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FAIL_ON_FUNCTION=true
|
||||
;;
|
||||
|
||||
-e | --false)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FALSE_IS_EMPTY=true
|
||||
;;
|
||||
|
||||
-s=* | --source=*)
|
||||
f2source="${arg#*=}"
|
||||
if [[ "$arg" == --source=* ]]; then
|
||||
f2source="${arg#--source=}"
|
||||
else
|
||||
f2source="${arg#-s=}"
|
||||
fi
|
||||
|
||||
if [[ -f "$f2source" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
@@ -126,17 +155,13 @@ mo() (
|
||||
fi
|
||||
;;
|
||||
|
||||
-p=* | --path=*)
|
||||
MO_SEARCH_PATH="$(moProcessSearchPath "${arg#*=}")"
|
||||
;;
|
||||
|
||||
--)
|
||||
# Set a flag indicating we've encountered double hyphens
|
||||
#: Set a flag indicating we've encountered double hyphens
|
||||
doubleHyphens=true
|
||||
;;
|
||||
|
||||
*)
|
||||
# Every arg that is not a flag or a option should be a file
|
||||
#: Every arg that is not a flag or a option should be a file
|
||||
files=(${files[@]+"${files[@]}"} "$arg")
|
||||
;;
|
||||
esac
|
||||
@@ -149,42 +174,41 @@ mo() (
|
||||
)
|
||||
|
||||
|
||||
# Internal: Change relative paths into absolute paths
|
||||
moProcessSearchPath() {
|
||||
local in out path startingPwd IFS
|
||||
|
||||
IFS=:
|
||||
startingPwd=$PWD
|
||||
|
||||
for path in $1; do
|
||||
cd "$startingPwd" && cd "$path" 2>/dev/null && out="$out:$PWD"
|
||||
done
|
||||
|
||||
echo "${out:1}"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Call a function.
|
||||
#
|
||||
# $1 - Function to call
|
||||
# $2 - Content to pass
|
||||
# $3 - Additional arguments as a single string
|
||||
# $1 - Variable for output
|
||||
# $2 - Function to call
|
||||
# $3 - Content to pass
|
||||
# $4 - Additional arguments as a single string
|
||||
#
|
||||
# This can be dangerous, especially if you are using tags like
|
||||
# {{someFunction ; rm -rf / }}
|
||||
#
|
||||
# Returns nothing.
|
||||
moCallFunction() {
|
||||
local moArgs
|
||||
local moArgs moContent moFunctionArgs moFunctionResult
|
||||
|
||||
moArgs=()
|
||||
moTrimWhitespace moFunctionArgs "$4"
|
||||
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
|
||||
moArgs=$3
|
||||
# Intentionally bad behavior
|
||||
# shellcheck disable=SC2206
|
||||
moArgs=($4)
|
||||
fi
|
||||
|
||||
echo -n "$2" | eval "$1" "$moArgs"
|
||||
moContent=$(echo -n "$3" | MO_FUNCTION_ARGS="$moFunctionArgs" eval "$2" "${moArgs[@]}") || {
|
||||
moFunctionResult=$?
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then
|
||||
echo "Function '$2' with args (${moArgs[*]+"${moArgs[@]}"}) failed with status code $moFunctionResult"
|
||||
exit "$moFunctionResult"
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2031
|
||||
local "$1" && moIndirect "$1" "$moContent"
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +260,7 @@ moFindEndTag() {
|
||||
if [[ -z "${4-}" ]] && moIsStandalone standaloneBytes "$scanned" "${content[2]}" true; then
|
||||
#: This is also a standalone tag - clean up whitespace
|
||||
#: and move those whitespace bytes to the "tag" element
|
||||
# shellcheck disable=SC2206
|
||||
standaloneBytes=( $standaloneBytes )
|
||||
content[1]="${scanned:${standaloneBytes[0]}}${content[1]}${content[2]:0:${standaloneBytes[1]}}"
|
||||
scanned="${scanned:0:${standaloneBytes[0]}}"
|
||||
@@ -307,22 +332,22 @@ moFullTagName() {
|
||||
#
|
||||
# Returns nothing.
|
||||
moGetContent() {
|
||||
local content filename target
|
||||
local moContent moFilename moTarget
|
||||
|
||||
target=$1
|
||||
moTarget=$1
|
||||
shift
|
||||
if [[ "${#@}" -gt 0 ]]; then
|
||||
content=""
|
||||
moContent=""
|
||||
|
||||
for filename in "$@"; do
|
||||
for moFilename in "$@"; do
|
||||
#: This is so relative paths work from inside template files
|
||||
content="$content"'{{>'"$filename"'}}'
|
||||
moContent="$moContent"'{{>'"$moFilename"'}}'
|
||||
done
|
||||
else
|
||||
moLoadFile content /dev/stdin || return 1
|
||||
moLoadFile moContent || return 1
|
||||
fi
|
||||
|
||||
local "$target" && moIndirect "$target" "$content"
|
||||
local "$moTarget" && moIndirect "$moTarget" "$moContent"
|
||||
}
|
||||
|
||||
|
||||
@@ -497,6 +522,7 @@ moIsFunction() {
|
||||
local functionList functionName
|
||||
|
||||
functionList=$(declare -F)
|
||||
# shellcheck disable=SC2206
|
||||
functionList=( ${functionList//declare -f /} )
|
||||
|
||||
for functionName in "${functionList[@]}"; do
|
||||
@@ -588,7 +614,7 @@ moJoin() {
|
||||
# Internal: Read a file into a variable.
|
||||
#
|
||||
# $1 - Variable name to receive the file's content
|
||||
# $2 - Filename to load
|
||||
# $2 - Filename to load - if empty, defaults to /dev/stdin
|
||||
#
|
||||
# Returns nothing.
|
||||
moLoadFile() {
|
||||
@@ -599,7 +625,7 @@ moLoadFile() {
|
||||
# As a future optimization, it would be worth considering removing
|
||||
# cat and replacing this with a read loop.
|
||||
|
||||
content=$(cat -- "$2" && echo '.') || return 1
|
||||
content=$(cat -- "${2:-/dev/stdin}" && echo '.') || return 1
|
||||
len=$((${#content} - 1))
|
||||
content=${content:0:$len} # Remove last dot
|
||||
|
||||
@@ -671,9 +697,7 @@ moParse() {
|
||||
if moTest "$moTag"; then
|
||||
# Show / loop / pass through function
|
||||
if moIsFunction "$moTag"; then
|
||||
#: Consider piping the output to moGetContent
|
||||
#: so the lambda does not execute in a subshell?
|
||||
moContent=$(moCallFunction "$moTag" "${moBlock[0]}" "$moArgs")
|
||||
moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs"
|
||||
moParse "$moContent" "$moCurrent" false
|
||||
moContent="${moBlock[2]}"
|
||||
elif moIsArray "$moTag"; then
|
||||
@@ -806,6 +830,7 @@ moPartial() {
|
||||
local moContent moFilename moIndent moIsBeginning moPartial moStandalone moUnindented
|
||||
|
||||
if moIsStandalone moStandalone "$2" "$4" "$5"; then
|
||||
# shellcheck disable=SC2206
|
||||
moStandalone=( $moStandalone )
|
||||
echo -n "${2:0:${moStandalone[0]}}"
|
||||
moIndent=${2:${moStandalone[0]}}
|
||||
@@ -824,39 +849,7 @@ moPartial() {
|
||||
(
|
||||
# It would be nice to remove `dirname` and use a function instead,
|
||||
# but that's difficult when you're only given filenames.
|
||||
if ! cd "$(dirname -- "$moFilename")"; then
|
||||
if [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
|
||||
echo "Error changing to directory: $(dirname -- "$moFilename")" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mustache likes to be silent when there are errors.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$moFilename" != */* ]] && [[ ! -f "$moFilename" ]] && [[ -n "$MO_SEARCH_PATH" ]]; then
|
||||
# Search the path for the file
|
||||
IFS=:
|
||||
|
||||
for moSearchPath in $MO_SEARCH_PATH; do
|
||||
if [[ ! -f "$moFilename" ]]; then
|
||||
cd "$moSearchPath"
|
||||
fi
|
||||
done
|
||||
|
||||
IFS=$' \n\t'
|
||||
fi
|
||||
|
||||
if [[ ! -f "${moFilename##*/}" ]]; then
|
||||
if [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
|
||||
echo "File does not exist: $PWD/${moFilename##*/}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mustache likes to be silent when there are errors.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$(dirname -- "$moFilename")" || exit 1
|
||||
moUnindented="$(
|
||||
moLoadFile moPartial "${moFilename##*/}" || exit 1
|
||||
moParse "${moPartial}" "$6" true
|
||||
@@ -891,11 +884,11 @@ moPartial() {
|
||||
# Returns nothing.
|
||||
moShow() {
|
||||
# Namespace these variables
|
||||
local moJoined moNameParts
|
||||
local moJoined moNameParts moContent
|
||||
|
||||
if moIsFunction "$1"; then
|
||||
CONTENT=$(moCallFunction "$1" "" "$3")
|
||||
moParse "$CONTENT" "$2" false
|
||||
moCallFunction moContent "$1" "" "$3"
|
||||
moParse "$moContent" "$2" false
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -970,6 +963,7 @@ moStandaloneAllowed() {
|
||||
local bytes
|
||||
|
||||
if moIsStandalone bytes "$2" "$4" "$5"; then
|
||||
# shellcheck disable=SC2206
|
||||
bytes=( $bytes )
|
||||
echo -n "${2:0:${bytes[0]}}"
|
||||
local "$1" && moIndirect "$1" "${4:${bytes[1]}}"
|
||||
@@ -1097,13 +1091,13 @@ moTrimWhitespace() {
|
||||
moUsage() {
|
||||
grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
|
||||
echo ""
|
||||
set | grep ^MO_VERSION=
|
||||
echo "MO_VERSION=$MO_VERSION"
|
||||
}
|
||||
|
||||
|
||||
# Save the original command's path for usage later
|
||||
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
|
||||
MO_VERSION="2.0.4"
|
||||
MO_VERSION="2.2.0"
|
||||
|
||||
# If sourced, load all functions.
|
||||
# If executed, perform the actions as expected.
|
||||
|
||||
10
run-tests
10
run-tests
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. ./mo
|
||||
PASS=0
|
||||
FAIL=0
|
||||
@@ -18,13 +19,16 @@ for TEST in tests/*.expected; do
|
||||
"${BASE}.sh"
|
||||
else
|
||||
# Fall back to using .env and .template
|
||||
# shellcheck disable=SC1090
|
||||
. "${BASE}.env"
|
||||
echo "Do not read this input" | mo "${BASE}.template"
|
||||
fi
|
||||
) | diff -U5 - "${TEST}" > "${BASE}.diff"
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "FAIL"
|
||||
statusCode=$?
|
||||
|
||||
if [[ $statusCode -ne 0 ]]; then
|
||||
echo "FAIL (status code $statusCode)"
|
||||
FAIL=$(( FAIL + 1 ))
|
||||
else
|
||||
echo "ok"
|
||||
|
||||
@@ -1 +1 @@
|
||||
File does not exist: /home/fidian/repo/mo/tests/--help
|
||||
cat: --help: No such file or directory
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# This should display a message indicating that the file --help
|
||||
# could not be found. It should not display a help messsage.
|
||||
cd "${0%/*}"
|
||||
../mo -u -- --help 2>&1
|
||||
cd "${0%/*}" || exit 1
|
||||
../mo -- --help 2>&1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
unset __NO_SUCH_VAR
|
||||
POPULATED="words" EMPTY="" ../mo --fail-not-set ./fail-not-set-file.template 2>&1
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
unset __NO_SUCH_VAR
|
||||
POPULATED="words" EMPTY="" ../mo --fail-not-set 2>&1 <<EOF
|
||||
Populated: {{POPULATED}};
|
||||
|
||||
1
tests/fail-on-function.expected
Normal file
1
tests/fail-on-function.expected
Normal file
@@ -0,0 +1 @@
|
||||
Fail on function? Function 'failFunction' with args () failed with status code 1
|
||||
18
tests/fail-on-function.sh
Executable file
18
tests/fail-on-function.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}" || exit 1
|
||||
|
||||
failFunction() {
|
||||
false
|
||||
}
|
||||
|
||||
# Must be sourced to use functions
|
||||
# shellcheck disable=SC1091
|
||||
. ../mo
|
||||
mo --fail-on-function 2>&1 <<EOF
|
||||
Fail on function? {{failFunction}}
|
||||
EOF
|
||||
|
||||
if [[ $? -ne 1 ]]; then
|
||||
echo "Did not return 1"
|
||||
fi
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
USER=j.doe ADMIN=false ../mo --false false-is-empty-arg.template
|
||||
|
||||
3
tests/function-args-read.env
Normal file
3
tests/function-args-read.env
Normal file
@@ -0,0 +1,3 @@
|
||||
testArgs() {
|
||||
echo "$MO_FUNCTION_ARGS"
|
||||
}
|
||||
4
tests/function-args-read.expected
Normal file
4
tests/function-args-read.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
No args: [] - done
|
||||
One arg: [one] - done
|
||||
Multiple arguments: [aa bb cc 'x' " ! {[_.|] - done
|
||||
Evil: [bla; cat /etc/issue] - done
|
||||
4
tests/function-args-read.template
Normal file
4
tests/function-args-read.template
Normal file
@@ -0,0 +1,4 @@
|
||||
No args: [{{testArgs}}] - done
|
||||
One arg: [{{testArgs one}}] - done
|
||||
Multiple arguments: [{{testArgs aa bb cc 'x' " ! {[_.| }}] - done
|
||||
Evil: [{{testArgs bla; cat /etc/issue}}] - done
|
||||
@@ -14,14 +14,15 @@ Simple usage:
|
||||
Options:
|
||||
|
||||
-u, --fail-not-set
|
||||
- Fail upon expansion of an unset variable.
|
||||
Fail upon expansion of an unset variable.
|
||||
-x, --fail-on-function
|
||||
Fail when a function returns a non-zero status code.
|
||||
-e, --false
|
||||
- Treat the string "false" as empty for conditionals.
|
||||
Treat the string "false" as empty for conditionals.
|
||||
-h, --help
|
||||
- This message.
|
||||
This message.
|
||||
-s=FILE, --source=FILE
|
||||
- Load FILE into the environment before processing templates.
|
||||
-p=PATH, --path=PATH
|
||||
- Set a colon-delimited list of folders to search for templates.
|
||||
Load FILE into the environment before processing templates.
|
||||
Can be used multiple times.
|
||||
|
||||
MO_VERSION=2.0.4
|
||||
MO_VERSION=2.2.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
../mo -u --help
|
||||
cd "${0%/*}" || exit 1
|
||||
../mo --help
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
cat: --something: No such file or directory
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# This should display a message indicating that the file --something
|
||||
# could not be found.
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
../mo --something 2>&1
|
||||
|
||||
@@ -1 +1 @@
|
||||
File does not exist: /home/fidian/repo/mo/tests/partial-missing.partial
|
||||
cat: partial-missing.partial: No such file or directory
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
../mo -u partial-missing.template 2>&1
|
||||
returned=$?
|
||||
cd "${0%/*}" || exit 1
|
||||
../mo partial-missing.template 2>&1
|
||||
|
||||
if [[ $returned -ne 1 ]]; then
|
||||
echo "Did not return 1. Instead, returned $returned."
|
||||
if [[ $? -ne 1 ]]; then
|
||||
echo "Did not return 1"
|
||||
fi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
echo "Do not display this" | ../mo --source=invalid 2>&1
|
||||
|
||||
if [[ $? -ne 1 ]]; then
|
||||
|
||||
2
tests/source-multiple-1.vars
Normal file
2
tests/source-multiple-1.vars
Normal file
@@ -0,0 +1,2 @@
|
||||
export A=from1
|
||||
export B=from1
|
||||
2
tests/source-multiple-2.vars
Normal file
2
tests/source-multiple-2.vars
Normal file
@@ -0,0 +1,2 @@
|
||||
export B=from2
|
||||
export C=from2
|
||||
3
tests/source-multiple.expected
Normal file
3
tests/source-multiple.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
A: from1
|
||||
B: from2
|
||||
C: from2
|
||||
8
tests/source-multiple.sh
Executable file
8
tests/source-multiple.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}" || exit 1
|
||||
cat <<EOF | ../mo --source=source-multiple-1.vars --source=source-multiple-2.vars
|
||||
A: {{A}}
|
||||
B: {{B}}
|
||||
C: {{C}}
|
||||
EOF
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
echo "Do not display this" | ../mo --source= 2>&1
|
||||
|
||||
if [[ $? -ne 1 ]]; then
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
. ../mo
|
||||
cat <<EOF | mo --source=source.vars
|
||||
cd "${0%/*}" || exit 1
|
||||
cat <<EOF | ../mo --source=source.vars
|
||||
{{VAR}}
|
||||
{{#ARR}}
|
||||
* {{.}}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export VAR=value
|
||||
export ARR=(1 2 3)
|
||||
declare -A ASSOC_ARR
|
||||
# Can not export associative arrays, otherwise they turn into indexed arrays
|
||||
ASSOC_ARR=([a]=AAA [b]=BBB)
|
||||
|
||||
Reference in New Issue
Block a user