Clojure wrapper around Jtwig to make using it in Clojure applications simple. It also provides some extra functions on top of what Jtwig provides already. As well, it adds a simple template caching layer that works out-of-the-box when rendering templates from files.
For web apps built on [Ring](https://github.com/ring-clojure/ring) and [Compojure](https://github.com/weavejester/compojure), you can do something like:
You will also probably want to add `clj-jtwig.web.middleware/wrap-servlet-context-path` to your [Ring middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) so that template functions such as `path`, `javascript` and `stylesheet` can automatically prepend the servlet context path to urls you provide to your web app's resources.
If you need to output the servlet context path in one of your templates (e.g. for use with Javascript code that performs AJAX requests), then you can either update the `render` function example provided above to `assoc` the value of `clj-jtwig.web.middleware/*servlet-context-path*` to the `params` map. Then you can refer to this value in your templates the same way as any other value you pass in. Or you can do something like this in one of your templates:
```html
<scripttype="text/javascript">
var context = "{{ path('') }}";
</script>
```
Which will make a global variable `context` available to your Javascript code which will have the value of the servlet context path.
A number of functions are provided out of the box by Jtwig. A few more are provided to fill in some gaps by clj-jtwig. The following is a list of all the functions available with clj-jtwig.
| batch | `batch(items, batch_size)`<br/>`batch(items, batch_size, filler_item)`<br/>"Batches" items by returning a list of lists with the given number of items. If you provide a second parameter, it is used to fill missing items.
| butlast | `butlast(collection)`<br/>`butlast(string)`<br/>`butlast(values, ...)`<br/>Returns all items except for the last one from a collection, series of values, or a string. If a string is passed, it will be treated as a collection of chars.
| capitalize | `capitalize(string)`<br/>Capitalizes a value. The first character will be uppercase, all others lowercase.
| center | `center(string, max_width)`<br/>`center(string, max_width, padding_string)`<br/>Pads a string with whitespace on the left and right as necessary to 'center' the given value. If the padding_string argument is provided, that string will be used to pad instead of whitespace.
| concat | `concat(values, ...)`<br/>Concatenates any number of values together as strings.
| convert_encoding | `convert_encoding(string, output_charset, input_charset)`<br/>Converts a string from one encoding to another. The first argument is the expected output charset and the second one is the input charset.
| date_format | `date_format(date)`<br/>`date_format(date, format)`<br/>Formats a date to a given format. The format specifier is the same as supported by `SimpleDateFormat`. If the format argument is not specified, the format used will be `yyyy-MM-dd HH:mm:ss`. The date argument should be an instance of `java.util.Date`.
| date_modify | `date_modify(date, modifier)`<br/>Modifies a date with a given modifier string. The modifier string can be things like "+1 day" or "+30 minutes". Recognized modifiers are 'seconds', 'minutes', 'hours', 'days', 'months' or 'years'. The date argument should be an instance of `java.util.Date`. A new instance of `java.util.Date` is returned.
| default | `default(value, default_value)`<br/>Returns the passed default value if the value is undefined or empty, otherwise the value of the variable.
| dump | `dump(value)`<br/>Uses `clojure.pprint/pprint` to dump the entire value of a variable to a string and returns that string.
| escape | `escape(string)`<br/>`escape(string, strategy)`<br/>Escapes a string for safe insertion into the final output. The optional strategy parameter specifies the escape strategy: 'html' (default), 'js' or 'xml'.
| first | `first(collection)`<br/>`first(string)`<br/>`first(values, ...)`<br/>Returns the first "element" of a collection, series of values, or a string (in which case the first character is returned).
| format | `format(format_string, values, ...)`<br/>Formats a given string by replacing the placeholders (placeholders follow the `String.format` notation). The values provided will be used in order for each placeholder in the string.
| javascript | `javascript(url)`<br/>Returns a `<script>` tag for a Javascript source file. The Javascript source file's modification timestamp will be appended to the URL if the url given is a relative one to help avoid browser caching issues.
| join | `join(sequence)`<br/>`join(sequence, separator)`<br/>Returns a string which is the concatenation of the items of a sequence. The separator argument specifies a string to use to place in between each joined item. If not specified, a blank string is used as the separator.
| json_encode | `json_encode(string)`<br/>Returns the JSON representation of the given value.
| keys | `keys(map)`<br/>Returns the keys of a map as a collection. It is useful when you want to iterate over the keys of a map.
| last | `last(collection)`<br/>`last(string)`<br/>`last(values, ...)`<br/>Returns the last "element" of a collection, series of values, or a string (in which case, the last character is returned).
| length | `length(collection)`<br/>`length(string)`<br/>`length(values, ...)`<br/>Returns the number of items in a collection, series of values, or the length of a string.
| lower | `lower(string)`<br/>Converts a string to lowercase.
| max | `max(collection)`<br/>`max(values, ...)`<br/>Returns the biggest value in a collection or a set of values.
| merge | `merge(first_collection, second_collection)`<br/>Merges a collection with another collection.
| min | `min(collection)`<br/>`min(values, ...)`<br/>Returns the lowest value in a collection or a set of values.
| nth | `nth(collection, index)`<br/>`nth(collection, index, value_if_not_found)`<br/>Returns a value from a list corresponding with the index specified. If the value_if_not_found argument is not specified and the index provided is out of bounds, an exception will be thrown.
| number_format | `number_format(number)`<br/>`number_format(number, num_decimals)`<br/>`number_format(number, num_decimals, decimal_point_char)`<br/>`number_format(number, num_decimals, decimal_point_char, thousand_sep_char)`<br/>Formats numbers. You can control the number of decimal places, decimal point, and thousands separator using the arguments. The default values for the second, third and fourth values are '0', '.' and ',', respectively.
| path | `path(url)`<br/>Returns a path with the current servlet context prepended. Requires the use of the `clj-jtwig.web.middleware/wrap-servlet-context-path` Ring middleware to work properly.
| pad_left | `pad_left(string, max_width)`<br/>`pad_left(string, max_width, padding_string)`<br/>Pads a string with leading whitespace as necessary until the string and whitespace combined are max_width characters in length. If the padding_string argument is specified, it will be used to pad the string instead of whitespace.
| pad_right | `pad_right(string, max_width)`<br/>`pad_right(string, max_width, padding_string)`<br/>Pads a string with trailing whitespace as necessary until the string and whitespace combined are max_width characters in length. If the padding_string argument is specified, it will be used to pad the string instead of whitespace.
| random | `random(collection)`<br/>`random(string)`<br/>`random(values, ...)`<br/>`random(number)`<br/>Returns a random item from a collection or set of values. If an single number argument is provided, returns a random number from 0 to the number specified. If a string is specified, a random character from that string is returned.
| range | `range(low, high)`<br/>`range(low, high, step)`<br/>Returns a list containing an arithmetic progression of integers. The step argument specifies how to count from low to high, which by default is 1.
| rest | `rest(collection)`<br/>`rest(string)`<br/>`rest(values, ...)`<br/>Returns all the items except for the first one from a collection, series of values, or a string. If a string is passed, it will be treated as a collection of chars.
| reverse | `reverse(collection)`<br/>`reverse(string)`<br/>`reverse(values, ...)`<br/>Reverses the items in a collection, series of values, or a string.
| round | `round(number)`<br/>`round(number, rounding_method)`<br/>Rounds a number to using the rounding method specified. Allowed rounding methods are 'common', 'ceil' and 'floor' with the default being 'common'.
| second | `second(collection)`<br/>`second(string)`<br/>`second(values, ...)`<br/>Returns the second item of a collection, series of values, or a string.
| slice | `slice(collection, start, length)`<br/>`slice(string, start, length)`<br/>Extracts a slice of a collection, or a string where the 2 last arguments specify the start and end indices respectively.
| sort | `sort(collection)`<br/>`sort(values, ...)`<br/>Sorts a collection or a set of values in ascending order. *Note that this differs in behaviour from Jtwig 2.2.0 in that the sorted collection returned from this is a new collection. The input collection is _not_ modified at all by this function.*
| sort_descending | `sort_descending(collection)`<br/>`sort_descending(values, ...)`<br/>Sorts a collection or a set of values in descending order.
| sort_by | `sort_by(collection_of_maps, sort_key)`<br/>Sorts a collection of maps in ascending order. The second argument specifies the key who's value in each map is to be used for sorting comparisons.
| sort_descending_by | `sort_descending_by(collection_of_maps, sort_key)`<br/>Sorts a collection of maps in descending order. The second argument specifies the key who's value in each map is to be used for sorting comparisons.
| stylesheet | `stylesheet(url)`<br/>`stylesheet(url, media)`<br/>Returns a `<link>` tag for a CSS stylesheet with an optional 'media' attribute value. The CSS file's modification timestamp will be appended to the URL if the url given is a relative one to help avoid browser caching issues.
| title | `title(string)`<br/>Returns a titlecased version of the value. Words will start with uppercase letters, all remaining characters are lowercase.
| trim | `trim(string)`<br/>Strips whitespace (or other characters) from the beginning and end of a string.
| upper | `upper(string)`<br/>Converts a value to uppercase.
| url_encode | `url_encode(string)`<br/>`url_encode(map)`<br/>Percent encodes a given string as a URL segment or a map of key/values as a query string (e.g. in a `key=value&key=value ...` format).
| wrap | `wrap(string, max_width)`<br/>`wrap(string, max_width, wrap_long_words)`<br/>`wrap(string, max_width, wrap_long_words, new_line_string)`<br/>Wraps the given text to the maximum width specified. If wrap_long_words is true, then long words/text such as URLs will also be cut and wrapped as necessary. new_line_string can be specified to use a different character/string as the new line separator (by default it will be the system new line character(s)).
template file is rendered, the source file on disk is checked to see if it has been modified since it was last cached.
If it has been we re-load, compile and cache it before rendering it again. As long as the file has not been modified the previously compiled result is re-used.
## Jtwig is a Java library. What does that mean for Clojure?
Jtwig is written in Java. This has important implications for the way that you pass model maps from Clojure to your Jtwig templates.
Jtwig uses a `HashMap<String, Object>` object to hold template model data [as can be seen here](https://github.com/lyncode/jtwig/blob/master/jtwig-core/src/main/java/com/lyncode/jtwig/JtwigModelMap.java). This is fine for Jtwig as that's pretty idiomatic for Java apps. Idiomatic Clojure code using maps will typically use keywords as the keys of a map. This doesn't translate so nicely when you pass in a Clojure map with all keywords as the keys to a template and the template attempts to reference values inside the map.
Ideally, you would expect to be able to do this in Clojure:
Instead, "null" will get output. This is because Jtwig is trying to resolve values in the model map assuming the keys are strings and Java doesn't have Clojure's keywords natively of course. To try to help you out with this, when you call `render`, `render-file` or `render-resource` in clj-jtwig, by default it will call `clojure.walk/stringify-keys` on the map you pass in.
If the `get_bar` function had been written like this instead:
```clojure
(deftwigfn "get_bar" [m]
(get m "bar"))
```
Then you would get the expected output of "hello".
To help avoid such confusion, you could make sure that the maps you pass in to clj-jtwig are already using strings for keys and not rely on the `stringify-keys` call. If you would like to write your code like this, you can optionally pass `{:skip-model-map-stringify? true}` as the third argument to any of the render functions in clj-jtwig and it will not call `stringify-keys`, which will cause exceptions to be thrown if you pass a map using keyword keys (because it will try to cast from a keyword to a string).
TL;DR, even if you pass Clojure maps using keywords for all your keys, clj-jtwig converts all of them to strings to play nice with Jtwig. Your custom template functions written in Clojure should assume any maps it receives as arguments have all their keys as strings instead of keywords.