Created
February 21, 2010 10:51
-
-
Save rcampbell/310258 to your computer and use it in GitHub Desktop.
Autowiring a model and taxonomy to a view using Enlive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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