mirror of
https://github.com/tests-always-included/mo.git
synced 2026-04-08 08:50:38 +02:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c86fd9a89b | ||
|
|
03eb3925ac | ||
|
|
8e3e08a42b | ||
|
|
4c332c9188 | ||
|
|
67ba8bae2c | ||
|
|
1d2617dde1 | ||
|
|
dcd9d7738b | ||
|
|
65f12277e6 | ||
|
|
8cd67fb908 | ||
|
|
bdb795bddf | ||
|
|
1f39b0f568 | ||
|
|
0e6247e9e9 | ||
|
|
891b6a5de2 | ||
|
|
3828588512 | ||
|
|
929ffc5b88 | ||
|
|
5b8cf24068 | ||
|
|
fcedd32155 | ||
|
|
9f6d3bcdab | ||
|
|
eac2685632 | ||
|
|
505570a57b | ||
|
|
aabc62f1d6 | ||
|
|
e672bda163 | ||
|
|
dbefade193 | ||
|
|
57a8d41394 | ||
|
|
5739ccd705 | ||
|
|
f889c37316 | ||
|
|
4f615a0faf | ||
|
|
1e91e414cc | ||
|
|
ad98577c42 | ||
|
|
e78b15d95b | ||
|
|
f20e4ae83a | ||
|
|
a5ec7dd740 | ||
|
|
ab5dfee519 | ||
|
|
81fd601ebd |
45
.github/workflows/release.yaml
vendored
Normal file
45
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: docker push
|
||||
on: [push]
|
||||
jobs:
|
||||
push_to_registry:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Docker meta
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tag-match: v(.*)
|
||||
- name: Set up QEMU
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Cache Docker layers
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Login to GitHub Container Registry
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
push: true
|
||||
194
API.md
194
API.md
@@ -4,21 +4,49 @@ API / Function Documentation
|
||||
This documentation is generated automatically from the source of [mo] thanks to [tomdoc.sh].
|
||||
|
||||
|
||||
mo()
|
||||
----
|
||||
`mo()`
|
||||
------
|
||||
|
||||
Public: Template parser function. Writes templates to stdout.
|
||||
|
||||
* $0 - Name of the mo file, used for getting the help message.
|
||||
* --fail-not-set - Fail upon expansion of an unset variable. Default behavior is to silently ignore and expand into empty string.
|
||||
* --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.
|
||||
* --help - Display a help message.
|
||||
* --source=FILE - Source a file into the environment before processint template files.
|
||||
* -- - Used to indicate the end of options. You may optionally use this when filenames may start with two 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.
|
||||
@@ -26,39 +54,24 @@ Mo uses the following environment variables:
|
||||
Returns nothing.
|
||||
|
||||
|
||||
files
|
||||
-----
|
||||
`moCallFunction()`
|
||||
------------------
|
||||
|
||||
After we encounter two hyphens together, all the rest of the arguments are files.
|
||||
Internal: Call a function.
|
||||
|
||||
* $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.
|
||||
|
||||
|
||||
MO_FAIL_ON_UNSET
|
||||
`moFindEndTag()`
|
||||
----------------
|
||||
|
||||
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
|
||||
|
||||
|
||||
moFindEndTag()
|
||||
--------------
|
||||
|
||||
Internal: Scan content until the right end tag is found. Creates an array with the following members:
|
||||
|
||||
[0] = Content before end tag
|
||||
@@ -75,8 +88,8 @@ Everything using this function uses the "standalone tags" logic.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moFindString()
|
||||
--------------
|
||||
`moFindString()`
|
||||
----------------
|
||||
|
||||
Internal: Find the first index of a substring. If not found, sets the index to -1.
|
||||
|
||||
@@ -87,8 +100,8 @@ Internal: Find the first index of a substring. If not found, sets the index to
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moFullTagName()
|
||||
---------------
|
||||
`moFullTagName()`
|
||||
-----------------
|
||||
|
||||
Internal: Generate a dotted name based on current context and target name.
|
||||
|
||||
@@ -99,8 +112,8 @@ Internal: Generate a dotted name based on current context and target name.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moGetContent()
|
||||
--------------
|
||||
`moGetContent()`
|
||||
----------------
|
||||
|
||||
Internal: Fetches the content to parse into a variable. Can be a list of partials for files or the content from stdin.
|
||||
|
||||
@@ -110,8 +123,8 @@ Internal: Fetches the content to parse into a variable. Can be a list of partia
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moIndentLines()
|
||||
---------------
|
||||
`moIndentLines()`
|
||||
-----------------
|
||||
|
||||
Internal: Indent a string, placing the indent at the beginning of every line that has any content.
|
||||
|
||||
@@ -122,8 +135,8 @@ Internal: Indent a string, placing the indent at the beginning of every line tha
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moIndirect()
|
||||
------------
|
||||
`moIndirect()`
|
||||
--------------
|
||||
|
||||
Internal: Send a variable up to the parent of the caller of this function.
|
||||
|
||||
@@ -141,8 +154,8 @@ Examples
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moIndirectArray()
|
||||
-----------------
|
||||
`moIndirectArray()`
|
||||
-------------------
|
||||
|
||||
Internal: Send an array as a variable up to caller of a function
|
||||
|
||||
@@ -161,8 +174,8 @@ Examples
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moIsArray()
|
||||
-----------
|
||||
`moIsArray()`
|
||||
-------------
|
||||
|
||||
Internal: Determine if a given environment variable exists and if it is an array.
|
||||
|
||||
@@ -173,16 +186,16 @@ Be extremely careful. Even if strict mode is enabled, it is not honored in newe
|
||||
Examples
|
||||
|
||||
var=(abc)
|
||||
if moIsArray var; the
|
||||
if moIsArray var; then
|
||||
echo "This is an array"
|
||||
echo "Make sure you don't accidentally use $var"
|
||||
echo "Make sure you don't accidentally use \$var"
|
||||
fi
|
||||
|
||||
Returns 0 if the name is not empty, 1 otherwise.
|
||||
|
||||
|
||||
moIsFunction()
|
||||
--------------
|
||||
`moIsFunction()`
|
||||
----------------
|
||||
|
||||
Internal: Determine if the given name is a defined function.
|
||||
|
||||
@@ -202,8 +215,8 @@ Examples
|
||||
Returns 0 if the name is a function, 1 otherwise.
|
||||
|
||||
|
||||
moIsStandalone()
|
||||
----------------
|
||||
`moIsStandalone()`
|
||||
------------------
|
||||
|
||||
Internal: Determine if the tag is a standalone tag based on whitespace before and after the tag.
|
||||
|
||||
@@ -223,8 +236,8 @@ Examples
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moJoin()
|
||||
--------
|
||||
`moJoin()`
|
||||
----------
|
||||
|
||||
Internal: Join / implode an array
|
||||
|
||||
@@ -235,19 +248,19 @@ Internal: Join / implode an array
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moLoadFile()
|
||||
------------
|
||||
`moLoadFile()`
|
||||
--------------
|
||||
|
||||
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.
|
||||
|
||||
|
||||
moLoop()
|
||||
--------
|
||||
`moLoop()`
|
||||
----------
|
||||
|
||||
Internal: Process a chunk of content some number of times. Writes output to stdout.
|
||||
|
||||
@@ -258,8 +271,8 @@ Internal: Process a chunk of content some number of times. Writes output to std
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moParse()
|
||||
---------
|
||||
`moParse()`
|
||||
-----------
|
||||
|
||||
Internal: Parse a block of text, writing the result to stdout.
|
||||
|
||||
@@ -270,8 +283,14 @@ Internal: Parse a block of text, writing the result to stdout.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moPartial()
|
||||
-----------
|
||||
`moArgs`
|
||||
--------
|
||||
|
||||
Split arguments from the tag name. Arguments are passed to functions.
|
||||
|
||||
|
||||
`moPartial()`
|
||||
-------------
|
||||
|
||||
Internal: Process a partial.
|
||||
|
||||
@@ -291,8 +310,8 @@ Prefix all variables.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moShow()
|
||||
--------
|
||||
`moShow()`
|
||||
----------
|
||||
|
||||
Internal: Show an environment variable or the output of a function to stdout.
|
||||
|
||||
@@ -300,12 +319,13 @@ Limit/prefix any variables used.
|
||||
|
||||
* $1 - Name of environment variable or function
|
||||
* $2 - Current context
|
||||
* $3 - Arguments string if $1 is a function
|
||||
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moSplit()
|
||||
---------
|
||||
`moSplit()`
|
||||
-----------
|
||||
|
||||
Internal: Split a larger string into an array.
|
||||
|
||||
@@ -317,8 +337,8 @@ Internal: Split a larger string into an array.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moStandaloneAllowed()
|
||||
---------------------
|
||||
`moStandaloneAllowed()`
|
||||
-----------------------
|
||||
|
||||
Internal: Handle the content for a standalone tag. This means removing whitespace (not newlines) before a tag and whitespace and a newline after a tag. That is, assuming, that the line is otherwise empty.
|
||||
|
||||
@@ -331,8 +351,8 @@ Internal: Handle the content for a standalone tag. This means removing whitespa
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moStandaloneDenied()
|
||||
--------------------
|
||||
`moStandaloneDenied()`
|
||||
----------------------
|
||||
|
||||
Internal: Handle the content for a tag that is never "standalone". No adjustments are made for newlines and whitespace.
|
||||
|
||||
@@ -344,8 +364,8 @@ Internal: Handle the content for a tag that is never "standalone". No adjustmen
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moTest()
|
||||
--------
|
||||
`moTest()`
|
||||
----------
|
||||
|
||||
Internal: Determines if the named thing is a function or if it is a non-empty environment variable. When MO_FALSE_IS_EMPTY is set to a non-empty value, then "false" is also treated is an empty value.
|
||||
|
||||
@@ -358,8 +378,8 @@ Do not use variables without prefixes here if possible as this needs to check if
|
||||
Returns 0 if the name is not empty, 1 otherwise. When MO_FALSE_IS_EMPTY is set, this returns 1 if the name is "false".
|
||||
|
||||
|
||||
moTestVarSet()
|
||||
--------------
|
||||
`moTestVarSet()`
|
||||
----------------
|
||||
|
||||
Internal: Determine if a variable is assigned, even if it is assigned an empty value.
|
||||
|
||||
@@ -368,8 +388,8 @@ Internal: Determine if a variable is assigned, even if it is assigned an empty v
|
||||
Returns true (0) if the variable is set, 1 if the variable is unset.
|
||||
|
||||
|
||||
moTrimChars()
|
||||
-------------
|
||||
`moTrimChars()`
|
||||
---------------
|
||||
|
||||
Internal: Trim the leading whitespace only.
|
||||
|
||||
@@ -382,8 +402,8 @@ Internal: Trim the leading whitespace only.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moTrimWhitespace()
|
||||
------------------
|
||||
`moTrimWhitespace()`
|
||||
--------------------
|
||||
|
||||
Internal: Trim leading and trailing whitespace from a string.
|
||||
|
||||
@@ -393,8 +413,8 @@ Internal: Trim leading and trailing whitespace from a string.
|
||||
Returns nothing.
|
||||
|
||||
|
||||
moUsage()
|
||||
---------
|
||||
`moUsage()`
|
||||
-----------
|
||||
|
||||
Internal: Displays the usage for mo. Pulls this from the file that contained the `mo` function. Can only work when the right filename comes is the one argument, and that only happens when `mo` is called with `$0` set to this file.
|
||||
|
||||
@@ -403,11 +423,11 @@ Internal: Displays the usage for mo. Pulls this from the file that contained th
|
||||
Returns nothing.
|
||||
|
||||
|
||||
MO_ORIGINAL_COMMAND
|
||||
-------------------
|
||||
`MO_ORIGINAL_COMMAND`
|
||||
---------------------
|
||||
|
||||
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
|
||||
|
||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM alpine:3.13.4
|
||||
|
||||
COPY ./mo .
|
||||
RUN apk add --update bash && \
|
||||
chmod +x mo &&\
|
||||
mv mo /usr/local/bin/mo && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
WORKDIR /opt
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/mo"]
|
||||
@@ -41,7 +41,7 @@ There are a few ways you can install this tool. How you install it depends on h
|
||||
You can install this file in `/usr/local/bin/` or `/usr/bin/` by simply downloading it, changing the permissions, then moving it to the right location. Double check that your system's PATH includes the destination folder, otherwise users may have a hard time starting the command.
|
||||
|
||||
# Download
|
||||
curl -sSO https://raw.githubusercontent.com/tests-always-included/mo/master/mo
|
||||
curl -sSL https://git.io/get-mo -o mo
|
||||
|
||||
# Make executable
|
||||
chmod +x mo
|
||||
@@ -58,7 +58,7 @@ You can install this file in `/usr/local/bin/` or `/usr/bin/` by simply download
|
||||
This is very similar to installing it globally but it does not require root privileges. It is very important that your PATH includes the destination folder otherwise it won't work. Some local folders that are typically used are `~/bin/` and `~/.local/bin/`.
|
||||
|
||||
# Download
|
||||
curl -sSO https://raw.githubusercontent.com/tests-always-included/mo/master/mo
|
||||
curl -sSL https://git.io/get-mo -o mo
|
||||
|
||||
# Make executable
|
||||
chmod +x mo
|
||||
@@ -78,7 +78,7 @@ This is very similar to installing it globally but it does not require root priv
|
||||
Bash scripts can source `mo` to include the functionality in their own routines. This usage typically would have `mo` saved to a `lib/` folder in an application and your other scripts would use `. lib/mo` to bring it into your project.
|
||||
|
||||
# Download
|
||||
curl -sSO https://raw.githubusercontent.com/tests-always-included/mo/master/mo
|
||||
curl -sSL https://git.io/get-mo -o mo
|
||||
|
||||
# Move into your project folder
|
||||
mv mo ~/projects/amazing-things/lib/
|
||||
@@ -115,6 +115,8 @@ The result? You get a list of the five elements in the array. It is vital that
|
||||
|
||||
There are more scripts available in the [demos directory](demo/) that could help illustrate how you would use this program.
|
||||
|
||||
There are additional features that the program supports. Try using `mo --help` to see what is available.
|
||||
|
||||
|
||||
Concessions
|
||||
-----------
|
||||
|
||||
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}}
|
||||
@@ -3,6 +3,9 @@
|
||||
cd "$(dirname "$0")" # Go to the script's directory
|
||||
|
||||
EVERY_REPO() {
|
||||
# The block contents come in through standard input. Capture it here.
|
||||
content=$(cat)
|
||||
|
||||
echo "# Starting EVERY_REPO"
|
||||
|
||||
# Get list of repos
|
||||
@@ -14,7 +17,7 @@ EVERY_REPO() {
|
||||
# It rewrites {{__REPO__.name}} into {{resque.name}}, for instance.
|
||||
# You can prefix your environment variables and do other things as well.
|
||||
|
||||
echo -n "$1" | sed "s/__REPO__/${REPO}/"
|
||||
echo "$content" | sed "s/__REPO__/${REPO}/"
|
||||
|
||||
echo "## Looped one time for repo: $REPO"
|
||||
done
|
||||
|
||||
39
demo/function-for-building-json
Executable file
39
demo/function-for-building-json
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")" # Go to the script's directory
|
||||
|
||||
# Detect if this is the first item and write a comma if it is.
|
||||
# Normally, I would track this using a variable, like so:
|
||||
#
|
||||
# COMMA_IF_NOT_FIRST_FLAG=false
|
||||
# COMMA_IF_NOT_FIRST() {
|
||||
# $COMMA_IF_NOT_FIRST || echo ","
|
||||
# COMMA_IF_NOT_FIRST_FLAG=true
|
||||
# }
|
||||
#
|
||||
# Since this function executes in a subshell, that approach will not work.
|
||||
# Instead, we peek inside mo and see what is being processed. If the variable
|
||||
# name in moParse() changes, this will need to get updated as well. An
|
||||
# alternate variable that is usable is context, but that is in moLoop() and is
|
||||
# two levels levels deep instead of just one.
|
||||
COMMA_IF_NOT_FIRST() {
|
||||
[[ "${moCurrent#*.}" != "0" ]] && echo ","
|
||||
}
|
||||
|
||||
# Create an array that will be embedded into the JSON. If you are manipulating
|
||||
# JSON, might I suggest you look at using jq? It's really good at processing
|
||||
# JSON.
|
||||
items=(
|
||||
'{"position":"one","url":"1"}'
|
||||
'{"position":"two","url":"2"}'
|
||||
'{"position":"three","url":"3"}'
|
||||
)
|
||||
. ../mo
|
||||
cat <<EOF | mo
|
||||
{
|
||||
{{#items}}
|
||||
{{COMMA_IF_NOT_FIRST}}
|
||||
{{.}}
|
||||
{{/items}}
|
||||
}
|
||||
EOF
|
||||
48
demo/function-for-foreach
Executable file
48
demo/function-for-foreach
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Example for how #29 can get implemented.
|
||||
|
||||
cd "$(dirname "$0")" # Go to the script's directory
|
||||
|
||||
foreach() {
|
||||
# Trying to use unique names
|
||||
local foreachSourceName foreachIterator foreachEvalString foreachContent
|
||||
|
||||
foreachContent=$(cat)
|
||||
|
||||
if [[ "$2" != "as" && "$2" != "in" ]]; then
|
||||
echo "Invalid foreach - bad format."
|
||||
elif [[ "$(declare -p "$1")" != "declare -"[aA]* ]]; then
|
||||
echo "$1 is not an array"
|
||||
else
|
||||
foreachSourceName="${1}[@]"
|
||||
|
||||
for foreachIterator in "${!foreachSourceName}"; do
|
||||
foreachEvalString=$(declare -p "$foreachIterator")
|
||||
foreachEvalString="declare -A $3=${foreachEvalString#*=}"
|
||||
eval "$foreachEvalString"
|
||||
echo "$foreachContent" | mo
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# The links are associative arrays
|
||||
declare -A resque hub rip
|
||||
resque=([name]=Resque [url]=http://example.com/resque)
|
||||
hub=([name]=Hub [url]=http://example.com/hub)
|
||||
rip=([name]=Rip [url]=http://example.com/rip)
|
||||
|
||||
# This is a list of the link arrays
|
||||
links=(resque hub rip)
|
||||
|
||||
# Source mo in order to work with arrays
|
||||
. ../mo
|
||||
|
||||
# Process the template
|
||||
cat <<EOF | mo --allow-function-arguments
|
||||
Here are your links:
|
||||
{{#foreach links as link}}
|
||||
* [{{link.name}}]({{link.url}})
|
||||
{{/foreach}}
|
||||
|
||||
EOF
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
cd "$(dirname "$0")"/..
|
||||
|
||||
date-string() { date; }
|
||||
wrapper() { echo -n "*** $1 ***"; }
|
||||
date-string() {
|
||||
date
|
||||
}
|
||||
wrapper() {
|
||||
echo -n "*** $(cat) ***"
|
||||
}
|
||||
|
||||
export IP=127.0.0.1
|
||||
export ALLOWED_HOSTS=( 192.168.0.1 192.168.0.2 192.168.0.3 )
|
||||
|
||||
. mo # Keep in mind this script is executing in the parent directory
|
||||
. ./mo # Keep in mind this script is executing in the parent directory
|
||||
cat <<EOF | mo
|
||||
# {{#wrapper}}OH SO IMPORTANT{{/wrapper}}
|
||||
# This file automatically generated at {{date-string}}
|
||||
|
||||
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
|
||||
|
||||
221
mo
221
mo
@@ -11,12 +11,21 @@
|
||||
#/
|
||||
#/ Simple usage:
|
||||
#/
|
||||
#/ mo [--false] [--help] [--source=FILE] filenames...
|
||||
#/ mo [OPTIONS] filenames...
|
||||
#/
|
||||
#/ --fail-not-set - Fail upon expansion of an unset variable.
|
||||
#/ --false - Treat the string "false" as empty for conditionals.
|
||||
#/ --help - This message.
|
||||
#/ --source=FILE - Load FILE into the environment before processing templates.
|
||||
#/ Options:
|
||||
#/
|
||||
#/ -u, --fail-not-set
|
||||
#/ 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.
|
||||
#/ -h, --help
|
||||
#/ This message.
|
||||
#/ -s=FILE, --source=FILE
|
||||
#/ 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.
|
||||
@@ -29,27 +38,63 @@
|
||||
# Public: Template parser function. Writes templates to stdout.
|
||||
#
|
||||
# $0 - Name of the mo file, used for getting the help message.
|
||||
# --fail-not-set - Fail upon expansion of an unset variable. Default behavior
|
||||
# is to silently ignore and expand into empty string.
|
||||
# --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.
|
||||
# --help - Display a help message.
|
||||
# --source=FILE - Source a file into the environment before processint
|
||||
# template files.
|
||||
# -- - Used to indicate the end of options. You may optionally
|
||||
# use this when filenames may start with two 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_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_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.
|
||||
#
|
||||
# Returns nothing.
|
||||
mo() (
|
||||
@@ -64,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
|
||||
@@ -74,18 +119,32 @@ mo() (
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--fail-not-set)
|
||||
--allow-function-arguments)
|
||||
# shellcheck disable=SC2030
|
||||
MO_ALLOW_FUNCTION_ARGUMENTS=true
|
||||
;;
|
||||
|
||||
-u | --fail-not-set)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FAIL_ON_UNSET=true
|
||||
;;
|
||||
|
||||
--false)
|
||||
-x | --fail-on-function)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FAIL_ON_FUNCTION=true
|
||||
;;
|
||||
|
||||
-e | --false)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FALSE_IS_EMPTY=true
|
||||
;;
|
||||
|
||||
--source=*)
|
||||
-s=* | --source=*)
|
||||
if [[ "$arg" == --source=* ]]; then
|
||||
f2source="${arg#--source=}"
|
||||
else
|
||||
f2source="${arg#-s=}"
|
||||
fi
|
||||
|
||||
if [[ -f "$f2source" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
@@ -97,12 +156,12 @@ mo() (
|
||||
;;
|
||||
|
||||
--)
|
||||
# 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
|
||||
@@ -115,6 +174,44 @@ mo() (
|
||||
)
|
||||
|
||||
|
||||
# Internal: Call a function.
|
||||
#
|
||||
# $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 moContent moFunctionArgs moFunctionResult
|
||||
|
||||
moArgs=()
|
||||
moTrimWhitespace moFunctionArgs "$4"
|
||||
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
|
||||
# Intentionally bad behavior
|
||||
# shellcheck disable=SC2206
|
||||
moArgs=($4)
|
||||
fi
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Scan content until the right end tag is found. Creates an array
|
||||
# with the following members:
|
||||
#
|
||||
@@ -163,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]}}"
|
||||
@@ -234,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"
|
||||
}
|
||||
|
||||
|
||||
@@ -424,6 +522,7 @@ moIsFunction() {
|
||||
local functionList functionName
|
||||
|
||||
functionList=$(declare -F)
|
||||
# shellcheck disable=SC2206
|
||||
functionList=( ${functionList//declare -f /} )
|
||||
|
||||
for functionName in "${functionList[@]}"; do
|
||||
@@ -515,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() {
|
||||
@@ -526,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
|
||||
|
||||
@@ -567,7 +666,7 @@ moLoop() {
|
||||
moParse() {
|
||||
# Keep naming variables mo* here to not overwrite needed variables
|
||||
# used in the string replacements
|
||||
local moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag
|
||||
local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag
|
||||
|
||||
moCurrent=$2
|
||||
moIsBeginning=$3
|
||||
@@ -585,15 +684,20 @@ moParse() {
|
||||
# Sets context
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
moTrimWhitespace moTag "${moTag:1}"
|
||||
|
||||
# Split arguments from the tag name. Arguments are passed to
|
||||
# functions.
|
||||
moArgs=$moTag
|
||||
moTag=${moTag%% *}
|
||||
moTag=${moTag%%$'\t'*}
|
||||
moArgs=${moArgs:${#moTag}}
|
||||
moFindEndTag moBlock "$moContent" "$moTag"
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
|
||||
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=$($moTag "${moBlock[0]}")
|
||||
moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs"
|
||||
moParse "$moContent" "$moCurrent" false
|
||||
moContent="${moBlock[2]}"
|
||||
elif moIsArray "$moTag"; then
|
||||
@@ -658,11 +762,16 @@ moParse() {
|
||||
moContent="${moTag:1}"'}}'"$moContent"
|
||||
moSplit moContent "$moContent" '}}}'
|
||||
moTrimWhitespace moTag "${moContent[0]}"
|
||||
moArgs=$moTag
|
||||
moTag=${moTag%% *}
|
||||
moTag=${moTag%%$'\t'*}
|
||||
moArgs=${moArgs:${#moTag}}
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
moContent=${moContent[1]}
|
||||
|
||||
# Now show the value
|
||||
moShow "$moTag" "$moCurrent"
|
||||
# Quote moArgs here, do not quote it later.
|
||||
moShow "$moTag" "$moCurrent" "$moArgs"
|
||||
;;
|
||||
|
||||
'&'*)
|
||||
@@ -676,8 +785,14 @@ moParse() {
|
||||
*)
|
||||
# Normal environment variable or function call
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
moArgs=$moTag
|
||||
moTag=${moTag%% *}
|
||||
moTag=${moTag%%$'\t'*}
|
||||
moArgs=${moArgs:${#moTag}}
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
moShow "$moTag" "$moCurrent"
|
||||
|
||||
# Quote moArgs here, do not quote it later.
|
||||
moShow "$moTag" "$moCurrent" "$moArgs"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -715,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]}}
|
||||
@@ -763,29 +879,30 @@ moPartial() {
|
||||
#
|
||||
# $1 - Name of environment variable or function
|
||||
# $2 - Current context
|
||||
# $3 - Arguments string if $1 is a function
|
||||
#
|
||||
# Returns nothing.
|
||||
moShow() {
|
||||
# Namespace these variables
|
||||
local moJoined moNameParts
|
||||
local moJoined moNameParts moContent
|
||||
|
||||
if moIsFunction "$1"; then
|
||||
CONTENT=$($1 "")
|
||||
moParse "$CONTENT" "$2" false
|
||||
moCallFunction moContent "$1" "" "$3"
|
||||
moParse "$moContent" "$2" false
|
||||
return 0
|
||||
fi
|
||||
|
||||
moSplit moNameParts "$1" "."
|
||||
|
||||
if [[ -z "${moNameParts[1]}" ]]; then
|
||||
if [[ -z "${moNameParts[1]-}" ]]; then
|
||||
if moIsArray "$1"; then
|
||||
eval moJoin moJoined "," "\${$1[@]}"
|
||||
echo -n "$moJoined"
|
||||
else
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -z "$MO_FAIL_ON_UNSET" ]] || moTestVarSet "$1"; then
|
||||
if moTestVarSet "$1"; then
|
||||
echo -n "${!1}"
|
||||
else
|
||||
elif [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
|
||||
echo "Env variable not set: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -846,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]}}"
|
||||
@@ -972,11 +1090,14 @@ moTrimWhitespace() {
|
||||
# Returns nothing.
|
||||
moUsage() {
|
||||
grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
|
||||
echo ""
|
||||
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.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,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%/*}"
|
||||
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
|
||||
13
tests/function-args.env
Normal file
13
tests/function-args.env
Normal file
@@ -0,0 +1,13 @@
|
||||
name=Willy
|
||||
MO_ALLOW_FUNCTION_ARGUMENTS=true
|
||||
|
||||
pipeTo() {
|
||||
cat | "$1"
|
||||
}
|
||||
|
||||
testArgs() {
|
||||
printf "%d" "$#"
|
||||
|
||||
# Display all arguments
|
||||
printf " %q" ${@+"$@"}
|
||||
}
|
||||
4
tests/function-args.expected
Normal file
4
tests/function-args.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
No args: 0 '' - done
|
||||
One arg: 1 one - done
|
||||
Getting name in a string: 1 The\ name\ is\ Willy - done
|
||||
Reverse this: edcba
|
||||
4
tests/function-args.template
Normal file
4
tests/function-args.template
Normal file
@@ -0,0 +1,4 @@
|
||||
No args: {{testArgs}} - done
|
||||
One arg: {{testArgs one}} - done
|
||||
Getting name in a string: {{testArgs "The name is $name"}} - done
|
||||
Reverse this: {{#pipeTo rev}}abcde{{/pipeTo}}
|
||||
@@ -1,5 +1,5 @@
|
||||
name=Willy
|
||||
wrapped() {
|
||||
# The final newline is eaten by mo
|
||||
echo "<b>$1</b>"
|
||||
# This eats the newline in the content
|
||||
echo "<b>$(cat)</b>"
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
<b> Willy is awesome.
|
||||
</b>... this is the last line.
|
||||
<b> Willy is awesome.</b>... this is the last line.
|
||||
|
||||
@@ -9,9 +9,20 @@ Learn more about mustache templates at https://mustache.github.io/
|
||||
|
||||
Simple usage:
|
||||
|
||||
mo [--false] [--help] [--source=FILE] filenames...
|
||||
mo [OPTIONS] filenames...
|
||||
|
||||
--fail-not-set - Fail upon expansion of an unset variable.
|
||||
--false - Treat the string "false" as empty for conditionals.
|
||||
--help - This message.
|
||||
--source=FILE - Load FILE into the environment before processing templates.
|
||||
Options:
|
||||
|
||||
-u, --fail-not-set
|
||||
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.
|
||||
-h, --help
|
||||
This message.
|
||||
-s=FILE, --source=FILE
|
||||
Load FILE into the environment before processing templates.
|
||||
Can be used multiple times.
|
||||
|
||||
MO_VERSION=2.2.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
../mo --help
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
cd "${0%/*}" || exit 1
|
||||
../mo partial-missing.template 2>&1
|
||||
|
||||
if [[ $? -ne 1 ]]; then
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "${0%/*}"
|
||||
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
|
||||
export ASSOC_ARR=([a]=AAA [b]=BBB)
|
||||
# Can not export associative arrays, otherwise they turn into indexed arrays
|
||||
ASSOC_ARR=([a]=AAA [b]=BBB)
|
||||
|
||||
Reference in New Issue
Block a user