Template Inheritance

129 views
Skip to first unread message

David Nolen

unread,
Oct 9, 2009, 1:58:55 PM10/9/09
to Enlive
So I've been working on a couple of web projects recently in Python
and have been enjoying how Django and Mako support template
inheritance. What I don't like about these projects is something that
Enlive handles really, really well - that the division of labor
between programmer and designer is clear.

That is a designer can work purely with HTML+CSS and the programmer
can create the template that transforms that HTML+CSS into a dynamic
page with an Enlive template.

Now something that I haven't been able to get my head around is the
best way to simulate template inheritance from these other templating
libraries.

On IRC channel I mentioned a common scenario. You have a base template
like so:

<html>
<head />
<body>
<header />
<main>
<col1 />
<col2 />
<col3 />
</main>
<footer />
</body>
<html>

Now for a page I might like inherit from this base template and
substitute a nav template into col1. On different pages I may want to
put different HTML widgets into the nav template based on different
criteria.

On the IRC channel Christophe mentioned that this could be handled by
function composition but I haven't really seen any examples how this
might be accomplished.

If it can be done with function composition perhaps there should be an
example that ships with Enlive to help out us newbs :)

David

Christophe Grand

unread,
Oct 10, 2009, 7:23:03 AM10/10/09
to enliv...@googlegroups.com
Hi David!
The "functional" way I alluded to is:
;; Let layout.html be the html above
(deftemplate layout "layout.html"
 [col1 col2 col3]
  [:col1] (content col1)
  [:col2] (content col2)
  [:col3] (content col3))

(def inheriting-page [arg1 arg2]
  (layout (widget1 arg1 arg2) (widget2 arg1 arg2) (widget3 arg1 arg2)))
;; where widgetN are snippets


Before trying to reproduce "traditional" inheritance, I'd like to point out that traditional iheritance often mixes designer and developer responsabilities.
How traditional inheritance can look like with Enlive? A child template produces a page, a parent template takes the interesting part of the child template and put them in place.
Basically it can be written:
(deftemplate layout "layout.html" [child]
  [:col1] (substitute (select child [:col1]))
  [:col2] (substitute (select child [:col2]))
  [:col3] (substitute (select child [:col2])))

Then (comp layout child-template) will work if child-template is a snippet (but it's a detail that can be worked around -- eg by building on emit* and snippet*).
This composition can even be driven by child-template.html: eg by looking for a meta tag whose content attribute contains an identifier for the parent template -- haven't we crossed the designer/developer boundary there?

I pasted an example here http://gist.github.com/206792

I can devise other combinations where the parent automatically lookup for elements with known ids or classes and merge them in its tree etc. But I think that the most effective way is that you give me: a parent.html, a child.html, the clojure code, each showing what you'd like to minimally write, then we'll be able to discuss how best to implement that on top of Enlive.

Christophe

Meikel Brandmeyer

unread,
Oct 10, 2009, 3:45:10 PM10/10/09
to enliv...@googlegroups.com
Hello David and Christophe.

Ah. Ok. I think I understand. It's good to look at it from a distance.
How do traditional template systems work? You specify some chain:

A → B → C → D

Something uses D. Then the system goes all the way up to A and starts
processing. Then it hits some template thing (eg. ${self.title()} in
Mako notation) and goes down again to find the lowest definition of
title in the chain.

Compare this to the functional approach above:

We start processing at D (which is like C and B (maybe a function
containing) a snippet, A would be a template) and pass on the result
as input to C. When we reach A, would are finished. Every level has
only to know itself and maybe the parameters required for the next
step. Here a short example.

(deftemplate A "base.html"
[kontent title sidebar]
[:title] (content title)
[:sidebar] (substitute sidebar)
[:div#content] (content kontent))

(defn b
[content title]
(A content title (make-b-sidebar)))

(defn x
[content title]
(let [x-content (snippet .....)]
(A (x-content content) title (make-x-sidebar))))

Looking at this, makes it seem so simple. And the inheritance is done
implicitly by the function call chain. I will definitively give this a
try.

I think my main mistake, was think to much about the HTML templates
themselves. This is maybe a relic of the traditional systems, where
logic is put into the template. With enlive the template is really
only the skeleton with the logic put into the clojure code.

Sincerely
Meikel

Wilson MacGyver

unread,
Oct 10, 2009, 4:05:08 PM10/10/09
to enliv...@googlegroups.com
I believe lift framework written in scala using a very similar
functional snippet approach.

Christophe Grand

unread,
Oct 11, 2009, 5:24:25 AM10/11/09
to enliv...@googlegroups.com
Hi Meikel,

On Sat, Oct 10, 2009 at 9:45 PM, Meikel Brandmeyer <m...@kotka.de> wrote:
Ah. Ok. I think I understand. It's good to look at it from a distance. How do traditional template systems work? You specify some chain:

A → B → C → D

Something uses D. Then the system goes all the way up to A and starts processing. Then it hits some template thing (eg. ${self.title()} in Mako notation) and goes down again to find the lowest definition of title in the chain.

It's very much like OOP: D inherits render() from A (via B and C) and calls title() which is overridden in D.
I think it's worth trying to use functional templates and not implementing an object system on top of Enlive.

Christophe

Meikel Brandmeyer

unread,
Oct 11, 2009, 4:41:38 PM10/11/09
to enliv...@googlegroups.com
Hi Christophe,

Am 11.10.2009 um 11:24 schrieb Christophe Grand:

> It's very much like OOP: D inherits render() from A (via B and C)
> and calls title() which is overridden in D.
> I think it's worth trying to use functional templates and not
> implementing an object system on top of Enlive.

This is exactly what I will do! I will definitively try the functional
approach!

I will post my results to the list. :) (if the move to the new
apartment allows early results.. sigh)

Sincerely
Meikel

Reply all
Reply to author
Forward
0 new messages