Skip to content

Instantly share code, notes, and snippets.

@rcampbell
Created February 21, 2010 10:51
Show Gist options
  • Save rcampbell/310258 to your computer and use it in GitHub Desktop.
Save rcampbell/310258 to your computer and use it in GitHub Desktop.
Autowiring a model and taxonomy to a view using Enlive
(ns render
(:refer-clojure :exclude [empty complement])
(:use [clojure.set :only [difference]]
[net.cgrand.enlive-html]))
(def this-ns *ns*)
(defmulti populate
"Populate the given tag with content v and
taxonomy options if supported by the tag"
{:arglist '([node [k v] taxonomy])}
#(keyword (str (ns-name this-ns))
(name (:tag (first %&)))))
(defmethod populate ::select [node [k v] taxonomy]
(at node [:option]
(clone-for [option (taxonomy k)]
(do-> (set-attr :value option)
#(if (= (apply str v) option)
((set-attr :selected "selected") %) %)
(content option)))))
; Be careful here: checkboxes and radios aren't supported because
; browsers don't post off/false, but just omit sending the form
; param entirely. Maybe throw an UnsupportedOperationException...
; The solution for now is to map them to hidden true/false inputs
; and use js to manage the actual checkbox UI component state
(defmethod populate ::input [node [k v] taxonomy]
(let [type (keyword ((node :attrs) :type))]
(cond (or (= :text type)
(= :hidden type)) ((set-attr :value (apply str v)) node)
; (or (= :radio type)
; (= :checkbox type)) (#(if v
; ((set-attr :checked "checked") %)
; ((remove-attr :checked) %)) node)
:else node)))
(defmethod populate ::textarea [node [k v] taxonomy]
((content (apply str v)) node))
(derive ::ul ::list)
(derive ::ol ::list)
(defmethod populate ::list [node [k v] taxonomy]
(at node [:li]
(clone-for [i v] (content i))))
(defmethod populate :default [node [k v] taxonomy]
((content (apply str v)) node))
(defn- populator [e taxonomy]
(fn [node] (populate node e taxonomy)))
(defn- merge-in-model [source coordinate taxonomy]
"Iterates over a map, selecting the template's nodes whose ids
match the keys and populating them with the corresponding values"
(reduce (fn [source e]
(transform source [(selector [(id= (name (key e)))])
(populator e taxonomy)]))
source coordinate))
(defn- remove-nil-nodes [source coordinate taxonomy]
"Iterates over a seq of ids missing from the selected
coordinate, killing each matching template node"
(reduce (fn [source id]
(transform source [(selector [(id= id)]) nil]))
source (map name (difference (set (keys taxonomy))
(set (keys coordinate))))))
(defn render [coordinate taxonomy]
(fn [source]
(let [populated (merge-in-model source coordinate taxonomy)
filtered (remove-nil-nodes populated coordinate taxonomy)]
filtered)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment