cuma icon indicating copy to clipboard operation
cuma copied to clipboard

Extensible micro template engine for Clojure

cuma

Extensible micro template engine for Clojure.

Build Status Dependency Status

Installation

Add following dependency to your profject.clj.

cuma

Usage

require [cuma.core :refer [render]]

Replace Variable

(render "hello $(x)" {:x "world"})
;=> hello world

Replace escaped variable.

(render "$(x)" {:x "<h1>"})
;=> &lt;h1&gt;

Replace unescaped variable.

(render "$(raw x)" {:x "<h1>"})
;=> <h1>

Include another template.

(render "$(include tmpl)" {:tmpl "hello $(x)" :x "world"})
;=> hello world

Apply custom function to variable. Function detail is explained at following.

(render "$(upper x)" {:upper (fn [data s] (.toUpperCase s)) :x "hello")
;=> HELLO

Chain custom functions.

(let [f (fn [_ arg] (str "foo " arg))
      g (fn [_ arg] (str "bar " arg))]
 (render "$(-> x g f)" {:f f :g g :x "baz"}))
;=> "foo bar baz"

Replace Section

if section

(render "@(if flg) foo @(end)" {:flg true})
;=> foo
(render "@(if flg) foo @(end)" {:flg false})
;=>

Implicit variable $(.) is binded in if section.

(render "@(if x) $(.) @(end)" {:x "hello"})
;=> hello

Map data is expanded to variable in if section.

(render "@(if m) $(n) @(end)" {:m {:n "foo"}})
;=> foo

for section

Implicit variable $(.) is binded in for section.

(render "@(for arr) $(.) @(end)" {:arr [1 2 3]})
;=> 1 2 3

Map data is expanded to variable in for section.

(render "@(for arr) $(n) @(end)" {:arr [{:n 1} {:n 2} {:n 3}]})
;=> 1 2 3

(render "@(for arr1) @(for arr2) $(a)$(b) @(end) @(end)"
        {:arr1 [{:a 1} {:a 2}] :arr2 [{:b 3} {:b 4}]})
;=> 13 14 23 24

let section

(render "@(let :x \"foo\" :y 123) $(x) $(y) @(end)" {})
;=> foo 123

layout-file section

layout with implicit variable .

  • layout.tpl
hello $(.)
  • your code
(render "@(layout-file \"layout.tpl\")world@(end)" {})
;=> helo world

layout with block section (block section can be used only in layout-file section)

  • layout.tpl
a = $(a), b = $(b)
  • your code
(render "@(layout-file \"layout.pl\") @(block :a)hello@(end) @(block :b)world@(end) @(end)" {})
;=> a = hello, b = world

custom section

(render "@(foo) world @(end)" {:foo (fn [data body] (str "hello " body))})
;=> hello world

(render "@(foo x) world @(end)"
        {:foo (fn [data body arg] (str arg " " body)) :x "hello"})
;=> hello world

Dotted Variable

(render "$(a.b.c)" {:a {:b {:c "hello"}}})
;=> hello

Not Supporting Form

; NOT SUPPORTED: nested variable
(render "$(f (g x))" {...})

Writing Extension

Replacing variable and section are allowd to use custom function, and cuma allows you to make custon function as extension.

Cuma searches cuma.extension.* namespaces, and load all public functions as extension.

raw, ->, include, if, for are also extension. https://github.com/liquidz/cuma/blob/master/src/cuma/extension/core.clj

Variable Extension

(render "$(f x y z)" {:x 1 :y 2 :z 3 :foo "bar"})
(ns cuma.extension.YOUR_EXTENSION_NAME)

(defn f
  "@data => {:x 1 :y 2 :z 3 :foo "bar" :render #'cuma.core/render, OTHER_EXTENSIONS}
   @args => [1 2 3]"
  [data & args]
  (apply + args))

Section Extension

(render "@(f x y z) world @(end)" {:x 1 :y 2 :z 3 :foo "bar"})
(ns cuma.extension.YOUR_EXTENSION_NAME)

(defn f
  "@data => {:x 1 :y 2 :z 3 :foo "bar" :render #'cuma.core/render, OTHER_EXTENSIONS}
   @body => " hello "
   @args => [1 2 3]"
  [data body & args]
  ((:render data) (str "hello" body)))

Performance

Benchmarking is powered by criterium. Test code is here.

cuma performance

License

Copyright (C) 2015 Masashi Iizuka(@uochan)

Distributed under the Eclipse Public License, the same as Clojure.