erbとかgspみたいな雰囲気で

とりあえずclojarsにはwebアプリケーション向けのテンプレーティングするやつがいくつかあるんだけど、clojure.templateを使ってもちょっと汎用的に使えそうなものを考えてみた。

clojure.templateはこんなかんじの関数とマクロ。

;;apply-template function
(apply-template '[x] '(+ x x) '[2])
;=> (+ 2 2)

;;do-template macto
(macroexpand '(do-template [x y] (+ y x) 2 4 3 5))
;=> (do (+ 4 2) (+ 5 3))

なんか合成関数とか引数の部分適用があれば別にいらなくね的なこともいわれかねないかんじですが、とりあえずもちょっと引数を少なくして文字列をかえしてあげるようにかんがえてみたのが以下。

(ns template_engine
  (:use [clojure.template]
        [clojure.contrib.duck-streams :only [reader]]))

(defn- read-template-file [f]
  (let [r (java.io.PushbackReader. (reader f))]
    (read r false false)))

(defn bind-template 
  "文字列のリストとマップを指定してtemplatingするよ"
  [tpl mp]
  (let [tpl-str tpl 
        temp (vec (keys mp))
        args (vec (vals mp))] 
    (apply str (apply-template temp tpl-str args))))

(defn bind-template-with-file 
  "テンプレファイルとマップを指定してtemplatingするよ"
  [f mp]
  (let [tpl (read-template-file f)] 
    (bind-template tpl mp)))

こんなかんじでつかう。Johnnyって誰? ジョニー・サンダース?

(template_engine/bind-template
  '(nm " is " age " years old.")
  {'nm "Johnny" 'age 12})
;; => "Johnny is 12 years old."

外部ファイルのclojureにあてこむときにはこんなかんじ。

tpl/view.clj

(nm " is " age " years old.")

でもってこんなかんじで実行。

(template_engine/bind-template-with-file 
  "tpl/view.clj"
  {'nm "Nancy" 'age 13})
;; => "Nancy is 13 years old."

load-fileをつかうとそのまま評価されちゃうので、テンプレートファイルをリストのまま読みこむようにヘルパー関数的なものも書いた。
これはあとからも使えそうね。つかこれまたclojure.contribにあったりして。

つかNancyって誰? ナンシー関?