[Mustache] templates are simple, logic-less templates. Because of their simplicity, they are able to be ported to many languages. The syntax is quite simple.
The above file is [`demo/fun-trip.mo`](demo/fun-trip.mo). Let's try using this template some data from bash's environment. Go to your checked out copy of the project and run a command like this:
This bash version supports conditionals, functions (both as filters and as values), as well as indexed arrays (for iteration). You are able to leverage these additional features by adding more information into the environment. It is easiest to do this when you source `mo`. See the [demo scripts](demo/) for further examples.
There are a few ways you can install this tool. How you install it depends on how you want to use it.
### Globally; For Everyone
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.
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/`.
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.
If you only plan using strings and numbers, nothing could be simpler. In your shell script you can choose to export the variables. The below script is [`demo/using-strings`](demo/using-strings).
The result? You get a list of the five elements in the array. It is vital that you source `mo` and run the function when you want arrays to work because you can not execute a command and have arrays passed to that command's environment. Instead, we first source the file to load the function and then run the function directly.
There are several functions and variables used to process templates. `mo` reserves variables that start with `MO_` for variables exposing data or configuration, functions starting with `mo::`, and local variables starting with `mo[A-Z]`. You are welcome to use internal functions, though only ones that are marked as "Public" should not change their interface. Scripts may also read any of the 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_DEBUG` - When set to a non-empty value, additional debug information is written to stderr.
*`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.
I admit that implementing everything in bash just doesn't make a lot of sense. For example, the following things just don't work because they don't really mesh with the "bash way".
* 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.
* 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<`".
### General Scripting Issues
* Using binary files as templates is simply not allowed.
* Bash does not support anything more complex than strings/numbers inside of associative arrays. I'm not able to add objects nor nested arrays to bash - it's just a shell after all!
* You must make sure the data is in the environment when `mo` runs. The easiest way to do that is to source `mo` in your shell script after setting up lots of other environment variables / functions.
Developing
----------
Check out the code and hack away. Please add tests to show off bugs before fixing them. New functionality should also be covered by a test.
First, make sure you install Node.js. After that, run `npm run install-tests` to get the dependencies and the repository of YAML tests. Run `npm run test` to run the JavaScript tests. There's over 100 of them, which is great. Not all of them will pass, but that's discussed later.
When submitting patches, make sure to run them past [ShellCheck] and ensure no problems are found. Also please use Bash 3 syntax if you are manipulating arrays.
There is a diagnostic script that is aimed to help at making sure the internal functions all work when testing the script out on a version of bash or in an environment that is causing issues. When adding new functions, please make sure this script gets updated to properly test your code.
It is acceptable for some of the official spec tests to fail. Anything dealing with multiple levels of objects (eg. `{{a.b.c}}`) and changing the delimiters (`{{= | | =}}`) will fail. Other than that, this bash implementation of the mustache spec should pass tests.
* Interpolation - Multiple Calls: This fails because lambdas execute in a subshell so their output can be captured. This is flagged as a TODO in the code.
* HTML Escaping - Since bash is not often executed in a web server context, it makes no sense to have the output escaped as HTML. Performing shell escaping of variables may be an option in the future if there's a demand.
There's a few places in the code marked with `TODO` to signify areas that could use improvement. Care to help? Keep in mind that this uses bash exclusively, so it might not look the prettiest.