Text Templates in Clojure

Our first program will demonstrate text templates in Clojure. Here’s the full source code:

(ns text-templates
  (:require [selmer.parser :as selmer]
            [clojure.string :as str]))

(defn main []
  ;; We can create a new template and parse its body from a string.
  ;; Templates are a mix of static text and "tags" enclosed in
  ;; {{...}} that are used to dynamically insert content.
  (let [t1 "Value is {{.}}\n"]
    (println (selmer/render t1 "some text"))
    (println (selmer/render t1 5))
    (println (selmer/render t1 ["Go" "Rust" "C++" "C#"])))

  ;; If the data is a map we can use the {{key}} tag to access
  ;; its values.
  (let [t2 "Name: {{name}}\n"]
    (println (selmer/render t2 {:name "Jane Doe"}))
    (println (selmer/render t2 {"name" "Mickey Mouse"})))

  ;; if/else provide conditional execution for templates. A value is considered
  ;; false if it's nil or false.
  ;; This sample demonstrates another feature of Selmer templates:
  ;; using - in tags to trim whitespace.
  (let [t3 "{{if . -}} yes {{else -}} no {{endif}}\n"]
    (println (selmer/render t3 "not empty"))
    (println (selmer/render t3 nil)))

  ;; for blocks let us loop through sequences. Inside
  ;; the for block {{.}} is set to the current item of the iteration.
  (let [t4 "Range: {% for item in . %}{{item}} {% endfor %}\n"]
    (println (selmer/render t4 ["Go" "Rust" "C++" "C#"]))))

(main)

To run the program, save it as text_templates.clj and use clj to execute it:

$ clj -M text_templates.clj
Value is some text
Value is 5
Value is [Go Rust C++ C#]
Name: Jane Doe
Name: Mickey Mouse
yes
no
Range: Go Rust C++ C#

This example uses the Selmer library, which provides templating functionality similar to Go’s text/template package. Here’s a breakdown of the Clojure version:

  1. We use Selmer’s render function to execute templates, which is similar to Go’s Execute method.

  2. In Clojure, we use maps (similar to Go’s structs or maps) to pass named values to templates.

  3. Conditional logic in Selmer uses {% if %} and {% endif %} tags, which is slightly different from Go’s syntax.

  4. Looping in Selmer is done with {% for %} and {% endfor %} tags, providing similar functionality to Go’s range blocks.

  5. Clojure doesn’t have a built-in concept of “exported fields” like Go does. In Clojure, all keys in a map are accessible in templates.

  6. Error handling in Clojure is typically done with try/catch blocks, which we’ve omitted here for brevity. In a production setting, you’d want to add appropriate error handling.

This Clojure code demonstrates the core concepts of text templating, providing similar functionality to the Go example while using Clojure’s idiomatic approaches and the Selmer library.