Compare commits

...

5 Commits

Author SHA1 Message Date
Tyler Akins
7e86c1a5f5
Detect when variables are declared and not set
It is possible to declare a variable but not assign a value to it using
`export x`. When you do this, `declare -p x` shows the variable but does
not show an "=" nor any value afterwards.

When running another command, this variable will not be added to the
environment variables even though it is flagged as exported. Most likely
it's because the value is not a string and can't be easily converted to
a string; this is the same behavior as arrays.

Using `[[ -v x ]]` is also inadequate and I believe its because there
are false positives when trying to access data, which goes on to break
tests and the new braces and parenthesis indirection. Perhaps it could
be reviewed and made to work.

The best solution so far is to combine `declare -p` with `[[ -v` to see
if the variable is declared and if a value is set.

Closes #75.
2024-07-24 12:22:03 -05:00
Tyler Akins
b595ad26b7
Documenting that parents are not supported 2024-06-21 20:44:16 -05:00
Tyler Akins
5a49fe9900
Releasing 3.0.6 2024-06-16 21:13:43 -05:00
Tyler Akins
8056ee6961
Only cut strings once
It's faster to loop through the string and check the character at an
index than it is to trim single characters from the end in a loop.
Trimming multiple characters in the loop is surprisingly slower than
trimming one.

Addresses more of the speed problem reported in #73.
2024-06-16 21:11:45 -05:00
Tyler Akins
5db34e55d3
Caching function name lookups
Faster than dumping functions each time.
Related to #73.
2024-06-16 21:11:02 -05:00
4 changed files with 81 additions and 43 deletions

View File

@ -265,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.
* 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 "`&gt;b&lt;`".

112
mo
View File

@ -115,6 +115,8 @@ mo() (
moDoubleHyphens=false
MO_OPEN_DELIMITER_DEFAULT="{{"
MO_CLOSE_DELIMITER_DEFAULT="}}"
MO_FUNCTION_CACHE_HIT=()
MO_FUNCTION_CACHE_MISS=()
if [[ $# -gt 0 ]]; then
for arg in "$@"; do
@ -430,30 +432,19 @@ mo::indirectArray() {
#
# Returns nothing.
mo::trimUnparsed() {
local moLastLen moR moN moT
local moI moC
moLastLen=0
moR=$'\r'
moN=$'\n'
moT=$'\t'
moI=0
moC=${MO_UNPARSED:0:1}
while [[ "${#MO_UNPARSED}" != "$moLastLen" ]]; do
moLastLen=${#MO_UNPARSED}
# These conditions are necessary for a significant speed increase
while [[ "${MO_UNPARSED:0:1}" == " " ]]; do
MO_UNPARSED=${MO_UNPARSED# }
done
while [[ "${MO_UNPARSED:0:1}" == "$moR" ]]; do
MO_UNPARSED=${MO_UNPARSED#"$moR"}
done
while [[ "${MO_UNPARSED:0:1}" == "$moN" ]]; do
MO_UNPARSED=${MO_UNPARSED#"$moN"}
done
while [[ "${MO_UNPARSED:0:1}" == "$moT" ]]; do
MO_UNPARSED=${MO_UNPARSED#"$moT"}
done
while [[ "$moC" == " " || "$moC" == $'\r' || "$moC" == $'\n' || "$moC" == $'\t' ]]; do
moI=$((moI + 1))
moC=${MO_UNPARSED:$moI:1}
done
if [[ "$moI" != 0 ]]; then
MO_UNPARSED=${MO_UNPARSED:$moI}
fi
}
@ -911,10 +902,28 @@ mo::parseValue() {
#
# Returns 0 if the name is a function, 1 otherwise.
mo::isFunction() {
local moFunctionName
for moFunctionName in "${MO_FUNCTION_CACHE_HIT[@]}"; do
if [[ "$moFunctionName" == "$1" ]]; then
return 0
fi
done
for moFunctionName in "${MO_FUNCTION_CACHE_MISS[@]}"; do
if [[ "$moFunctionName" == "$1" ]]; then
return 1
fi
done
if declare -F "$1" &> /dev/null; then
MO_FUNCTION_CACHE_HIT=( ${MO_FUNCTION_CACHE_HIT[@]+"${MO_FUNCTION_CACHE_HIT[@]}"} "$1" )
return 0
fi
MO_FUNCTION_CACHE_MISS=( ${MO_FUNCTION_CACHE_MISS[@]+"${MO_FUNCTION_CACHE_MISS[@]}"} "$1" )
return 1
}
@ -993,13 +1002,24 @@ mo::isArrayIndexValid() {
# Can not use logic like this in case invalid variable names are passed.
# [[ "${!1-a}" == "${!1-b}" ]]
#
# Using logic like this gives false positives.
# [[ -v "$a" ]]
#
# Declaring a variable is not the same as assigning the variable.
# export x
# declare -p x # Output: declare -x x
# 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.
mo::isVarSet() {
if ! declare -p "$1" &> /dev/null; then
return 1
if declare -p "$1" &> /dev/null && [[ -v "$1" ]]; then
return 0
fi
return 0
return 1
}
@ -1410,31 +1430,39 @@ mo::standaloneCheck() {
#
# Returns nothing.
mo::standaloneProcess() {
local moContent moLast moT moR moN
moT=$'\t'
moR=$'\r'
moN=$'\n'
moLast=
local moI moTemp
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
moLast=$MO_PARSED
MO_PARSED=${MO_PARSED% }
MO_PARSED=${MO_PARSED%"$moT"}
while [[ "${MO_PARSED:$moI:1}" == " " || "${MO_PARSED:$moI:1}" == $'\t' ]]; do
moI=$((moI - 1))
done
moLast=
if [[ $((moI + 1)) != "${#MO_PARSED}" ]]; then
MO_PARSED="${MO_PARSED:0:${moI}+1}"
fi
while [[ "$moLast" != "$MO_UNPARSED" ]]; do
moLast=$MO_UNPARSED
MO_UNPARSED=${MO_UNPARSED# }
MO_UNPARSED=${MO_UNPARSED#"$moT"}
moI=0
while [[ "${MO_UNPARSED:${moI}:1}" == " " || "${MO_UNPARSED:${moI}:1}" == $'\t' ]]; do
moI=$((moI + 1))
done
MO_UNPARSED=${MO_UNPARSED#"$moR"}
MO_UNPARSED=${MO_UNPARSED#"$moN"}
if [[ "${MO_UNPARSED:${moI}:1}" == $'\r' ]]; then
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
}
@ -1960,7 +1988,7 @@ mo::tokenizeTagContentsSingleQuote() {
# Save the original command's path for usage later
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
MO_VERSION="3.0.5"
MO_VERSION="3.0.7"
# If sourced, load all functions.
# If executed, perform the actions as expected.

View File

@ -94,7 +94,7 @@ This is open source! Please feel free to contribute.
https://github.com/tests-always-included/mo
MO_VERSION=3.0.5
MO_VERSION=3.0.7
EOF
}

9
tests/issue-75 Executable file
View 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