Enlive: yet another HTML templating library

unsorted — cgrand, 19 January 2009 @ 16 h 04 min
[UPDATE] I have rewritten Enlive, this posts doesn’t work with the actual Enlive.

Enlive is a selector based templating library.
Its main design goal is to decouple html and presentation code, that’s why Enlive templates are plain old html files (it will ease roundtripping with designers).
In code, you have to declare where (using CSS-like selectors) and how (using clojure code) to alter the html template.
The resulting templating functions are compiled (the html tree isn’t transformed at runtime) and yields a seq of strings.

Here is an example:

(deftemplate microblog-template "net/cgrand/enlive_html/example.html" [title posts]
  [:title] title
  [:h1] title
  [:div.no-msg] (when-not (seq posts) ~(html/show))
  [:div.post] (for [{:keys [title body]} posts]
           ~(at
              [:h2] title
              [:p] body)))

;; at the repl:
net.cgrand.enlive-html.examples=> (apply str (microblog-template "Hello user!"
             [{:title "post #1"
               :body "hello with dangerous chars: <>&"}
              {:title "post #2"
               :body "dolor ipsum"}]))

<em>"&lt;html>&lt;head>&lt;title>Hello user!&lt;/title>&lt;/head>
&lt;body>&lt;h1>Hello user!&lt;/h1>
&lt;div class=\"post\">&lt;h2>post #1&lt;/h2>
&lt;p>hello with dangerous chars: &amp;lt;&amp;gt;&amp;amp;&lt;/p>&lt;/div>
&lt;div class=\"post\">&lt;h2>post #2&lt;/h2>
&lt;p>dolor ipsum&lt;/p>&lt;/div>&lt;/body>&lt;/html>"</em>

(NB: manually edited to add linebreaks.)

(Disclaimer: original idea by Ozzilee)

Functionally growing a tree

mistakes — cgrand, 11 January 2009 @ 19 h 32 min

I was trying to write a restartable parser in Clojure when it occured to me that I was doing it wrong by not using clojure.zip to build the parse tree.
Update: follow-up

try-or, or-try, try-else or else-try?

utilities — cgrand, 8 January 2009 @ 18 h 26 min

I can’t decide which name is best for this macro:

(defmacro try-or
 "Evaluates exprs one at a time, from left to right. If a form returns a 
  value, this value is returned. If a form throws an exception, the next 
  form is evaluated. 
  If the last form throws an exception, the exception isn't caught." 
 ([] nil)
 ([form] form)
 ([form & forms]
   `(try 
      ~form
      (catch Exception e#
        (try-or ~@forms)))))

Recursive seqs

utilities — cgrand, 5 January 2009 @ 16 h 11 min

Recursively defined sequences are pretty but difficult to get right when you don’t want to assign the sequence to a var. Here is a couple of macros to ease recursive definitions.

(defmacro rec-cat 
 "Similar to lazy-cat but binds the resulting sequence using the supplied 
  binding-form, allowing recursive expressions. The first collection 
  expression must not be recursive and must return a non-nil seq."
 [binding-form expr & rec-exprs]
  `(let [rec-rest# (atom nil)
         result# (lazy-cat ~expr (force @rec-rest#))
         ~binding-form result#]
     (swap! rec-rest# (constantly (delay (lazy-cat ~@rec-exprs))))
     result#))
         
(defmacro rec-cons 
 "Similar to lazy-cons but binds the resulting sequence using the supplied 
  binding-form, allowing recursive expressions. The first expression must 
  not be recursive and must return a non-nil seq."
 [binding-form expr & rec-exprs]
  `(let [rec-rest# (atom nil)
         result# (lazy-cons ~expr (force @rec-rest#))
         ~binding-form result#]
     (swap! rec-rest# (constantly (delay (lazy-cat ~@rec-exprs))))
     result#))

Examples:

Natural numbers (just like (iterate inc 0)):

(rec-cons naturals 0 (map inc naturals))

fibonnaci sequence:

(rec-cat fibs [0 1] (map + fibs (rest fibs)))

Chouser’s cute reduction:

(defn reduction
  "Returns a lazy seq of the intermediate values of the reduction (as
  per reduce) of coll by f, starting with init."
  ([f coll]
   (if (seq coll)
     (rec-cons reductions (first coll) (map f reductions (rest coll)))
     (cons (f) nil)))
  ([f init coll]
   (rec-cons reductions init (map f reductions coll))))

Surprising

unsorted — cgrand, 19 November 2008 @ 21 h 46 min
user=> (time (dotimes [i 100000000] [i]))
"Elapsed time: 5928.406543 msecs"
nil
user=> (time (dotimes [i 100000000] #(i)))
"Elapsed time: 1774.025749 msecs"
nil

So, it seems that creating a closure is faster than creating a vector. Cool.

Clojure Golf: fib-seq

unsorted — cgrand, 30 October 2008 @ 0 h 45 min

Revisiting a classic:

(def fib-seq
  (lazy-cat [0 1] (map + fib-seq (rest fib-seq))))

Clojure Golf: subsets-by-card (2)

unsorted — cgrand, 22 October 2008 @ 10 h 14 min

I wasn’t happy with the last one. At least this one is lazier and in increasing cardinality order.

(defn subsets-by-card [s]
  (reduce (fn [ssbc x]
           (map (fn [a b] (concat a (map #(conj % x) b)))
             (concat ssbc [nil]) (concat [nil] ssbc)))
    [[#{}]] s))

Decreasing order:

(defn subsets-by-card-reverse [s]
  (reduce (fn [ssbc x]
            (map (fn [a b] (concat a (map #(<ins>disj</ins> % x) b)))
              (concat ssbc [nil]) (concat [nil] ssbc)))
    [[<ins>(set s)</ins>]] s))

Clojure Golf: subsets-by-card

unsorted — cgrand, 20 October 2008 @ 23 h 09 min
(defn subsets-by-card [s]
  (reduce (fn [ssbc x] 
            (concat
              (map (fn [a b] (concat a (map #(conj % x) b)))
                (cons nil ssbc) ssbc)
              [[#{}]]))
    [[#{}]] s))

(a tough one)

Clojure Golf: subsets

unsorted — cgrand, @ 23 h 08 min
(defn subsets [s]
  (reduce (fn [ss x] (concat ss (map #(conj % x) ss))) [#{}] s))

Clojure Golf: combinations (2)

unsorted — cgrand, 19 October 2008 @ 9 h 41 min
(defn combinations [& cs]
  (reduce #(for [v %1 i %2] (conj v i)) [[]] cs))
« Previous PageNext Page »
(c) 2024 Clojure and me | powered by WordPress with Barecity