19 Commits

Author SHA1 Message Date
Tyler Akins
9b2f6cea6c Removing debugging line 2019-07-19 21:47:06 -05:00
Tyler Akins
7a8d1d260e Implementing a search path
Closes #30
2019-07-19 21:43:42 -05:00
Tyler Akins
9f6d3bcdab Adding an example
Shows how one can solve #29
2019-07-19 20:22:27 -05:00
Tyler Akins
eac2685632 Adding an example for adding a comma
This closes #26.
2019-04-14 06:25:06 -05:00
Tyler Akins
505570a57b Adding short options
Feature request #25
2019-03-13 20:38:00 -05:00
Tyler Akins
aabc62f1d6 Merge pull request #22 from mamercad/mamercad-curl-follow-redirects
curl needs to follow redirects
2018-03-13 07:25:13 -05:00
Mark Mercado
e672bda163 curl needs to follow redirects 2018-03-13 08:11:43 -04:00
Tyler Akins
dbefade193 Adding a version number
It is shown with --help. It's also available as $MO_VERSION when mo is
sourced.
2017-11-30 05:43:10 -06:00
Tyler Akins
57a8d41394 Merge pull request #21 from jas99/patch-1
Fix: Intall issue
2017-11-20 07:22:43 -06:00
Jaspreet Singh
5739ccd705 Fix: Intall issue
Added -L flag to curl; so as to allow following redirect.
2017-11-19 15:53:12 +05:30
Tyler Akins
f889c37316 Found another issue with strict mode 2017-11-13 13:53:38 -06:00
Tyler Akins
4f615a0faf More fixes if variables are not set and strict mode is enabled 2017-11-13 10:43:32 -06:00
Tyler Akins
1e91e414cc Fix for when running in strict mode 2017-11-13 10:31:51 -06:00
Tyler Akins
ad98577c42 Changing how functions are called
Old way:

    functionName CONTENT argument1 argument2

New way:

    echo -n "$CONTENT" | functionName argument1 argument2

This follow the Unix style more closely.
2017-11-13 10:13:56 -06:00
Tyler Akins
e78b15d95b Updating test to enable the flag 2017-11-03 19:38:34 -05:00
Tyler Akins
f20e4ae83a Making a bit more safe and disabling an eval by default 2017-11-03 16:45:51 -05:00
Tyler Akins
a5ec7dd740 I think this finally implements arguments to functions
This closes #18.
2017-11-03 16:34:08 -05:00
Tyler Akins
ab5dfee519 Fixing install directions 2017-10-24 07:47:50 -05:00
Tyler Akins
81fd601ebd Shortening URL 2017-10-23 15:11:23 -05:00
25 changed files with 454 additions and 140 deletions

187
API.md
View File

@@ -4,60 +4,94 @@ 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.
* --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.
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_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_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
-----
`files`
-------
After we encounter two hyphens together, all the rest of the arguments are files.
MO_FAIL_ON_UNSET
----------------
`MO_ALLOW_FUNCTION_ARGUMENTS`
-----------------------------
shellcheck disable=SC2030
MO_FALSE_IS_EMPTY
-----------------
`MO_FAIL_ON_UNSET`
------------------
shellcheck disable=SC2030
doubleHyphens
-------------
`MO_FALSE_IS_EMPTY`
-------------------
shellcheck disable=SC2030
`doubleHyphens`
---------------
Set a flag indicating we've encountered double hyphens
files
-----
`files`
-------
Every arg that is not a flag or a option should be a file
moFindEndTag()
--------------
`moProcessSearchPath()`
-----------------------
Internal: Change relative paths into absolute paths
`moCallFunction()`
------------------
Internal: Call a function.
* $1 - Function to call
* $2 - Content to pass
* $3 - Additional arguments as a single string
This can be dangerous, especially if you are using tags like {{someFunction ; rm -rf / }}
Returns nothing.
`moFindEndTag()`
----------------
Internal: Scan content until the right end tag is found. Creates an array with the following members:
@@ -75,8 +109,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 +121,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 +133,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 +144,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 +156,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 +175,8 @@ Examples
Returns nothing.
moIndirectArray()
-----------------
`moIndirectArray()`
-------------------
Internal: Send an array as a variable up to caller of a function
@@ -161,8 +195,8 @@ Examples
Returns nothing.
moIsArray()
-----------
`moIsArray()`
-------------
Internal: Determine if a given environment variable exists and if it is an array.
@@ -173,16 +207,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 +236,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 +257,8 @@ Examples
Returns nothing.
moJoin()
--------
`moJoin()`
----------
Internal: Join / implode an array
@@ -235,8 +269,8 @@ Internal: Join / implode an array
Returns nothing.
moLoadFile()
------------
`moLoadFile()`
--------------
Internal: Read a file into a variable.
@@ -246,8 +280,8 @@ Internal: Read a file into a variable.
Returns nothing.
moLoop()
--------
`moLoop()`
----------
Internal: Process a chunk of content some number of times. Writes output to stdout.
@@ -258,8 +292,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 +304,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 +331,14 @@ Prefix all variables.
Returns nothing.
moShow()
--------
`IFS`
-----
Search the path for the file
`moShow()`
----------
Internal: Show an environment variable or the output of a function to stdout.
@@ -300,12 +346,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 +364,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 +378,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 +391,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 +405,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 +415,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 +429,8 @@ Internal: Trim the leading whitespace only.
Returns nothing.
moTrimWhitespace()
------------------
`moTrimWhitespace()`
--------------------
Internal: Trim leading and trailing whitespace from a string.
@@ -393,8 +440,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,8 +450,8 @@ 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

View File

@@ -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
@@ -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
-----------

View File

@@ -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
View 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
View 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

View File

@@ -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}}

View File

@@ -0,0 +1,3 @@
* Child1
{{> deeper/child2 }}
{{> ../relative-templates/deeper/child2 }}

View File

@@ -0,0 +1 @@
* Child3

View File

@@ -0,0 +1,2 @@
* Child2
{{> ../child3 }}

View File

@@ -0,0 +1,7 @@
This is "file"
Child1 in different ways
{{> child1 }}
{{> ./child1 }}
{{> ../relative-templates/child1 }}
{{> deeper/../child1 }}

199
mo
View File

@@ -11,12 +11,20 @@
#/
#/ 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.
#/ -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.
#/ -p=PATH, --path=PATH
#/ - Set a colon-delimited list of folders to search for templates.
#
# Mo is under a MIT style licence with an additional non-advertising clause.
# See LICENSE.md for the full text.
@@ -29,33 +37,50 @@
# 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
# --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.
# -- - Used to indicate the end of options. You may optionally
# use this when filenames may start with two hyphens.
# --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.
#
# 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.
#
# Returns nothing.
# 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.
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
local moContent f2source files doubleHyphens paths
IFS=$' \n\t'
files=()
@@ -74,18 +99,23 @@ 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)
-e | --false)
# shellcheck disable=SC2030
MO_FALSE_IS_EMPTY=true
;;
--source=*)
f2source="${arg#--source=}"
-s=* | --source=*)
f2source="${arg#*=}"
if [[ -f "$f2source" ]]; then
# shellcheck disable=SC1090
@@ -96,6 +126,10 @@ mo() (
fi
;;
-p=* | --path=*)
MO_SEARCH_PATH="$(moProcessSearchPath "${arg#*=}")"
;;
--)
# Set a flag indicating we've encountered double hyphens
doubleHyphens=true
@@ -115,6 +149,45 @@ 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
#
# This can be dangerous, especially if you are using tags like
# {{someFunction ; rm -rf / }}
#
# Returns nothing.
moCallFunction() {
local moArgs
moArgs=()
# shellcheck disable=SC2031
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
moArgs=$3
fi
echo -n "$2" | eval "$1" "$moArgs"
}
# Internal: Scan content until the right end tag is found. Creates an array
# with the following members:
#
@@ -567,7 +640,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,6 +658,13 @@ 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"
@@ -593,7 +673,7 @@ moParse() {
if moIsFunction "$moTag"; then
#: Consider piping the output to moGetContent
#: so the lambda does not execute in a subshell?
moContent=$($moTag "${moBlock[0]}")
moContent=$(moCallFunction "$moTag" "${moBlock[0]}" "$moArgs")
moParse "$moContent" "$moCurrent" false
moContent="${moBlock[2]}"
elif moIsArray "$moTag"; then
@@ -658,11 +738,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 +761,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
@@ -733,7 +824,39 @@ moPartial() {
(
# It would be nice to remove `dirname` and use a function instead,
# but that's difficult when you're only given filenames.
cd "$(dirname -- "$moFilename")" || exit 1
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
moUnindented="$(
moLoadFile moPartial "${moFilename##*/}" || exit 1
moParse "${moPartial}" "$6" true
@@ -763,6 +886,7 @@ moPartial() {
#
# $1 - Name of environment variable or function
# $2 - Current context
# $3 - Arguments string if $1 is a function
#
# Returns nothing.
moShow() {
@@ -770,22 +894,22 @@ moShow() {
local moJoined moNameParts
if moIsFunction "$1"; then
CONTENT=$($1 "")
CONTENT=$(moCallFunction "$1" "" "$3")
moParse "$CONTENT" "$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
@@ -972,11 +1096,14 @@ moTrimWhitespace() {
# Returns nothing.
moUsage() {
grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
echo ""
set | grep ^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"
# If sourced, load all functions.
# If executed, perform the actions as expected.

View File

@@ -1 +1 @@
cat: --help: No such file or directory
File does not exist: /home/fidian/repo/mo/tests/--help

View File

@@ -2,4 +2,4 @@
# This should display a message indicating that the file --help
# could not be found. It should not display a help messsage.
cd "${0%/*}"
../mo -- --help 2>&1
../mo -u -- --help 2>&1

13
tests/function-args.env Normal file
View File

@@ -0,0 +1,13 @@
name=Willy
MO_ALLOW_FUNCTION_ARGUMENTS=true
pipeTo() {
cat | "$1"
}
testArgs() {
printf "%d" "$#"
# Display all arguments
printf " %q" ${@+"$@"}
}

View 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

View 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}}

View File

@@ -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>"
}

View File

@@ -1,2 +1 @@
<b> Willy is awesome.
</b>... this is the last line.
<b> Willy is awesome.</b>... this is the last line.

View File

@@ -9,9 +9,19 @@ 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.
-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.
-p=PATH, --path=PATH
- Set a colon-delimited list of folders to search for templates.
MO_VERSION=2.0.4

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
cd "${0%/*}"
../mo --help
../mo -u --help

View File

@@ -1 +0,0 @@
cat: --something: No such file or directory

View File

@@ -1 +1 @@
cat: partial-missing.partial: No such file or directory
File does not exist: /home/fidian/repo/mo/tests/partial-missing.partial

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env bash
cd "${0%/*}"
../mo partial-missing.template 2>&1
../mo -u partial-missing.template 2>&1
returned=$?
if [[ $? -ne 1 ]]; then
echo "Did not return 1"
if [[ $returned -ne 1 ]]; then
echo "Did not return 1. Instead, returned $returned."
fi

View File

@@ -1,7 +1,8 @@
#!/usr/bin/env bash
cd "${0%/*}"
cat <<EOF | ../mo --source=source.vars
. ../mo
cat <<EOF | mo --source=source.vars
{{VAR}}
{{#ARR}}
* {{.}}

View File

@@ -1,4 +1,4 @@
export VAR=value
export ARR=(1 2 3)
declare -A ASSOC_ARR
export ASSOC_ARR=([a]=AAA [b]=BBB)
ASSOC_ARR=([a]=AAA [b]=BBB)