Hi there Find attached some code that uses apache velocity for command line based processing, i.e. it's not setup for servlets; i use this as an offline pre processor for web sites.
It does hierarchical templating of files in a source directory and copies to a target directory. The templating maps are contained in (by default) __pp.clj files in the source tree. Further, your __pp.clj maps can also contain functions for dynamically populating the template. The nice thing is that each directory can contain it's own __pp.clj file each of which can override it's parent definitions (in a zope like 'aquisition'). Processing can also be controlled by passing command line arguments which are added to the templates top level context. It's not documented, but if anyone is interested I can do some. cheers bd On Tue, 2009-05-26 at 19:47 -0700, markgunnels wrote: > Hopefully this doesn't get me booed off the message board but is there > a Clojure equivalent to Ruby's ERB? I'm try to use Clojure to perform > code generation and, while it makes for an excellent language to write > a parser in, I can't quite figure out the best way to actually > template out the code. > > Thanks for any help, > Mark > > P.S. Rich -- if you read this, Clojure is terrific. > > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---
(ns ipowerhouse.live (:import java.io.File (org.apache.velocity VelocityContext Template) (org.apache.velocity.app Velocity) (org.apache.velocity.app.event InvalidReferenceEventHandler EventCartridge) java.util.Properties (org.apache.commons.io FileUtils FilenameUtils)) (:use ipowerhouse.utils.useful)) (declare *pp*) (declare *extensions*) (declare *src*) (declare *dst*) (declare *ignore*) (declare *verbose*) (def *rvc* (VelocityContext.)) (def Rcontexts (ref (hash-map))) (def Rdirty (ref (vector))) (defn printIt [& m] (if (= *verbose* "yes") (println m))) ; event handler stuff unused as of now ... (comment (defn getIRH [] (proxy [InvalidReferenceEventHandler] [] (invalidGetMethod [ctx rf obj prop info] (printIt "**get:" info) ) (invalidSetMethod [ctx left right info] (printIt "**set:" info) ) (invalidMethod [ctx rf obj method info] (printIt "**method:" info) ))) (defn addEventHandler [vc] (let [ec (EventCartridge.)] (.addEventHandler ec (getIRH)) (.attachToContext ec vc))) (addEventHandler *rvc*) ) (defn removeSrc [#^File f] (let [p (.getPath f) ctop (count *src*)] (subs p ctop))) (defn check-dirty [p fileName] (let [src (File. (str *src* "/" p fileName)) dst (File. (str *dst* "/" p fileName))] (when (and (.exists src) (.exists dst)) (when (FileUtils/isFileNewer src dst) (printIt src "is dirty") (dosync (commute Rdirty conj p)))))) (defn dirty? [f] (some #(.startsWith f %1) @Rdirty)) (defn forced? [] (.containsKey *rvc* "force")) (defn getDir [f] (FilenameUtils/getPath (FilenameUtils/normalizeNoEndSeparator f))) (defn closestContext [curdir] (loop [d curdir] (let [p (getDir d)] (if (contains? @Rcontexts p) (do ;(printIt "ctx for " curdir " is " p) (@Rcontexts p)) (if (= p "") *rvc* (recur p)))))) ;(def getClosestContext (memoize MgetClosestContext)) (defn keyCheck [k] (if (keyword? k) (name k) k)) (defn keywordsToStrings [z] (if (map? z) (reduce (fn [a k] (assoc a (keyCheck k) (keywordsToStrings (z k)))) {} (keys z)) z)) (defn execute [f prms] (if (fn? f) (f prms) f)) (defn mapIntoContext [m context] (let [conv (keywordsToStrings m)] (doseq [[k v] conv] (.put context k v)) context)) (defn construct [#^File f] "this only checks dirs where *pp* are actually present" (let [fr (java.io.FileReader. f) p (removeSrc f) ctxKey (getDir p)] (if-not (contains? @Rcontexts ctxKey) (let [ vc (if (= ctxKey "") (VelocityContext. *rvc*) (VelocityContext. (closestContext p)))] (let [m (load-reader fr)] (when m (let [conv (keywordsToStrings m) tmpParams { :parentFile (.getParentFile f) :context vc :uri p }] (reduce (fn [a k] (.put a k (execute (conv k) tmpParams)) a) vc (keys conv))))) (check-dirty ctxKey (str "/" *pp*)) (dosync (alter Rcontexts assoc ctxKey vc)))))) (defn context[top] (let [ pps (filter #(re-find (re-pattern (str *pp* "$")) (.getAbsolutePath %1)) (file-seq (java.io.File. top))) pathlen (fn [f] (.length (.getAbsolutePath f)))] (doseq [s (sort-by pathlen pps)] (construct s)))) (defn newer? [srcf dstf] (FileUtils/isFileNewer srcf dstf)) (defn ignore? [fs] (if *ignore* (filter (fn [f] (let [p (.getPath f)] (every? #(= (.indexOf p %1) -1) *ignore*))) fs) fs)) (defn preserveExec [s d] (if (.canExecute s) (.setExecutable d true true))) (defn copyPreserve [s d] (FileUtils/copyFile s d) (preserveExec s d)) (defn process [dest] (let [ gen (fn [f srcf dstf] (let [ closestCtx (closestContext f) tmpl (Velocity/getTemplate f)] (printIt "templating " f ) (try (barf dstf (with-out-str (.merge tmpl closestCtx *out*))) (preserveExec srcf dstf) (catch Exception e (print e))))) template (fn [#^String f #^File srcf #^File dstf] (if (.exists dstf) (when (or (newer? srcf dstf) (dirty? f) (forced?)) (gen f srcf dstf)) (gen f srcf dstf))) copy (fn [#^File srcf #^File dstf] (if (.exists dstf) (when (newer? srcf dstf) (do (printIt "copying " dstf) (copyPreserve srcf dstf))) (do (printIt "creating " dstf) (copyPreserve srcf dstf)))) run (fn [srcf] (let [relative (removeSrc srcf) dstf (File. (str *dst* relative))] (if (.isDirectory srcf) (when (not (.exists dstf)) (FileUtils/forceMkdir dstf)) (if (FilenameUtils/isExtension relative *extensions*) (template relative srcf dstf) (copy srcf dstf))))) ] (dorun (map run (ignore? (file-seq (java.io.File. *src*))))))) (defn checkFile [p] (let [f (File. p)] (if (.exists f) (FilenameUtils/normalizeNoEndSeparator p) (throw (Exception. (str "File " p " does not exist")))))) (defn validateConfig [m] (if-not (contains? m :src) (throw (Exception. "Need :src")) (def *src* (checkFile (m :src)))) (if-not (contains? m :dst) (throw (Exception. "Need :dst")) (def *dst* (checkFile (m :dst)))) (if-not (contains? m :meta) (def *pp* "__pp.clj") (def *pp* (m :meta))) (if-not (contains? m :ignore) (def *ignore* nil) (def *ignore* (m :ignore))) (if-not (contains? m :ext) (def *extensions* (into-array ["html","json","conf","css"])) (def *extensions* (into-array (m :ext))))) (let [config (second *command-line-args*)] (let [fr (java.io.FileReader. (File. config)) m (load-reader fr)] (validateConfig m))) (println "src --> " *src*) (println "dst --> " *dst*) (println "ignoring:" *ignore*) (.put *rvc* "_STAGE_TOP" *dst*) (.put *rvc* "_SOURCE_TOP" *src*) (.put *rvc* "_FNU" FilenameUtils) (.put *rvc* "_ESC" (org.apache.velocity.tools.generic.EscapeTool.)) (println "Command line:") (doseq [[k v] (drop 1 (partition 2 *command-line-args*))] (println "\t" k ": " v) (if (= k "VERBOSE") (def *verbose* v) (.put *rvc* k v))) (let [p (Properties.)] (.setProperty p "file.resource.loader.path" *src*) (Velocity/init p)) (time (do (context *src*) (check-dirty "" "VM_global_library.vm") (println "\nprocessing ...") (process *src*)))
{ :nginxUser "blackdog", :site "igameware - web 2.0 games", :software-supplier "ipowerhouse.com", :software-supplier-email "black...@ipowerhouse.com" }
(import '(org.apache.commons.io FileUtils FilenameUtils)) { :helpFiles (fn [prms] (let [c (.length (str (.get (prms :context) "_SOURCE_TOP") "/sites/igameware/www"))] (sort (map #(java.io.File. %1) (filter #(FilenameUtils/isExtension %1 "html") (map #(subs (.getPath %1) c) (file-seq (java.io.File. (prms :parentFile) "help")))))))) }