{"id":15010390,"url":"https://github.com/yogthos/selmer","last_synced_at":"2025-05-13T17:08:03.735Z","repository":{"id":9652506,"uuid":"11588994","full_name":"yogthos/Selmer","owner":"yogthos","description":"A fast, Django inspired template system in Clojure.","archived":false,"fork":false,"pushed_at":"2025-01-31T17:16:05.000Z","size":1338,"stargazers_count":1006,"open_issues_count":21,"forks_count":115,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-04-22T06:04:11.015Z","etag":null,"topics":["clojure","html","html-template","template-engine"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yogthos.png","metadata":{"files":{"readme":"README.md","changelog":"changes.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"yogthos"}},"created_at":"2013-07-22T18:36:19.000Z","updated_at":"2025-04-14T21:38:54.000Z","dependencies_parsed_at":"2024-01-31T05:48:19.929Z","dependency_job_id":"7848fb6a-3029-4482-9e7f-7257a979cb41","html_url":"https://github.com/yogthos/Selmer","commit_stats":{"total_commits":744,"total_committers":81,"mean_commits":9.185185185185185,"dds":"0.34543010752688175","last_synced_commit":"0364769ec6d0085c456c87e5e46db5301cebb8ec"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yogthos%2FSelmer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yogthos%2FSelmer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yogthos%2FSelmer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yogthos%2FSelmer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yogthos","download_url":"https://codeload.github.com/yogthos/Selmer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250528868,"owners_count":21445517,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","html","html-template","template-engine"],"created_at":"2024-09-24T19:33:57.371Z","updated_at":"2025-04-23T23:11:12.429Z","avatar_url":"https://github.com/yogthos.png","language":"Clojure","readme":"Selmer\n======\n\n![build status](https://github.com/yogthos/Selmer/actions/workflows/main.yml/badge.svg)\n\nA fast, [Django](https://docs.djangoproject.com/en/dev/ref/templates/builtins/) inspired template system in pure Clojure.\n\n\u003c!-- TOC was generated by Markdown Writer for Atom: https://atom.io/packages/markdown-writer --\u003e\n\u003c!-- TOC depthTo:2 --\u003e\n\n- [Installation](#installation)\n- [Marginalia documentation](#marginalia-documentation)\n- [Usage](#usage)\n- [Error Handling](#error-handling)\n- [Variables and Tags](#variables-and-tags)\n- [Filters](#filters)\n- [Tags](#tags)\n- [Template Inheritance](#template-inheritance)\n- [Missing values](#missing-values)\n- [Internationalization](#internationalization)\n- [License](#license)\n\n\u003c!-- /TOC --\u003e\n\n\n## Installation\n\n#### Leiningen\n\n[![Clojars Project](http://clojars.org/selmer/latest-version.svg)](http://clojars.org/selmer)\n\n#### tools.deps\n\n```edn\n{selmer {:mvn/version \"\u003cversion\u003e\"}}\n```\n\n## Marginalia documentation\n\n[Marginalia documentation](https://rawgithub.com/yogthos/Selmer/master/docs/uberdoc.html)\n\n## Usage\n\n### [Jump to Filters](#filters)\n\n#### Built-in Filters\n\n[abbreviate](#abbreviate)\n[add](#add)\n[addslashes](#addslashes)\n[block.super](#blocksuper)\n[capitalize](#capitalize)\n[center](#center)\n[count](#count)\n[count-is](#count-is)\n[currency-format](#currency-format)\n[date](#date)\n[default](#default)\n[default-if-empty](#default-if-empty)\n[double-format](#double-format)\n[email](#email)\n[empty?](#empty)\n[not-empty](#not-empty)\n[first](#first)\n[take](#take)\n[drop](#drop)\n[drop-last](#drop-last)\n[get-digit](#get-digit)\n[hash](#hash)\n[join](#join)\n[json](#json)\n[last](#last)\n[length](#length)\n[length-is](#length-is)\n[linebreaks](#linebreaks)\n[linebreaks-br](#linebreaks-br)\n[linenumbers](#linenumbers)\n[lower](#lower)\n[name](#name)\n[phone](#phone)\n[pluralize](#pluralize)\n[range](#range)\n[rand-nth](#rand-nth)\n[remove](#remove)\n[remove-tags](#remove-tags)\n[safe](#safe)\n[sort](#sort)\n[sort-by](#sort-by)\n[sort-by-reversed](#sort-by-reversed)\n[sort-reversed](#sort-reversed)\n[subs](#subs)\n[sum](#sum)\n[str](#str)\n[title](#title)\n[upper](#upper)\n[urlescape](#urlescape)\n[multiply](#multiply)\n[divide](#divide)\n[round](#round)\n[between?](#between)\n[replace](#replace)\n\n### [Jump to Tags](#tags)\n\n#### Built-in Tags\n\n[block](#block)\n[comment](#comment)\n[cycle](#cycle)\n[debug](#debug)\n[if](#if)\n[ifequal](#ifequal)\n[ifunequal](#ifunequal)\n[include](#include)\n[extends](#extends)\n[firstof](#firstof)\n[for](#for)\n[now](#now)\n[safe](#safe-tag)\n[script](#script)\n[style](#style)\n[verbatim](#verbatim)\n[with](#with)\n\n### [Jump to Template Inheritance](#template-inheritance)\n\n\n### Templates\n\nSelmer templates consist of plain text that contains embedded expression and filter tags. While Selmer\nis primarily meant for HTML generation, it can be used for templating any text.\n\nSelmer compiles the template files and replaces any tags with the corresponding functions for handling\ndynamic content. The compiled template can then be rendered given a context map.\n\nFor example, if we wanted to render a string containing a name variable we could write the following:\n\n```clojure\n(use 'selmer.parser)\n\n(render \"Hello {{name}}!\" {:name \"Yogthos\"})\n=\u003e\"Hello Yogthos!\"\n```\n\nalternatively, it's possible to use string interpolation macro to inject symbols found in the environment directly into the template:\n\n```clojure\n(let [a 1\n      b \"hello\"]\n  (\u003c\u003c \"{{b|upper}}, {{a}} + {{a}} = 2\"))\n\n;;=\u003e \"HELLO, 1 + 1 = 2\"\n```\n\nTo render a file we can call `render-file` instead:\n\n```clojure\n(use 'selmer.parser)\n\n(render-file \"home.html\" {:name \"Yogthos\"})\n```\n\nTo list the declared variables in the template:\n\n```clojure\n(known-variables \"{{name}}\")\n=\u003e#{:name}\n```\n\n#### \\*\\*Important\\*\\*\n\nWhen rendering files Selmer will cache the compiled template. A recompile will be triggered if the last\nmodified timestamp of the file changes. Note that changes in files referenced by the template **will not**\ntrigger a recompile. This means that if your template extends or includes other templates you must touch\nthe file that's being rendered for changes to take effect.\n\nAlternatively you can turn caching on and off using `(selmer.parser/cache-on!)` and\n `(selmer.parser/cache-off!)` respectively.\n\n### Resource Path\n\nBy default the templates are located relative to the `ClassLoader` URL. If you'd like to set a custom location for the\ntemplates, you can use `selmer.parser/set-resource-path!` to do that:\n\n```clojure\n(selmer.parser/set-resource-path! \"/var/html/templates/\")\n```\n\nIt's also possible to set the root template path in a location relative to the resource path of the application:\n\n```clojure\n(set-resource-path! (clojure.java.io/resource \"META-INF/foo/templates\"))\n```\n\nThis allows the templates to be refrerenced using `include` and `extends` tags without having to specify the full path.\n\nTo reset the resource path back to the default simply pass it a `nil`:\n\n```clojure\n(selmer.parser/set-resource-path! nil)\n```\n\nThe application will then look for templates at this location. This can be useful if you're deploying the application\nas a jar and would like to be able to modify the HTML without having to redeploy it.\n\n### Custom Markers\n\nBy default, Selmer uses `{%` and `%}` to indicate the start and the end of an expression, while using `{{` and `}}` for variables.\nThis might conflict with clientside frameworks such as AngularJS. In this case you can specify custom tags by passing\na map containing any of the following keys to the parser:\n\n```clojure\n:tag-open\n:tag-close\n:filter-open\n:filter-close\n:tag-second\n```\n\n```clojure\n(render \"[% for ele in foo %]{{[{ele}]}}[%endfor%]\"\n        {:foo [1 2 3]}\n        {:tag-open \\[\n         :tag-close \\]})\n=\u003e\"{{1}}{{2}}{{3}}\"\n```\n\n### Namespaced Keys\n\nNote that if you're using namespaced keys, such as `:foo.bar/baz`, then you will need to escape the `.` as follows:\n\n```clojure\n(parser/render \"{{foo..bar/baz}}\" {:foo.bar/baz \"hello\"})\n```\n\n## Error Handling\n\nSelmer will attempt to validate your templates by default, if you wish to disable validation for any reason it can be done by\ncalling `(selmer.validator/validate-off!)`.\n\nWhenever an error is detected by the validator an instance of `clojure.lang.ExceptionInfo` will be thrown.\nThe exception will contain the following keys:\n\n* `:type` - `:selmer-validation-error`\n* `:error` - the error message\n* `:error-template` - the error page template\n* `:template` - template file that contains the error\n* `:validation-errors` - a vector of validation errors\n\nEach error in the `:validation-errors` vector is a map containing the details specific to the error:\n\n* `:line` - the line on which the error occurred\n* `:tag` - the tag that contains the error\n\nThe template under the `:error-template` key can be used to render a friendly error page.\nSelmer provides a middleware wrapper for this purpose:\n\n```clojure\n(ns myapp.handler\n  (:require [selmer.middleware :refer [wrap-error-page]]\n            [environ.core :refer [env]]))\n\n...\n\n#(if (env :dev) (wrap-error-page %) %)\n```\n\nThe middleware will render a page like the one below whenever any parsing errors are encountered.\n\n![](https://raw.github.com/yogthos/Selmer/master/error_page.png)\n\n## Variables and Tags\n\nVariables are used to inject dynamic content into the text of the template. The values for the variables\nare looked up in the context map as can be seen in the example above. When a value is missing then an\nempty string is rendered in its place.\n\nBy default variables are defined using the double curly braces: `{{myvar}}`.\n\nA variables can also be nested data structures, eg:\n\n`(render \"{{person.name}}\" {:person {:name \"John Doe\"}})`\n\n`(render \"{{foo.bar.0.baz}}\" {:foo {:bar [{:baz \"hi\"}]}})`\n\nIt works with string keys too. For optimal performance, prefer maps with keyword keys. Occasional\nstring keys are ok, but heavily nested context maps with all string key lookups are slower to render.\n\n`(render \"{{foo.bar.baz}}\" {:foo {:bar {\"baz\" \"hi\"}}})`\n\nTags are used to add various functionality to the template such as looping and conditions.\nFor example, if we wanted to create a list from a collection of items we could use the `for` tag\nas follows:\n\n```xml\n\u003cul\u003e\n{% for item in items %}\n    \u003cli\u003e{{item}}\u003c/li\u003e\n{% endfor %}\n\u003c/ul\u003e\n```\n\n## Filters\n\nIn many cases you may wish to postprocess the value of a variable. For example, you might want to convert\nit to upper case, pluralize it, or parse it as a date. This can be done by specifying a filter following the\nname of the variable. The filters are separated using the `|` character.\n\nFor example, if we wanted to convert the variable to upper case we could write `{{user-name|upper}}`. When\nrendered with `{:user-name \"Yogthos\"}` it would produce `YOGTHOS` as its output.\n\nSome filters can take parameters. `{{domain|hash:\"md5\"}}` rendered with `{:domain \"example.org\"}` would produce\n`1bdf72e04d6b50c82a48c7e4dd38cc69`. If a parameter begins with `@` it will be looked up in the context map and,\nif found, will be replaced with its value before\nbeing passed to the filter function. For example, `@foo.bar` will treated as `(get-in context-map [:foo :bar] \"@foo.bar\")`.\n\nFinally, you can easily register custom filters in addition to those already provided. A filter is simply a function\nthat accepts a value and returns its replacement:\n\n```clojure\n(use 'selmer.filters)\n\n(add-filter! :embiginate clojure.string/upper-case)\n(render \"{{shout|embiginate}}\" {:shout \"hello\"})\n=\u003e\"HELLO\"\n\n(add-filter! :empty? empty?)\n(render \"{{files|empty?}}\" {:files []})\n=\u003e\"true\"\n```\n\nby default the content of the filter will be escaped, if you'd like to make a safe filter then wrap it's body\nin a vector with a `:safe` keyword:\n\n```clojure\n(add-filter! :foo  (fn [x] [:safe (.toUpperCase x)]))\n\n(render \"{{x|foo}}\" {:x \"\u003cdiv\u003eI'm safe\u003c/div\u003e\"})\n=\u003e\"\u003cDIV\u003eI'M SAFE\u003c/DIV\u003e\"\n```\n\nIt is possible to disable escaping (if, for example, your target format is not HTML/XML) using the `selmer.util/without-escaping` macro:\n\n```clojure\n(require '[selmer.util :refer [without-escaping]])\n\n(without-escaping\n  (render \"{{x}}\" {:x \"I \u003c3 NY\"}))\n=\u003e\"I \u003c3 NY\"\n```\n\nAlternatively, you can turn off escaping permanently in all threads with the `selmer.util/turn-off-escaping!` function.\n\n\n### Built-in Filters\n\n#### abbreviate\n\nAbbreviate the input string to given width if it exceeds a maxium\nwidth. If only a maximum width is given, abbreviated and maximum width\nare the same. The first parameter is the maximum width, the optional\nsecond parameter the abbreviated width.\n\n`(render \"{{f|abbreviate:19}}\" {:f \"an abbreviate example text\"}) =\u003e \"an abbreviate ex...\"`\n\n`(render \"{{f|abbreviate:19:12}}\" {:f \"an abbreviate example text\"}) =\u003e \"an abbrev...\"`\n\n`(render \"{{f|abbreviate:26:12}}\" {:f \"an abbreviate example text\"}) =\u003e \"an abbreviate example text\"`\n\nThe last example shows: if the string fits in the maximum width the\nfull string is used even if the abbreviated form would be shorter.\n\nBy default `...` is used as replacement for the abbreviated part of\nthe string. You can easily change that with the `abbr-ellipsis` filter:\n\n`(render \"{{f|abbr-ellipsis:\\\"… etc. pp.\\\"|abbreviate:19}}\" {:f \"an abbreviate example text\"}) =\u003e \"an abbrev… etc. pp.\"`\n\n`(render \"{{f|abbr-ellipsis:\\\"\\\"|abbreviate:19}}\" {:f \"an abbreviate example text\"}) =\u003e \"an abbreviate examp\"`\n\nNote that the ellipsis can't be longer than the abbreviated width.\n\nWith the `abbr-left`, `abbr-right` and `abbr-middle` filters you can\nalso control in which position the abbreviation happens. Filter\n`abbr-right` is provided for completeness, even though it's the\ndefault.\n\n`(render \"{{f|abbr-left|abbreviate:19:12}}\" {:f \"an abbreviate example text\"}) =\u003e \"...mple text\"`\n\n`(render \"{{f|abbr-middle|abbreviate:19}}\" {:f \"an abbreviate example text\"}) =\u003e \"an abbre...ple text\"`\n\nYou also can combine the position and ellipsis filter:\n\n`(render \"{{f|abbr-ellipsis:\\\" \u003c-- snip --\u003e \\\"|abbr-middle|abbreviate:19}}\" {:f \"an abbreviate example text\"}) =\u003e \"an \u0026lt;-- snip --\u0026gt; ext\"`\n\nPlease note that the `abbr-left`, `abbr-right`, `abbr-middle` and\n`abbr-ellipsis` filters can only be used just before an `abbreviate`\nfilter!\n\n#### add\nCan add Integers and Doubles. If one of the parameters cannot be casted into one of the two, all parameters will be concatenated to a String.\n\n`(render \"{{add_me|add:2:3:4}}\" {:add_me 2})` =\u003e `11`\n\n`(render \"{{h|add:e:l:l:o}}\" {:h \"h\"})` =\u003e `\"hello\"`\n\n#### addslashes\nNota bene, the slashes aren't actually in the input string, but they *are* going to be in the input. Just trying to write valid Clojure code.\n\n`(render \"{{name|addslashes}}\" {:name \"\\\"Russian tea is best tea\\\"\"})` =\u003e `\"\\\"Russian tea is best tea\\\"\"`\n\n#### block.super\n\nCan be used inside a block to insert the content from the parent block in its place\n\n`{% block foo %} {{block.super}} some content{% endblock %}`\n\n\n#### capitalize\n`(render \"{{name|capitalize}}\" {:name \"russian tea is best tea\"})` =\u003e `\"Russian tea is best tea\"`\n\n#### center\n`(render \"{{name|center:20}}\" {:name \"yogthos\"})` =\u003e `\"      yogthos     \"`\n\n#### count\n`(render \"{{name|count}}\" {:name \"Yogthos\"})` =\u003e `\"7\"`\n\n`(render \"{{items|count}}\" {:items [1 2 3 4]})` =\u003e `\"4\"`\n\n#### count-is\n`(render \"{{x|count-is:3}}\" {:x [1 2 3]})` =\u003e `\"true\"`\n`(render \"{{x|count-is:0}}\" {})` =\u003e `\"true\"`\n\n#### currency-format\n`\"{{amount|currency-format}}\" {:amount 123})` =\u003e `\"$123.00\"`\n\nUses `java.text.NumberFormat/getCurrencyInstance` for formatting the currency value.\nThe formatter defaults to the default locale for this instance of the Java Virtual Machine.\n\nAn ISO 639 2-letter language code can be added as a locale.\n\n`\"{{amount|currency-format:de}}\" {:amount 123})` =\u003e `\"€ 123,00\"`\n\nAdditionally, the locale can be followed by the country code.\n\n`\"{{amount|currency-format:de:DE}}\" {:amount 123})` =\u003e `\"€ 123,00\"`\n\n#### date\nFormat a date. Supports a number of predefined formats, whose output may differ according to the current locale and / or JVM version. Valid formats are: `shortDate`, `shortTime`, `shortDateTime`, `mediumDate`, `mediumTime`, `mediumDateTime`, `longDate`, `longTime`, `longDateTime`, `fullDate`, `fullTime`, and `fullDateTime`.\n\n`(render \"{{d|date:shortDate}}\" {:d (java.util.Date.)})` =\u003e `\"8/3/13\"`\n\n`(render \"{{d|date:shortDate}}\" {:d nil})` =\u003e `\"\"`\n\nTo more precisely control the output format, pass a format string:\n\n`(render \"{{d|date:\\\"yyyy-MM-dd\\\"}}\" {:d (java.util.Date.)})` =\u003e `\"2013-08-03\"`\n\nAn ISO 639 2-letter language code can be added to force a particular locale:\n\n`(render \"{{now|date:\\\"MMMM\\\":fr}}\" {:now (java.util.Date.)})` =\u003e `\"mars\"`\n\nTo conveniently render the current date, see the [now](#now) tag.\n\n#### default\n`(render \"{{name|default:\"I \u003c3 ponies\"}}\" {:name \"yogthos\"})` =\u003e `\"yogthos\"`\n`(render \"{{name|default:\"I \u003c3 ponies\"}}\" {:name nil})` =\u003e `\"I \u003c3 ponies\"`\n`(render \"{{name|default:\"I \u003c3 ponies\"}}\" {:name []})` =\u003e `\"[]\"`\n\n`(render \"{{name|default:\"I \u003c3 ponies\"}}\" {})` =\u003e `\"I \u003c3 ponies\"`\n\n#### default-if-empty\n`(render \"{{name|default-if-empty:\"I \u003c3 ponies\"}}\" {:name \"yogthos\"})` =\u003e `\"yogthos\"`\n`(render \"{{name|default-if-empty:\"I \u003c3 ponies\"}}\" {:name nil})` =\u003e `\"I \u003c3 ponies\"`\n`(render \"{{name|default-if-empty:\"I \u003c3 ponies\"}}\" {:name []})` =\u003e `\"I \u003c3 ponies\"`\n`(render \"{{name|default-if-empty:\"I \u003c3 ponies\"}}\" {})` =\u003e `\"I \u003c3 ponies\"`\n\n#### double-format\n`(render \"{{tis-a-number|double-format:2}}\" {:tis-a-number 10.00001})` =\u003e `10.00`\n`(render \"{{tis-a-number|double-format}}\" {:tis-a-number 10.00001})` =\u003e `10.0`\n\n#### email\nRenders an email address as a selectable link.\n\n`(render \"{{address|email}}\" {:address \"mickey@disney.com\"})` =\u003e `\u003ca href=\"mickey@disney.com\"\u003emickey@disney.com\u003c/a\u003e`\n\nNota bene, the email filter takes an optional `validate?` argument. If it is present and equal to `false`, the email filter will process any argument as above:\n\n`(render \"{{address|email:false}}\" {:address \"this.is.not.an.email.address\"})` =\u003e `\u003ca href=\"this.is.not.an.email.address\"\u003ethis.is.not.an.email.address\u003c/a\u003e`\n\nHowever, if it is not present or is present but not equal to `false`, an obviously invalid email address will cause an exception to be thrown. Validation is done by a simple regular expression; it will not catch all invalid email addresses.\n\n#### empty?\n`(render \"{% if xs|empty? %}foo{% endif %}\" {:xs []})` =\u003e `\"foo\"`\n\n#### not-empty\n`(render \"{% if xs|not-empty %}foo{% endif %}\" {:xs [1 2]})` =\u003e `\"foo\"`\n\n#### first\n`(render \"{{seq-of-some-sort|first}}\" {:seq-of-some-sort [:dog :cat :bird :bird :bird :is :the :word]})` =\u003e `:dog`\n\n#### take\n`(render \"{{seq-of-some-sort|take:3}}\" {:seq-of-some-sort [:dog :cat :bird :bird :bird :is :the :word]})` =\u003e `[:dog :cat :bird]`\n\n#### drop\n`(render \"{{seq-of-some-sort|drop:4}}\" {:seq-of-some-sort [:dog :cat :bird :bird :bird :is :the :word]})` =\u003e `[:bird :is :the :word]`\n\n`(render \"{{seq-of-some-sort|drop:4}}\" {:seq-of-some-sort [:dog :cat :bird :bird :bird :is :the :word]})` =\u003e `[:bird :is :the :word]`\n\n`(render \"{{seq-of-some-sort|drop:4|join:\\\" \\\"}}\" {:seq-of-some-sort [\"dog\" \"cat\" \"bird\" \"bird\" \"bird\" \"is\" \"the\" \"word\"]})` =\u003e `bird is the word`\n\n#### drop-last\nSimilar to drop:\n\n`(render \"{{seq-of-some-sort|drop-last:4}}\" {:seq-of-some-sort [:dog :cat :bird :bird :bird :is :the :word]})` =\\\u003e `[:dog :cat :bird :bird]`\n\n\n#### get-digit\n`(render \"{{tis-a-number|get-digit:1}}\" {:tis-a-number 12.34567})` =\u003e `7`\n\n#### hash\navailable hashes: `md5`, `sha`, `sha256`, `sha384`, `sha512`\n\n`(render \"{{domain|hash:\\\"md5\\\"}}\" {:domain \"example.org\"})` =\u003e `\"1bdf72e04d6b50c82a48c7e4dd38cc69\"`\n\n\n#### join\n`(render \"{{sequence|join}}\" {:sequence [1 2 3 4]})` =\u003e `\"1234\"`\n`(render \"{{sequence|join:\\\", \\\"}}\" {:sequence [1 2 3 4]})` =\u003e `\"1, 2, 3, 4\"`\n\n#### json\nby default content will be escaped\n\n`(render \"{{data|json}}\" {:data [1 2 {:foo 27 :dan \"awesome\"}]})` =\u003e `\"[1,2,{\u0026quot;foo\u0026quot;:27,\u0026quot;dan\u0026quot;:\u0026quot;awesome\u0026quot;}]\"`\n\nif you wish to render it unescaped use the `safe` filter:\n\n`(render \"{{f|json|safe}}\" {:f {:foo 27 :dan \"awesome\"}})`\n\n\n#### last\n`(render \"{{sequence|last}}\" {:sequence 12.34567})` =\u003e `7`\n`(render \"{{sequence|last}}\" {:sequence [1 2 3 4]})` =\u003e `4`\n\n#### length\n`(render \"{{sequence|length}}\" {:sequence [1 2 3 4]})` =\u003e `4`\n\n#### length-is\n`(render \"{{sequence|length-is:4}}\" {:sequence [1 2 3 4]})` =\u003e `true`\n\n#### linebreaks\nSingle newlines become `\u003cbr /\u003e`, double newlines mean new paragraph. Content will\nbe escaped by default.\n\n`(render \"{{foo|linebreaks|safe}}\" {:foo \"\\nbar\\nbaz\"})` =\u003e `\"\u003cp\u003e\u003cbr /\u003ebar\u003cbr /\u003ebaz\u003c/p\u003e\"`\n\n#### linebreaks-br\nlike `linebreaks` but doesn't insert `\u003cp\u003e` tags.\n`(render \"{{foo|linebreaks-br|safe}}\" {:foo \"\\nbar\\nbaz\"})` =\u003e `\"\u003cbr /\u003ebar\u003cbr /\u003ebaz\"`\n\n#### linenumbers\nDisplays text with line numbers.\n`(render \"{{foo|linenumbers}}\" {:foo \"foo\\n\\bar\\nbaz\"})` =\u003e `\"1. foo\\n2. bar\\n3. baz\"`\n\n#### lower\n`(render \"{{foo|lower}}\" {:foo \"FOOBaR\"})` =\u003e `\"foobar\"`\n\n#### name\n`(render \"{{foo|name}}\" {:foo :foobar})` =\u003e `\"foobar\"`\n\n#### number-format\n`(render \"{{amount|number-format:%.3f}}\" {:amount 123.04455})` =\u003e `\"123.045\"`\n\nAn ISO 639 2-letter language code can be added as a locale.\n\n`(render \"{{amount|number-format:%.3f:de}}\" {:amount 123.04455})` =\u003e `\"123,045\"`\n\n#### phone\nRenders a phone number as a selectable link, for use with telephony systems (including mobile phones).\n\n`(render \"{{number|phone}}\" {:number \"01234 567890\"})` =\u003e `\"\u003ca href='tel:01234-567890'\u003e01234 567890\u003c/a\u003e\"`\n\nThe `phone` filter takes two optional positional arguments:\n\n* `national-prefix` The [ITU-T E.123](https://en.wikipedia.org/wiki/E.123) [international subscriber dialing prefix](https://en.wikipedia.org/wiki/List_of_country_calling_codes) to prepend in place of a leading zero. Default is do not prepend.\n* `validate?` if present and equal to \"false\", do not throw exception if number appears invalid. Default behaviour is do throw an exception.\n\nBoth arguments are optional, but because they are positional the `national-prefix` must come before `validate?` when\nboth arguments are supplied.\n\nThus:\n\n`(render \"{{number|phone:44:true}}\" {:number \"01234 567890\"})` =\u003e `\"\u003ca href='tel:+44-1234-567890'\u003e01234 567890\u003c/a\u003e\"`\n\nValidation is done by a simple regular expression; it will not catch all invalid phone numbers.\n\n#### pluralize\nReturns the correct (English) pluralization based on the variable. This works with many words, but certainly not all (eg. foot/feet, mouse/mice, etc.)\n\n`(render \"{{items|count}} item{{items|pluralize}}\" {:items []})` =\u003e `\"0 items\"`\n\n`(render \"{{items|count}} item{{items|pluralize}}\" {:items [1]})` =\u003e `\"1 item\"`\n\n`(render \"{{items|count}} item{{items|pluralize}}\" {:items [1 2]})` =\u003e `\"2 items\"`\n\n`(render \"{{fruit|count}} tomato{{fruit|pluralize:\\\"es\\\"}}\" {:fruit []})` =\u003e `\"0 tomatoes\"`\n\n`(render \"{{people|count}} lad{{people|pluralize:\\\"y\\\":\\\"ies\\\"}}\" {:people [1]})` =\u003e `\"1 lady\"`\n\n`(render \"{{people|count}} lad{{people|pluralize:\\\"y\\\":\\\"ies\\\"}}\" {:people [1 2]})` =\u003e `\"2 ladies\"`\n\n`(render \"{{people}} lad{{people|pluralize:\\\"y\\\":\\\"ies\\\"}}\" {:people 2})` =\u003e `\"2 ladies\"`\n\n#### range\nreturns a range (sequence) of values from 0 to the value, if no arguments are provided.\n`(render \"{{n|range}}\" {:n 3})` =\u003e `\"(0 1 2)\"`\n\nAccepts a start value and a step value:\n\n`(render \"{{n|range:1}}\" {:n 3})` =\u003e `\"(1 2)\"`\n\n`(render \"{{n|range:0:2}}\" {:n 3})` =\u003e `\"(0 2)\"`\n\nThe value can also be a numeric literal:\n\n`(render \"{{3|range}}\" {})` =\u003e `\"(0 1 2)\"`\n\n`(render \"{{3|range:1}}\" {})` =\u003e `\"(1 2)\"`\n\n`(render \"{{3|range:0:2}}\" {})` =\u003e `\"(0 2)\"`\n\n#### rand-nth\nreturns rand-nths value from a collection:\n`(render \"{{foo|rand-nth}}\" {:foo [1 2 3]})` =\u003e `\"2\"`\n\n#### remove\nremoves specified characters from the string:\n`(render \"{{foo|remove:\\\"aeiou\\\"}}\" {:foo \"abcdefghijklmnop\"})` =\u003e `\"bcdfghjklmnp\"`\n#### remove-tags\nRemoves the specified HTML tags from the string:\n`(render \"{{ value|remove-tags:b:span }}\" {:value \"\u003cb\u003e\u003cspan\u003efoobar\u003c/span\u003e\u003c/b\u003e\"})` =\u003e `\"foobar\"`\n\n#### safe\nBy default Selmer will HTML escape all variables, The `safe` filter exempts the variable from being html-escaped:\n\n`(render \"{{data}}\" {:data \"\u003cfoo\u003e\"})` =\u003e `\"\u0026lt;foo\u0026gt;\"`\n\n`(render \"{{data|safe}}\" {:data \"\u003cfoo\u003e\"})` =\u003e `\"\u003cfoo\u003e\"`\n\n#### sort\n`(render \"{{ value|sort }}\" {:value [1 4 2 3 5]})` =\u003e `\"(1 2 3 4 5)\"`\n\n#### sort-by\n`(render \"{{ value|sort-by:name }}\" {:value [{:name \"John\"} {:name \"Jane\"}]})` =\u003e `\"({:name \u0026quot;Jane\u0026quot;} {:name \u0026quot;John\u0026quot;})\"`\n\n#### sort-reversed\nsame as sort, but in reverse order\n\n#### sort-by-reversed\nsame as sort-by, but in reverse order\n\n#### str\nLike the clojure function `str`. So you can do crazy stuff like:\n`(render \"{{people|length-is:2|last|str|join:\\\"-\\\"}} lad{{people|pluralize:\\\"y\\\":\\\"ies\\\"}}\" {:people [1 2]})` =\u003e `\"t-r-u-e ladies\"`\nWithout raising an exception.\n\n#### subs\nLike the clojure function `subs`.\n `(render \"{{s|subs:0:3:\\\" ...\"}}\", {:s \"Foo bar baz\"})` =\u003e `\"Foo ...\"`\n\n#### title\nCapitalize the words of a string\n`(render \"{{s|title}}\" {:s \"my fancy title\"})` =\u003e `\"My Fancy Title\"`\n\n#### upper\n`(render \"{{shout|upper}}\" {:shout \"hello\"})` =\u003e `\"HELLO\"`\n\n#### urlescape\n`(render \"{{data|urlescape}}\" {:data \"clojure url\"})` =\u003e `\"clojure+url\"`\n\n#### round\n\nReturns the closest integer to the argument, with ties rounding up.\n\n`(render \"{{foo|round}}\" {:foo 3.33333})` =\\\u003e `\"3\"`\n\n#### multiply\n\nMultiplies the value by the given number. Throws error if one of the both\nvalues are neither Long nor Double.\n\n`(render \"{{foo|multiply:2}}\" {:foo 3.3})` =\\\u003e `\"6.6\"`\n\n#### divide\n\nMultiplies the value by the given number. Throws error if one of the both\nvalues are neither Long nor Double.\n\n`(render \"{{foo|divide:2}}\" {:foo 6})` =\\\u003e `\"3\"`\n\n#### replace\n\nReplaces all occurrences of the first string with the second string.\n\n`(render \"{{foo|replace:foo:bar}}\" {:foo \"foo test foo ...\"})` =\\\u003e `\"bar test\nbar ...\"`\n\n#### between?\n\n`(render \"{{foo|between?:2:4}}\" {:foo 3})` =\\\u003e `\"true\"`\n\n\n## Tags\n\nSelmer supports two types of tags. The tags can be inline, which means that they consist of a single\ntag statement such as `include` or `extends`, or contain a body and intermediate tags,\nsuch as `if`, `else`, `endif`.\n\nFor example if we wanted to iterate over a collection of items, we could write the following:\n\n```clojure\n(render\n  \"{% for user in users %}{{user.name}}{% endfor %}\"\n  {:users [{:name \"John\"} {:name \"Jane\"}]})\n=\u003e\"JohnJane\"\n```\n\nIt's also possible to define custom tags using the `add-tag!` macro:\n\n```clojure\n(use 'selmer.parser)\n\n(add-tag! :foo\n  (fn [args context-map]\n    (str \"foo \" (first args))))\n\n(render \"{% foo quux %} {% foo baz %}\" {})\n=\u003e\"foo quux foo baz\"\n```\nit's also possible to add block tags. When adding a block tag, the handler\nfunction should accept the tag arguments, the context map, and the content.\nThe content will be keyed on the opening tag name as can be seen below.\n\nThe example uses [infix](https://github.com/rm-hull/infix) library to implement\nan infix math parsing tag:\n\n```clojure\n(require\n  '[infix.core :refer [base-env]]\n  '[jasentaa.parser :as p :refer [parse-all]]\n  '[infix.grammar :refer [expression]]\n  '[selmer.parser :as selmer])\n\n(selmer/add-tag! :math\n          (fn [args context-map]\n            ((parse-all expression (apply str args))\n             (merge base-env context-map))))\n\n(selmer/render \"{% math x + y * z %}\" {:x 1 :y 2 :z 3})\n=\u003e\"7\"\n\n(selmer/add-tag! :uppercase\n          (fn [args context-map content]\n            (.toUpperCase (get-in content [:uppercase :content])))\n          :enduppercase)\n\n(render \"{% uppercase %}foo {{bar}} baz{% enduppercase %}\" {:bar \"injected\"})\n=\u003e\"FOO INJECTED BAZ\"\n```\n\nThe args passed to the handler by default are not resolved, so if the tag arg contains\na variable `{{variable}}`, a filter `{{variable|filter}}` or a tag `{% if foo %}bar{% endif %}`,\nyou will get them as-is inside a string:\n\n```\n{% custom-tag {{variable}} %} -\u003e \"{{variable}}\"\n{% custom-tag {{variable|filter}} %} -\u003e \"{{variable|filter}}\"\n{% custom-tag {% if foo %}bar{% endif %} %} -\u003e \"{% if foo %}bar{% endif %}\"\n```\n\nSimilarly, if you have a literal as the arg to a tag, the handler will receive it double quoted:\n\n```\n{% custom-tag \"Hello John\" %} -\u003e \"\\\"Hello John\\\"\"\n```\n\nIn both cases, you can use `resolve-arg` to resolve the variable, filter, tag or literal so that:\n\n```clojure\n(resolve-arg \"Hello {{variable}}\" {:variable \"John\"})\n=\u003e \"Hello John\"\n(resolve-arg \"Hello {{variable|upper}}\" {:variable \"John\"})\n=\u003e \"Hello JOHN\"\n(resolve-arg \"Hello {% if variable = \\\"John\\\" %}Mr {{variable}}{% endif %}\" {:variable \"John\"})\n=\u003e \"Hello Mr John\"\n(resolve-arg \"Hello John\" {})\n=\u003e \"Hello John\"\n```\n\nHere's an example custom tag which works like if/else but if a string ends with some other string,\nand we want to be able to use variables, filters and tags for the string:\n\n```clojure\n(add-tag!\n :ifendswith\n (fn [args context-map content]\n   (let [args (map #(resolve-arg % context-map) args)]\n     (if (str/ends-with? (first args) (second args))\n       (-\u003e content :ifendswith :content)\n       (-\u003e content :else :content))))\n :else :endifendswith)\n```\n\n### Built-in Tags\n\n#### include\n\nThis tag is only available for `render-file` since `render` function only operates on a template consisting of a single string.\n\nreplaces itself with the contents of the referenced template\n\n`{% include \"path/to/comments.html\" %}`\n\noptionally, you can supply default arguments any tags matching these will have the `default` filter applied using the value supplied:\n\n`{% include \"templates/inheritance/child.html\" with name=\"Jane Doe\" greeting=\"Hello!\" %}`\n\n#### block\n\nAllows specifying a block of content that can be overwritten using the template inheritance discussed below.\n\n`{% block foo %}This text can be overridden later{% endblock %}`\n\n#### cycle\n\nWill cycle through the supplied values.\n\n```\n(render \"{% for i in items %}\u003cli class={% cycle \\\"blue\\\" \\\"white\\\" %}\u003e{{i}}\u003c/li\u003e{% endfor %}\"\n        {:items (range 5)})\n```\n=\u003e\n```\n\"\u003cli class=\\\"blue\\\"\u003e0\u003c/li\u003e\u003cli class=\\\"white\\\"\u003e1\u003c/li\u003e\u003cli class=\\\"blue\\\"\u003e2\u003c/li\u003e\u003cli class=\\\"white\\\"\u003e3\u003c/li\u003e\u003cli class=\\\"blue\\\"\u003e4\u003c/li\u003e\"\n```\n\n#### debug\n\nPrints the context map passed to the template using `pr-str`. Pretty printing can be enabled by including [json-html](https://github.com/yogthos/json-html) or implementing `json-html.core/edn-\u003ehtml` function that accepts a map as its argument and returns a string.\n\n ```\n (render \"{% debug %}\" {:foo :bar})\n ```\n =\u003e\n ```\n \u003cdiv class=\"jh-root\"\u003e\n  \u003ctable class=\"jh-type-object\"\u003e\n   \u003ctbody\u003e\u003ctr\u003e\u003cth class=\"jh-key jh-object-key\"\u003e\u003cspan class=\"jh-type-string\"\u003e:foo\u003c/span\u003e\u003c/th\u003e\n     \u003ctd class=\"jh-value jh-object-value\"\u003e\u003cspan class=\"jh-type-string\"\u003e:bar\u003c/span\u003e\u003c/td\u003e\u003c/tr\u003e\n   \u003c/tbody\u003e\n  \u003c/table\u003e\n \u003c/div\u003e\n ```\n\n#### extends\n\nThis tag is used to reference a parent template. The blocks in parents are recursively overridden by\nthe blocks from child templates.\n\n* Note: child templates can **only** contain blocks. Any tags or text outside the blocks will be\nignored!\n\nFor example, say we have a base template called `base.html` and a child template `child.html`:\n\n```xml\n\u003chtml\u003e\n\t\u003cbody\u003e\n\t\t{% block foo %}This text can be overridden later{% endblock %}\n\t\u003c/body\u003e\n\u003c/html\u003e\n```\n\n```xml\n{% extends \"base.html\" %}\n{% block foo %}\u003cp\u003eThis text will override the text in the parent\u003c/p\u003e{% endblock %}\n```\n\n#### if\n\nIt's an `if` -- only render the body if the conditional is true.\n\n`{% if condition %}yes!{% endif %}`\n\n`{% if not condition %}yes!{% endif %}`\n\n`{% if condition %}yes!{% else %}no!{% endif %}`\n\nit's possible to use `any` and `all` operators to check multiple values:\n\n`(render \"{% if any foo bar baz %}hello{% endif %}\" {:bar \"foo\"})`\n\n`(render \"{% if not any foo bar baz %}hello{% endif %}\" {})`\n\n`(render \"{% if all foo bar %}hello{% endif %}\" {:foo \"foo\" :bar \"bar\"})`\n\nnumeric comparisons are also supported using the `=`, `\u003c`, `\u003e`, `\u003c=` and `\u003e=` operators\n\n`(render \"{% if 5 \u003e= x %}yes!{% endif %}\" {:x 3})`\n\n`(render \"{% if x \u003c= y %}yes!{% endif %}\" {:x 3 :y 5})`\n\n`(render \"{% if x = 5.0 %}yes!{% else %}no!{% endif %}\" {:x 5})`\n\n`(render \"{% if x \u003e 5 %}yes!{% else %}no!{% endif %}\" {:x 6})`\n\n`(render \"{% if vals|length \u003c= 3 %}yes!{% else %}no!{% endif %}\" {:vals (range 3)})`\n\nyou can compare strings or keywords with `=` as well:\n\n`(render \"{% if x = \\\"banana\\\" %}yellow{% endif %}\" {:x \"banana\"})`\n\n`(render \"{% if x = :banana %}yellow{% endif %}\" {:x :banana})`\n\nfilters work for the conditions:\n\n```clojure\n(add-filter! :empty? empty?)\n(render \"{% if files|empty? %}no files{% else %}files{% endif %}\"\n  {:files []})\n```\n\nYou can have elif (else if) clauses if you want:\n\n```clojure\n(render \"{% if pl \u003e 9000 %}\n              it's over 9000!\n         {% elif pl \u003e 100 %}\n              it's in a middle zone\n         {% elif status = \\\"decent\\\" %}\n              still pretty ok\n         {% else %}\n              lower than 10... not ok\n         {% endif %}\"\n  {:pl 14 :status \"decent\"})\n=\u003e \"still pretty ok\"\n```\n\n#### ifequal\nOnly render the body if the two args are equal (according to clojure.core/=).\n\n`{% ifequal foo bar %}yes!{% endifequal %}`\n\n`{% ifequal foo bar %}yes!{% else %}no!{% endifequal %}`\n\n`{% ifequal foo \"this also works\" %}yes!{% endifequal %}`\n\n#### ifunequal\nOnly render the body if the two args are unequal (according to clojure.core/=).\n\n`{% ifunequal foo bar %}yes!{% endifunequal %}`\n\n**for/endfor** *block*\n\n#### for\nRender the body one time for each element in the list. Each render will introduce the following variables into the context:\n\n* `forloop.first`\n* `forloop.last`\n* `forloop.counter`\n* `forloop.counter0`\n* `forloop.revcounter`\n* `forloop.revcounter0`\n* `forloop.length`\n* `forloop.parentloop`\n* `forloop.previous`\n\n`forloop.previous` is a hash map that contains the loop variable name:\n\n```\n{% for item in some.values %}\n  {% if forloop.previous.item = item %}\n   repeated\n  {% else %}\n   {{item}} is new!\n  {% endif %}\n{% endfor %}\n```\n\n`{% for x in some-list %}element: {{x}} first? {{forloop.first}} last? {{forloop.last}}{% endfor %}`\n\nyou can iterate over nested data structures, eg:\n\n`{% for item in items %} \u003ctr\u003e\u003ctd\u003e{{item.name}}\u003c/td\u003e\u003ctd\u003e{{item.age}}\u003c/td\u003e\u003c/tr\u003e {% endfor %}`\n\narray elements can be destructured in for loops:\n\n`(render \"{% for x,y in items %}{{x}},{{y}}{% endfor %}\" {:items [[\"a\" \"b\"] [\"c\" \"d\"]]})` =\u003e `\"a,bc,d\"`\n\nyou can also specify the default content if there are no items using the `{% empty %}` tag:\n\n`(render \"{% for i in foo %} {{i}} {% empty %}no elements{% endfor %}\" {})` =\u003e `\"no elements\"`\n\nfilters can be used inside the for loop:\n\n`(render \"{% for x in foo.bar|sort %}{{x}}{% endfor %}\" {:foo {:bar [1 4 3 5]}})` =\u003e `\"1345\"`\n\n`(render \"{% for i in x %}{% for j in i %}{{j}}-{{forloop.parentloop.counter}}{% endfor %}{% endfor %}\" {:x [[:a :b]]})` =\u003e `\":a-1:b-1\"`\n\n#### sum\nSums multiple variables together\n`(render \"{% sum foo bar baz %}\" {:foo 3 :bar 2 :baz 1})` =\u003e `\"6\"`\n\n`(render \"{% sum foo \\\\1.1 %}\" {:foo 1.1})` =\u003e `\"2.2\"`\n\n#### now\nrenders current time\n\n`(render (str \"{% now \\\"dd MM yyyy\\\" %}\") {})` =\u003e `\"\\\"01 08 2013\\\"\"`\n\n#### comment\nignores any content inside the block\n\n`(render \"foo bar {% comment %} baz test {{x}} {% endcomment %} blah\" {})` =\u003e `\"foo bar  blah\"`\n\nA short form is also available:\n\n`(render \"foo bar {# baz test {{x}} #} blah\" {})` =\u003e `\"foo bar  blah\"`\n\n#### firstof\nrenders the first occurance of supplied keys that doesn't resolve to false:\n\n`(render \"{% firstof var1 var2 var3 %}\" {:var2 \"x\" :var3 \"not me\"})` =\u003e `\"x\"`\n\n\u003ch4\u003e\n\u003ca name=\"user-content-safe-tag\" class=\"anchor\" href=\"#safe-tag\" aria-hidden=\"true\"\u003e\u003cspan class=\"octicon octicon-link\"\u003e\u003c/span\u003e\u003c/a\u003esafe\u003c/h4\u003e\n\nsafe tag will prevent escaping of any content inside it:\n\n`(render \"{% safe %}{{foo|upper}}{% endsafe %}\" {:foo \"\u003cfoo\u003e\"})` =\u003e `\u003cFOO\u003e`\n\nNote, the escaping of variables can also be controlled through the dynamic binding of `selmer.util/*escape-variables`.\n\n#### script\n\nThe script tag will generate an HTML script tag and prepend the value of the `selmer/context` key\nto the URI. When `selmer/context` key is not present then the original URI is set.\n\n`(render \"{% script \\\"/js/site.js\\\" %}\" {:selmer/context \"/myapp\"})` =\u003e\n```\n\"\u003cscript async-attr defer-attr src=\\\"/myapp/js/site.js\\\" type=\\\"text/javascript\\\"\u003e\u003c/script\u003e\"\n```\n\nSince 1.11.1 URI can be a name of context parameter with optional filters.\n\n`(render \"{% script path %}\" {:selmer/context \"/myapp\" :path \"/js/site.js\"})` =\u003e\n```\n\"\u003cscript async-attr defer-attr src=\\\"/myapp/js/site.js\\\" type=\\\"text/javascript\\\"\u003e\u003c/script\u003e\"\n```\n\n`(render \"{% script path|upper %}\" {:selmer/context \"/myapp\" :path \"/js/site.js\"})` =\u003e\n```\n\"\u003cscript async-attr defer-attr src=\\\"/myapp/JS/SITE.JS\\\" type=\\\"text/javascript\\\"\u003e\u003c/script\u003e\"\n```\n#### style\n\nThe style tag will generate an HTML style tag and prepend the value of the `selmer/context` key\nto the URI. When `selmer/context` key is not present then the original URI is set.\n\n`(render \"{% style \\\"/css/screen.css\\\" %}\" {:selmer/context \"/myapp\"})` =\u003e\n```\n\"\u003clink href=\\\"/myapp/css/screen.css\\\" rel=\\\"stylesheet\\\" type=\\\"text/css\\\" /\u003e\"\n```\n\nSince 1.11.1 URI can be a name of context parameter with optional filters.\n\n`(render \"{% style path %}\" {:selmer/context \"/myapp\" :path \"/css/screen.css\"})` =\u003e\n```\n\"\u003clink href=\\\"/myapp/css/screen.css\\\" rel=\\\"stylesheet\\\" type=\\\"text/css\\\" /\u003e\"\n```\n\n`(render \"{% style path|upper %}\" {:selmer/context \"/myapp\" :path \"/css/screen.css\"})` =\u003e\n```\n\"\u003clink href=\\\"/myapp/CSS/SCREEN.CSS\\\" rel=\\\"stylesheet\\\" type=\\\"text/css\\\" /\u003e\"\n```\n#### verbatim\nprevents any tags inside from being parsed:\n\n`(render \"{% verbatim %}{{if dying}}Still alive.{{/if}}{% endverbatim %}\" {})` =\u003e `\"{{if dying}}Still alive.{{/if}}\"`\n\n#### with\ninjects the specified keys into the context map:\n\n`(render \"{% with total=business.employees|count %}{{ total }}{% endwith %}\" {:business {:employees (range 5)}})` =\u003e `\"5 employees\"`\n\n## Template Inheritance\n\n**note**: tags using template inheritance can only be used with `render-file` since `render` will only consider the input string itself\n\n### Extending Templates\n\nTemplates can inherit from other templates using the `extends` tag. When extending a template, any blocks in the parent\nwill be overwritten by blocks from the child with the same name. For example if we had the following scenario:\n\n`base.html`\n\n```xml\n\u003chtml\u003e\n\u003cbody\u003e\n{% block header %}\n{% endblock %}\n\n{% block content %}\n{% endblock %}\n\n{% block footer %}\n{% endblock %}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n`child-a.html`\n\n```xml\n{% extends \"base.html\" %}\n{% block header %}\n\u003ch1\u003echild-a header\u003c/h1\u003e\n{% endblock %}\n\n{% block footer %}\n\u003cp\u003efooter\u003c/p\u003e\n{% endblock %}\n```\n\n`child-b.html`\n\n```xml\n{% extends \"child-a.html\" %}\n{% block header %}\n\u003ch1\u003echild-b header\u003c/h1\u003e\n{% endblock %}\n\n{% block content %}\nSome content\n{% endblock %}\n```\n\nIf we called `(render-file \"child-b.html\" {})` then the compiled template would look as follows:\n\n```xml\n\u003chtml\u003e\n\u003cbody\u003e\n{% block header %}\n\u003ch1\u003echild-b header\u003c/h1\u003e\n{% endblock %}\n{% block content %}\nSome content\n{% endblock %}\n\n{% block footer %}\n\u003cp\u003efooter\u003c/p\u003e\n{% endblock %}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nIt's also possible to include content from the parent block using the `{{block.super}}` hint. If we change `child-b.html`\nto look as follows:\n\n```xml\n{% extends \"child-a.html\" %}\n{% block header %}\n{{block.super}}\n\u003ch1\u003echild-b header\u003c/h1\u003e\n{% endblock %}\n\n{% block content %}\nSome content\n{% endblock %}\n```\n\nThen we'd have the following output:\n\n```xml\n\u003chtml\u003e\n\u003cbody\u003e\n{% block header %}\n\n\u003ch1\u003echild-a header\u003c/h1\u003e\n\n\u003ch1\u003echild-b header\u003c/h1\u003e\n{% endblock %}\n{% block content %}\nSome content\n{% endblock %}\n\n{% block footer %}\n\u003cp\u003efooter\u003c/p\u003e\n{% endblock %}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Including Templates\n\nTemplates can also `include` other templates. In this case the contents of the child are simply spliced in place\nof the tag:\n\n`base.html`\n\n```xml\n\u003chtml\u003e\n{% include \"content.html\" %}\n\u003c/html\u003e\n```\n\n`content.html`\n\n```xml\n\u003cbody\u003econtent\u003c/body\u003e\n```\n\nresults in:\n\n```xml\n\u003chtml\u003e\n\u003cbody\u003econtent\u003c/body\u003e\n\u003c/html\u003e\n```\n\nIt's also possible to specify default values for the included templates using\n`with`:\n\n`base.html`\n\n```xml\n\u003chtml\u003e\n{% include \"content.html\" with content=\"some content\" %}\n\u003c/html\u003e\n```\n\n`content.html`\n\n```xml\n\u003cbody\u003e{{content}}\u003c/body\u003e\n```\n\nresults in:\n\n```xml\n\u003chtml\u003e\n\u003cbody\u003e{{content|default:\"some content\"}}\u003c/body\u003e\n\u003c/html\u003e\n```\n\nYou may also specify more than one value:\n\n`base.html`\n\n```xml\n\u003chtml\u003e\n{% include \"content.html\" with content=\"some content\" url=\"/path/to/page\" %}\n\u003c/html\u003e\n```\n\n\n## Missing values\n\nMissing values are by default rendered as an empty string:\n\n```clojure\n(render \"{{missing}}\" {})\n=\u003e \"\"\n```\nThe same goes for for loops:\n```clojure\n(parser/render \"{% for e in items %}{% endfor %}\" {})\n=\u003e \"\"\n```\n\nIt is possible to overwrite this behavior to output a different value when encountering a mising value. This is done by calling `selmer.util/set-missing-value-formatter!` to provide a function that produces the desired output.\n\n`set-missing-value-formatter!` takes a function of two arguments, a map of info about the tag and the context map, which is called on a missing value. The function should return the value to be output in place of an empty string (which is the default from 'default-missing-value-formatter').\n\n\n```clojure\n(defn missing-value-fn [tag context-map]\n  (str \"\u003cMissing value: \" (or (:tag-value tag) (:tag-name tag)) \"\u003e\"))\n\n(selmer.util/set-missing-value-formatter! missing-value-fn)\n\n(selmer.parser/render \"{{not-here}}\" {})\n=\u003e \"\u003cMissing value: not-here\u003e\"\n```\n\nor you can throw an exception:\n\n```clojure\n(defn missing-value-fn [tag context-map]\n  (throw (Exception. \"Nope\")))\n\n(selmer.util/set-missing-value-formatter! missing-value-fn)\n\n(selmer.parser/render \"{{not-here}}\" {}) =\u003e Exception: Nope\n```\n\nWhen you set a custom missing value handler, by default filters are bypassed for missing values:\n\n```clojure\n(defn missing-value-fn [tag context-map]\n  (str \"\u003cMissing value: \" (or (:tag-value tag) (:tag-name tag)) \"\u003e\"))\n\n(selmer.util/set-missing-value-formatter! missing-value-fn)\n\n(selmer.parser/render \"{{not-here|count}}\" {})\n=\u003e \"\u003cMissing value: not-here\u003e\"\n```\n\nbut this can be overwritten so filters are evaluated for missing values:\n\n```clojure\n(defn missing-value-fn [tag context-map]\n  (str \"\u003cMissing value: \" (or (:tag-value tag) (:tag-name tag)) \"\u003e\"))\n\n(selmer.util/set-missing-value-formatter! missing-value-fn :filter-missing-values true)\n\n(selmer.parser/render \"{{not-here|count}}\" {})\n=\u003e \"0\"\n```\n\nAlthough for most use cases, this will not make sense.\n\n## Internationalization\n\nWhile there is no built in support for internationalization, it can be added via a custom tag.\nThe following example uses the [tongue](https://github.com/tonsky/tongue) library to implement\nthe `i18n` tag:\n\n```clojure\n(require '[tongue.core :as tongue]\n         '[selmer.parser :as parser])\n\n(def translate\n  ;; [locale key \u0026 args] =\u003e string\n  (tongue/build-translate\n    {:en {:animals\n          {:dog \"dog\"\n           :cat \"cat\"}}\n     :fr {:animals\n          {:dog \"chien\"\n           :cat \"chat\"}}}))\n\n(parser/add-tag! :i18n\n  (fn [[k] context]\n    (-\u003e\u003e k (keyword) (translate (or (:i18n/locale context) :en)))))\n\n(parser/render \"{% i18n animals/dog %}\" {})\n;=\u003e \"dog\"\n(parser/render \"{% i18n animals/dog %}\" {:i18n/locale :fr})\n;=\u003e \"chien\"\n```\n\n[**Back To Top ⇧**](#selmer)\n\n## License\n\nCopyright © 2015 Dmitri Sotnikov\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","funding_links":["https://github.com/sponsors/yogthos"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyogthos%2Fselmer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyogthos%2Fselmer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyogthos%2Fselmer/lists"}