/cuma

Extensible micro template engine for Clojure

Primary LanguageClojure

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.