- name
- selmer
- description
- Django-inspired HTML templating system for Clojure with filters, tags, and template inheritance
# Selmer
Django-inspired HTML templating system for Clojure providing a fast, productive templating experience with filters, tags, template inheritance, and extensive customization options.
## Overview
Selmer is a pure Clojure template engine inspired by Django's template syntax. It compiles templates at runtime, supports template inheritance and includes, provides extensive built-in filters and tags, and allows custom extensions. Designed for server-side rendering in web applications, email generation, reports, and any text-based output.
**Key Features:**
- Django-compatible template syntax
- Variable interpolation with nested data access
- 50+ built-in filters for data transformation
- Control flow tags (if, for, with)
- Template inheritance (extends, block, include)
- Custom filter and tag creation
- Template caching for performance
- Auto-escaping with override control
- Validation and error reporting
- Middleware support
**Installation:**
Leiningen:
```clojure
[selmer "1.12.65"]
```
deps.edn:
```clojure
{selmer/selmer {:mvn/version "1.12.65"}}
```
**Quick Start:**
```clojure
(require '[selmer.parser :refer [render render-file]])
;; Render string
(render "Hello {{name}}!" {:name "World"})
;=> "Hello World!"
;; Render file
(render-file "templates/home.html" {:user "Alice"})
```
## Core Concepts
### Variables
Variables use `{{variable}}` syntax and are replaced with values from the context map.
```clojure
(render "{{greeting}} {{name}}" {:greeting "Hello" :name "Bob"})
;=> "Hello Bob"
```
**Nested Access:**
```clojure
(render "{{person.name}}" {:person {:name "Alice"}})
;=> "Alice"
(render "{{items.0.title}}" {:items [{:title "First"}]})
;=> "First"
```
**Missing Values:**
```clojure
(render "{{missing}}" {})
;=> "" (empty string by default)
```
### Filters
Filters transform variable values using the pipe `|` operator.
```clojure
(render "{{name|upper}}" {:name "alice"})
;=> "ALICE"
;; Chain filters
(render "{{text|upper|take:5}}" {:text "hello world"})
;=> "HELLO"
```
### Tags
Tags use `{% tag %}` syntax for control flow and template structure.
```clojure
{% if user %}
Welcome {{user}}!
{% else %}
Please log in.
{% endif %}
```
### Template Inheritance
Parent template (`base.html`):
```html
<html>
<head>{% block head %}Default Title{% endblock %}</head>
<body>{% block content %}{% endblock %}</body>
</html>
```
Child template:
```html
{% extends "base.html" %}
{% block head %}Custom Title{% endblock %}
{% block content %}<h1>Hello!</h1>{% endblock %}
```
## API Reference
### Rendering Functions
#### `render`
Render a template string with context.
```clojure
(render template-string context-map)
(render template-string context-map options)
;; Examples
(render "{{x}}" {:x 42})
;=> "42"
(render "[% x %]" {:x 42}
{:tag-open "[%" :tag-close "%]"})
;=> "42"
```
**Parameters:**
- `template-string` - Template as string
- `context-map` - Data map for template
- `options` - Optional map with `:tag-open`, `:tag-close`, `:filter-open`, `:filter-close`
#### `render-file`
Render a template file from classpath or resource path.
```clojure
(render-file filename context-map)
(render-file filename context-map options)
;; Examples
(render-file "templates/email.html" {:name "Alice"})
(render-file "custom.tpl" {:x 1}
{:tag-open "<%%" :tag-close "%%>"})
```
**File Resolution:**
1. Checks configured resource path
2. Falls back to classpath
3. Caches compiled template
### Caching
#### `cache-on!`
Enable template caching (default).
```clojure
(require '[selmer.parser :refer [cache-on!]])
(cache-on!)
```
Templates are compiled once and cached. Use in production.
#### `cache-off!`
Disable template caching for development.
```clojure
(require '[selmer.parser :refer [cache-off!]])
(cache-off!)
```
Templates recompile on each render. Use during development.
### Configuration
#### `set-resource-path!`
Configure base path for template files.
```clojure
(require '[selmer.parser :refer [set-resource-path!]])
(set-resource-path! "/var/html/templates/")
(set-resource-path! nil) ; Reset to classpath
```
#### `set-missing-value-formatter!`
Configure how missing values are rendered.
```clojure
(require '[selmer.parser :refer [set-missing-value-formatter!]])
(set-missing-value-formatter!
(fn [tag context-map]
(str "MISSING: " tag)))
(render "{{missing}}" {})
;=> "MISSING: missing"
```
### Introspection
#### `known-variables`
Extract all variables from a template.
```clojure
(require '[selmer.parser :refer [known-variables]])
(known-variables "{{x}} {{y.z}}")
;=> #{:x :y.z}
```
Useful for validation and documentation.
### Validation
#### `validate-on!` / `validate-off!`
Control template validation.
```clojure
(require '[selmer.validator :refer [validate-on! validate-off!]])
(validate-on!) ; Default - validates templates
(validate-off!) ; Skip validation for performance
```
Validation catches undefined filters, malformed tags, and syntax errors.
### Custom Filters
#### `add-filter!`
Register a custom filter.
```clojure
(require '[selmer.filters :refer [add-filter!]])
(add-filter! :shout
(fn [s] (str (clojure.string/upper-case s) "!!!")))
(render "{{msg|shout}}" {:msg "hello"})
;=> "HELLO!!!"
;; With arguments
(add-filter! :repeat
(fn [s n] (apply str (repeat (Integer/parseInt n) s))))
(render "{{x|repeat:3}}" {:x "ha"})
;=> "hahaha"
```
#### `remove-filter!`
Remove a filter.
```clojure
(require '[selmer.filters :refer [remove-filter!]])
(remove-filter! :shout)
```
### Custom Tags
#### `add-tag!`
Register a custom tag.
```clojure
(require '[selmer.parser :refer [add-tag!]])
(add-tag! :uppercase
(fn [args context-map]
(clojure.string/upper-case (first args))))
;; In template: {% uppercase "hello" %}
```
**Block Tags:**
```clojure
(add-tag! :bold
(fn [args context-map content]
(str "<b>" (get-in content [:bold :content]) "</b>"))
:bold :endbold)
;; In template:
;; {% bold %}text here{% endbold %}
```
#### `remove-tag!`
Remove a tag.
```clojure
(require '[selmer.parser :refer [remove-tag!]])
(remove-tag! :uppercase)
```
### Error Handling
#### `wrap-error-page`
Middleware to display template errors with context.
```clojure
(require '[selmer.middleware :refer [wrap-error-page]])
(def app
(wrap-error-page handler))
```
Shows error message, line number, and template snippet.
### Escaping Control
#### `without-escaping`
Render template without HTML escaping.
```clojure
(require '[selmer.util :refer [without-escaping]])
(render "{{html}}" {:html "<b>Bold</b>"})
;=> "<b>Bold</b>"
(without-escaping
(render "{{html}}" {:html "<b>Bold</b>"}))
;=> "<b>Bold</b>"
```
## Built-in Filters
### String Filters
**upper** - Convert to uppercase
```clojure
{{name|upper}} ; "alice" → "ALICE"
```
**lower** - Convert to lowercase
```clojure
{{NAME|lower}} ; "ALICE" → "alice"
```
**capitalize** - Capitalize first letter
```clojure
{{word|capitalize}} ; "hello" → "Hello"
```
**title** - Title case
```clojure
{{phrase|title}} ; "hello world" → "Hello World"
```
**addslashes** - Escape quotes
```clojure
{{text|addslashes}} ; "I'm" → "I\'m"
```
**remove-tags** - Strip HTML tags
```clojure
{{html|remove-tags}} ; "<b>text</b>" → "text"
```
**safe** - Mark as safe (no escaping)
```clojure
{{html|safe}} ; Renders HTML without escaping
```
**replace** - Replace substring
```clojure
{{text|replace:"old":"new"}}
```
**subs** - Substring
```clojure
{{text|subs:0:5}} ; First 5 characters
```
**abbreviate** - Truncate with ellipsis
```clojure
{{text|abbreviate:10}} ; "Long text..." (max 10 chars)
```
### Formatting Filters
**date** - Format date
```clojure
{{timestamp|date:"yyyy-MM-dd"}}
{{timestamp|date:"MMM dd, yyyy"}}
```
**currency-format** - Format currency
```clojure
{{amount|currency-format}} ; 1234.5 → "$1,234.50"
```
Ver no GitHub