• Stars
    star
    243
  • Rank 166,489 (Top 4 %)
  • Language
    Clojure
  • Created almost 11 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Enlive next: faster, better, broader

Enliven Build Status

Enliven: a (not yet) continuous templating system.

WARNING very wet paint

Enliven is the successor to Enlive

Even dictionaries say so:

Enlive: To Enliven (Obs.)

Enliven also stands for Enlive N(ext).

Not tied to HTML

Currently Enliven can template plain text (enliven.text) and html (enliven.html). The HTML "domain" is even composite since it uses the text "domain" to template text nodes.

Simpler selectors

Selectors are now functions from loc to locs. Most domains should expose a sel function to coerce a domain specific selector (eg a CSS-selector in string or a regex) to a selector fn.

Parallel execution of transformations

All transformations occur at once for maximum declarativeness (and it enables good performance). It follows that two transformations can't work on the same node or on a node and one of its ancestors.

This constraint is heavily mitigated by infinite-resolution selectors and transformation-refined selectors.

Infinite-resolution selectors

Selectors don't stop at nodes as specified in the DOM. Any node can be subdivided at will!

For example classes in a class attribute can be targeted independently. Each character of a text node can be transformed independently. You can append/content/prepend on an element without conflicts!

However this happens under the hood, see:

(at 
  "div" (class "important" :important)
  "div" (class "footnote" :foot-note))

Those two transformations won't conflict because they refine their selector.

Point-free

Templates take a single argument which is the data model to render.

Transformations don't take as arguments the actual values but keys or paths into the model.

=> ((static-template
     (enliven.html.jsoup/parse "<div>")
     "div" (content :sentence))
     {:sentence "Hello world"})
"<html><head></head><body><div>Hello world</div></body></html>"

Some transformations like dup introduces a new scope so that sub-transformation can only see the current item.

=> ((static-template
     (enliven.html.jsoup/parse "<ul><li>")
     "li" (dup :todos 
            (content []))) ; [] is the empty path, so points to the whole value
     {:todos ["Laundry" "Walk the dog"]})
"<html><head></head><body><ul><li>Laundry</li><li>Walk the dog</li></ul></body></html>"

It's fast

Generating a 5x5 table. (pr-str just dumps the 5*5 vector):

  • pr-str (24.5µs),
  • enliven (24.8µs),
  • hiccup (44.8µs),
  • enlive (130µs),
  • laser (1280µs).

https://gist.github.com/cgrand/8471718

Dev details

Processing model

  1. Nodes are selected
  2. For each selected node, associated transformations are grounded -- this is where the nodes are refined to avoid conflicts. The result is a set of rules.
  3. A hierarchical plan is created from the rules.
  4. The plan can either be executed as is or "compiled".

Paths and segments

Rules (as produced by the grounding phase) are pairs of a path and an action.

As a first approximation, you can imagine that they are going to be used in conjunction with assoc-in to updates parts of the structure.

If you have one rule on [:a :b] and another one [:a :c], they can be executed in any order (they may even be performed concurrently as long as you coordinate on the longest common prefix).

However rules on [:a :b] and [:a :b :c] can't be freely reordered and as such are forbidden.

To alleviate this constraint, segments are a bit more expressive than simple keys in associative data structures.

Segments are used in conjunction with fetch and putback which are akin to get and assoc.

Numbers, strings and keywords are segments and Enliven knows that if they are different they don't impede.

Slices (ranges) are also segments and non-overlapping ranges don't impede. (Slices and numbers should not be mixed and that's the main purpose of path canonicalization).

Other segments are opaque and thus will conflict with any other segment. A good practice is to use non-opaque segments as much as possible.

For example the html/classes segment can't be used on an HTML element: otherwise it would clash with any other transformation on the element. So the html/classes segment is meant to be used after [:attrs :class] thus giving to the planner the information that html/classes owns only the class attribute.

Furthermore the html/classes segment returns a map so individual classes can be addressed individually. For example [:attrs :class html/classes "class1"] and [:attrs :class html/classes "class2"] don't clash.

A custom segment thus creates a sort of contention point but by having it to return an associative structure it becomes a coordination point.

Locs

Locs are a kind of zippers for associative structures. They support only two moves: up and down (which takes a segment as additional argument).

License

Copyright © 2014 Christophe Grand

Distributed under the Eclipse Public License, the same as Clojure.

More Repositories

1

enlive

a selector-based (à la CSS) templating and transformation system for Clojure
Clojure
1,619
star
2

xforms

Extra transducers and reducing fns for Clojure(script)
Clojure
573
star
3

moustache

a micro web framework/internal DSL to wire Ring handlers and middlewares
Clojure
261
star
4

seqexp

Regexp for sequences!
Clojure
241
star
5

parsley

a DSL for creating total and truly incremental parsers in Clojure
Clojure
200
star
6

macrovich

A set of three macros to ease writing `*.cljc` supporting Clojure, Clojurescript and self-hosted Clojurescript.
Clojure
163
star
7

sjacket

Structural code transformations for the masses.
Clojure
114
star
8

megaref

STM ref types that allow for more concurrency on associative values.
Clojure
95
star
9

spreadmap

Evil project to turn excel spreadsheets in persistent reactive structures.
Clojure
89
star
10

regex

a regex DSL for those who prefer verbose composable regexes to terse ones
Clojure
88
star
11

poucet

trace as data for Clojure/JVM
Clojure
86
star
12

confluent-map

A persistent confluent map for Clojure
Java
39
star
13

packed-printer

Compact pretty printer
Clojure
37
star
14

sqrel

The SQL library that won't drive you nuts.
Clojure
37
star
15

utils

useful functions and extensible macros
Clojure
36
star
16

indexed-set

A set implementation which supports unicity constraints and maintains summaries (indexes).
Clojure
31
star
17

replay

Instant test suites from repl transcript.
Clojure
26
star
18

cljs-js-repl

Upgradable self hosted clojurescript repl
Clojure
21
star
19

parsnip

parsley is dead, long live parsnip!
Clojure
17
star
20

boring

A tunnel-boring library
Clojure
11
star
21

enlivez

Clojure
8
star
22

dynvars

dynamic bindings in callback hell
Clojure
5
star
23

sandbox

expeiments, works in progress etc.
Clojure
4
star
24

advent2017

Clojure
2
star
25

trainings.geneva.2012

Ressources issues des formations Clojure données du 13 au 15 mai 2012
Clojure
2
star
26

berlin-tron

Clojure
1
star