mirror of
https://github.com/tests-always-included/mo.git
synced 2026-04-08 08:50:38 +02:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
becc97c677 | ||
|
|
edfce66305 | ||
|
|
e0b3b95eba | ||
|
|
7e86c1a5f5 | ||
|
|
b595ad26b7 | ||
|
|
5a49fe9900 | ||
|
|
8056ee6961 | ||
|
|
5db34e55d3 | ||
|
|
26ca5059d8 | ||
|
|
6e57510ba9 | ||
|
|
54b2184b70 | ||
|
|
84d17268c9 |
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
|||||||
@@ -117,9 +117,11 @@ There are more scripts available in the [demos directory](demo/) that could help
|
|||||||
|
|
||||||
There are additional features that the program supports. Try using `mo --help` to see what is available.
|
There are additional features that the program supports. Try using `mo --help` to see what is available.
|
||||||
|
|
||||||
|
Please note that this command is written in Bash and pulls data from either the environment or (when using `--source`) from a text file that will be sourced and loaded into the environment, which means you will need to have Bash-style variables defined. Please see the examples in `demo/` for different ways you can use `mo`.
|
||||||
|
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
-----------
|
------------
|
||||||
|
|
||||||
In addition to many of the features built-in to Mustache, `mo` includes a number of unique features that make it a bit more powerful.
|
In addition to many of the features built-in to Mustache, `mo` includes a number of unique features that make it a bit more powerful.
|
||||||
|
|
||||||
@@ -263,6 +265,7 @@ Pull requests to solve the following issues would be helpful.
|
|||||||
|
|
||||||
* Dotted names are supported but only for associative arrays (Bash 4). See [`demo/associative-arrays`](demo/associative-arrays) for an example.
|
* Dotted names are supported but only for associative arrays (Bash 4). See [`demo/associative-arrays`](demo/associative-arrays) for an example.
|
||||||
* There's no "top level" object, so `echo '{{.}}' | ./mo` does not do anything useful. In other languages you can say the data for the template is a string and in `mo` the data is always the environment. Luckily this type of usage is rare and `{{.}}` works great when iterating over an array.
|
* There's no "top level" object, so `echo '{{.}}' | ./mo` does not do anything useful. In other languages you can say the data for the template is a string and in `mo` the data is always the environment. Luckily this type of usage is rare and `{{.}}` works great when iterating over an array.
|
||||||
|
* [Parents](https://mustache.github.io/mustache.5.html#Parents), where a template can override chunks of a partial, are not supported.
|
||||||
* HTML encoding is not built into `mo`. `{{{var}}}`, `{{&var}}` and `{{var}}` all do the same thing. `echo '{{TEST}}' | TEST='<b>' mo` will give you "`<b>`" instead of "`>b<`".
|
* HTML encoding is not built into `mo`. `{{{var}}}`, `{{&var}}` and `{{var}}` all do the same thing. `echo '{{TEST}}' | TEST='<b>' mo` will give you "`<b>`" instead of "`>b<`".
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
145
mo
145
mo
@@ -38,7 +38,8 @@
|
|||||||
#/ This message.
|
#/ This message.
|
||||||
#/ -s=FILE, --source=FILE
|
#/ -s=FILE, --source=FILE
|
||||||
#/ Load FILE into the environment before processing templates.
|
#/ Load FILE into the environment before processing templates.
|
||||||
#/ Can be used multiple times.
|
#/ Can be used multiple times. The file must be a valid shell script
|
||||||
|
#/ and should only contain variable assignments.
|
||||||
#/ -o=DELIM, --open=DELIM
|
#/ -o=DELIM, --open=DELIM
|
||||||
#/ Set the opening delimiter. Default is "{{".
|
#/ Set the opening delimiter. Default is "{{".
|
||||||
#/ -c=DELIM, --close=DELIM
|
#/ -c=DELIM, --close=DELIM
|
||||||
@@ -114,6 +115,8 @@ mo() (
|
|||||||
moDoubleHyphens=false
|
moDoubleHyphens=false
|
||||||
MO_OPEN_DELIMITER_DEFAULT="{{"
|
MO_OPEN_DELIMITER_DEFAULT="{{"
|
||||||
MO_CLOSE_DELIMITER_DEFAULT="}}"
|
MO_CLOSE_DELIMITER_DEFAULT="}}"
|
||||||
|
MO_FUNCTION_CACHE_HIT=()
|
||||||
|
MO_FUNCTION_CACHE_MISS=()
|
||||||
|
|
||||||
if [[ $# -gt 0 ]]; then
|
if [[ $# -gt 0 ]]; then
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
@@ -155,7 +158,7 @@ mo() (
|
|||||||
moSource="${arg#-s=}"
|
moSource="${arg#-s=}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "$moSource" ]]; then
|
if [[ -e "$moSource" ]]; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
. "$moSource"
|
. "$moSource"
|
||||||
else
|
else
|
||||||
@@ -429,20 +432,19 @@ mo::indirectArray() {
|
|||||||
#
|
#
|
||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
mo::trimUnparsed() {
|
mo::trimUnparsed() {
|
||||||
local moLast moR moN moT
|
local moI moC
|
||||||
|
|
||||||
moLast=""
|
moI=0
|
||||||
moR=$'\r'
|
moC=${MO_UNPARSED:0:1}
|
||||||
moN=$'\n'
|
|
||||||
moT=$'\t'
|
|
||||||
|
|
||||||
while [[ "$MO_UNPARSED" != "$moLast" ]]; do
|
while [[ "$moC" == " " || "$moC" == $'\r' || "$moC" == $'\n' || "$moC" == $'\t' ]]; do
|
||||||
moLast=$MO_UNPARSED
|
moI=$((moI + 1))
|
||||||
MO_UNPARSED=${MO_UNPARSED# }
|
moC=${MO_UNPARSED:$moI:1}
|
||||||
MO_UNPARSED=${MO_UNPARSED#"$moR"}
|
|
||||||
MO_UNPARSED=${MO_UNPARSED#"$moN"}
|
|
||||||
MO_UNPARSED=${MO_UNPARSED#"$moT"}
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ "$moI" != 0 ]]; then
|
||||||
|
MO_UNPARSED=${MO_UNPARSED:$moI}
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -900,9 +902,41 @@ mo::parseValue() {
|
|||||||
#
|
#
|
||||||
# Returns 0 if the name is a function, 1 otherwise.
|
# Returns 0 if the name is a function, 1 otherwise.
|
||||||
mo::isFunction() {
|
mo::isFunction() {
|
||||||
if declare -F "$1" &> /dev/null; then
|
local moFunctionName
|
||||||
|
|
||||||
|
# Need to test for the array length, otherwise Mac will report an
|
||||||
|
# unbound variable
|
||||||
|
if [[ "${#MO_FUNCTION_CACHE_HIT[@]}" -gt 0 ]]; then
|
||||||
|
for moFunctionName in "${MO_FUNCTION_CACHE_HIT[@]}"; do
|
||||||
|
if [[ "$moFunctionName" == "$1" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${#MO_FUNCTION_CACHE_MISS[@]}" -gt 0 ]]; then
|
||||||
|
for moFunctionName in "${MO_FUNCTION_CACHE_MISS[@]}"; do
|
||||||
|
if [[ "$moFunctionName" == "$1" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if declare -F "$1" &> /dev/null; then
|
||||||
|
if [[ "${#MO_FUNCTION_CACHE_HIT[@]}" -gt 0 ]]; then
|
||||||
|
MO_FUNCTION_CACHE_HIT=( ${MO_FUNCTION_CACHE_HIT[@]+"${MO_FUNCTION_CACHE_HIT[@]}"} "$1" )
|
||||||
|
else
|
||||||
|
MO_FUNCTION_CACHE_HIT=( "$1" )
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${#MO_FUNCTION_CACHE_MISS[@]}" -gt 0 ]]; then
|
||||||
|
MO_FUNCTION_CACHE_MISS=( ${MO_FUNCTION_CACHE_MISS[@]+"${MO_FUNCTION_CACHE_MISS[@]}"} "$1" )
|
||||||
|
else
|
||||||
|
MO_FUNCTION_CACHE_MISS=( "$1" )
|
||||||
|
fi
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -982,14 +1016,46 @@ mo::isArrayIndexValid() {
|
|||||||
# Can not use logic like this in case invalid variable names are passed.
|
# Can not use logic like this in case invalid variable names are passed.
|
||||||
# [[ "${!1-a}" == "${!1-b}" ]]
|
# [[ "${!1-a}" == "${!1-b}" ]]
|
||||||
#
|
#
|
||||||
|
# Using logic like this gives false positives. Also, this is not supported on
|
||||||
|
# Bash 3.2 and the script parsing will error before any commands are executed.
|
||||||
|
# [[ -v "$a" ]]
|
||||||
|
#
|
||||||
|
# Declaring a variable is not the same as assigning the variable.
|
||||||
|
# export x
|
||||||
|
# declare -p x # Output: declare -x x
|
||||||
|
# # Bash 3.2 returns error code 1 and outputs:
|
||||||
|
# # bash: declare: x: not found
|
||||||
|
# export y=""
|
||||||
|
# declare -p y # Output: declare -x y=""
|
||||||
|
# unset z
|
||||||
|
# declare -p z # Error code 1 and output: bash: declare: z: not found
|
||||||
|
#
|
||||||
# Returns true (0) if the variable is set, 1 if the variable is unset.
|
# Returns true (0) if the variable is set, 1 if the variable is unset.
|
||||||
|
MO_VAR_TEST="ok"
|
||||||
|
if test -v "MO_VAR_TEST" &> /dev/null; then
|
||||||
|
mo::debug "Using declare -p and [[ -v ]] for variable checks"
|
||||||
|
# More recent Bash
|
||||||
mo::isVarSet() {
|
mo::isVarSet() {
|
||||||
if ! declare -p "$1" &> /dev/null; then
|
# Do not convert this to [[, otherwise Bash 3.2 will fail to parse the
|
||||||
return 1
|
# script.
|
||||||
|
if declare -p "$1" &> /dev/null && test -v "$1"; then
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 1
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
mo::debug "Using declare -p for variable checks"
|
||||||
|
# Bash 3.2
|
||||||
|
mo::isVarSet() {
|
||||||
|
if declare -p "$1" &> /dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
unset MO_VAR_TEST
|
||||||
|
|
||||||
|
|
||||||
# Internal: Determine if a value is considered truthy.
|
# Internal: Determine if a value is considered truthy.
|
||||||
@@ -1399,31 +1465,40 @@ mo::standaloneCheck() {
|
|||||||
#
|
#
|
||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
mo::standaloneProcess() {
|
mo::standaloneProcess() {
|
||||||
local moContent moLast moT moR moN
|
local moI moTemp
|
||||||
|
|
||||||
moT=$'\t'
|
|
||||||
moR=$'\r'
|
|
||||||
moN=$'\n'
|
|
||||||
moLast=
|
|
||||||
|
|
||||||
mo::debug "Standalone tag - processing content before and after tag"
|
mo::debug "Standalone tag - processing content before and after tag"
|
||||||
|
moI=$((${#MO_PARSED} - 1))
|
||||||
|
mo::debug "zero done ${#MO_PARSED}"
|
||||||
|
mo::escape moTemp "$MO_PARSED"
|
||||||
|
mo::debug "$moTemp"
|
||||||
|
|
||||||
while [[ "$moLast" != "$MO_PARSED" ]]; do
|
# Mac appears to allow getting characters from before the start of the string
|
||||||
moLast=$MO_PARSED
|
while [[ "$moI" -ge 0 ]] && [[ "${MO_PARSED:$moI:1}" == " " || "${MO_PARSED:$moI:1}" == $'\t' ]]; do
|
||||||
MO_PARSED=${MO_PARSED% }
|
moI=$((moI - 1))
|
||||||
MO_PARSED=${MO_PARSED%"$moT"}
|
|
||||||
done
|
done
|
||||||
|
|
||||||
moLast=
|
if [[ $((moI + 1)) != "${#MO_PARSED}" ]]; then
|
||||||
|
MO_PARSED="${MO_PARSED:0:${moI}+1}"
|
||||||
|
fi
|
||||||
|
|
||||||
while [[ "$moLast" != "$MO_UNPARSED" ]]; do
|
moI=0
|
||||||
moLast=$MO_UNPARSED
|
|
||||||
MO_UNPARSED=${MO_UNPARSED# }
|
while [[ "${MO_UNPARSED:${moI}:1}" == " " || "${MO_UNPARSED:${moI}:1}" == $'\t' ]]; do
|
||||||
MO_UNPARSED=${MO_UNPARSED#"$moT"}
|
moI=$((moI + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
MO_UNPARSED=${MO_UNPARSED#"$moR"}
|
if [[ "${MO_UNPARSED:${moI}:1}" == $'\r' ]]; then
|
||||||
MO_UNPARSED=${MO_UNPARSED#"$moN"}
|
moI=$((moI + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${MO_UNPARSED:${moI}:1}" == $'\n' ]]; then
|
||||||
|
moI=$((moI + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$moI" != 0 ]]; then
|
||||||
|
MO_UNPARSED=${MO_UNPARSED:${moI}}
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1949,7 +2024,7 @@ mo::tokenizeTagContentsSingleQuote() {
|
|||||||
|
|
||||||
# Save the original command's path for usage later
|
# Save the original command's path for usage later
|
||||||
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
|
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
|
||||||
MO_VERSION="3.0.2"
|
MO_VERSION="3.0.7"
|
||||||
|
|
||||||
# If sourced, load all functions.
|
# If sourced, load all functions.
|
||||||
# If executed, perform the actions as expected.
|
# If executed, perform the actions as expected.
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ Options:
|
|||||||
This message.
|
This message.
|
||||||
-s=FILE, --source=FILE
|
-s=FILE, --source=FILE
|
||||||
Load FILE into the environment before processing templates.
|
Load FILE into the environment before processing templates.
|
||||||
Can be used multiple times.
|
Can be used multiple times. The file must be a valid shell script
|
||||||
|
and should only contain variable assignments.
|
||||||
-o=DELIM, --open=DELIM
|
-o=DELIM, --open=DELIM
|
||||||
Set the opening delimiter. Default is "{{".
|
Set the opening delimiter. Default is "{{".
|
||||||
-c=DELIM, --close=DELIM
|
-c=DELIM, --close=DELIM
|
||||||
@@ -93,7 +94,7 @@ This is open source! Please feel free to contribute.
|
|||||||
|
|
||||||
https://github.com/tests-always-included/mo
|
https://github.com/tests-always-included/mo
|
||||||
|
|
||||||
MO_VERSION=3.0.2
|
MO_VERSION=3.0.7
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
tests/issue-75
Executable file
9
tests/issue-75
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
cd "${0%/*}" || exit 1
|
||||||
|
. ../run-tests
|
||||||
|
|
||||||
|
export uv
|
||||||
|
export template='{{^uv}}OK{{/uv}}{{#uv}}FAIL{{/uv}}'
|
||||||
|
export expected='OK'
|
||||||
|
|
||||||
|
runTest
|
||||||
Reference in New Issue
Block a user