Skip to content

Commit e332052

Browse files
committed
Documentation clean up.
1 parent 54a30e1 commit e332052

File tree

3 files changed

+62
-148
lines changed

3 files changed

+62
-148
lines changed

DESIGN.md

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,108 +2,111 @@ This file contains some proposals for this project's design decisions.
22

33
## Different forms of templates
44

5+
If we wanted to make an analogy between this library and the JVM:
6+
- the text-based, user-facing templates are the JVM languages,
7+
- the "data template" format is the JVM byte code specification,
8+
- the template renderers are the JVM runtimes.
9+
510
### User facing templates
611

7-
Text-based, described using one of the supported syntax, e.g.
12+
**Text-based**, they are described using one of the supported syntax, e.g.
813

9-
- `silly-j`, Hopen's default syntax.
10-
- Selmer's syntax.
14+
- Selmer's syntax, inspired from [Django](https://docs.djangoproject.com/en/dev/ref/templates/builtins/).
15+
- "Silly-j", a syntax designed to feel familiar to Clojure users.
16+
- Any other syntax that can be transformed into the "data template" format.
1117

1218
### Parsed template
13-
19+
1420
> "It's just data"
1521
>
1622
> -- Rich Hickey
1723
18-
It is designed to be read and processed, it could be written down
19-
into an EDN file and be loaded back later. This format is a standard
20-
for this library and it has a spec.
24+
Also known as the "data template", it is designed to be serialized into
25+
EDN format, to be read and processed by programs.
26+
27+
This format is a standard for this library and it has a spec.
2128

2229
Note: In the long run, this format has the potential to become a standard
2330
to represent parsed templates in the Clojure eco-system, so we *may* want
24-
to provide its spec as a separate project.
31+
to provide its spec as a separate project at some point.
2532

2633
Example of parsed template in the EDN format:
2734

2835
```clojure
2936
[;; Some immediate values.
3037
"hello " :foo-kw bar-symb true 3
3138

32-
;; Access to the context's root.
33-
#get [root :name]
34-
#get-in [root [:persons 0 :name]]
35-
#collect [root [:persons [0 1 2] :name]]
39+
;; Referencing data.
40+
(hopen/root :name)
41+
(hopen/ctx :name)
42+
(my-var :name)
3643

37-
;; Access to the current context.
38-
#get [ctx :name]
39-
#get-in [ctx [:persons 0 :name]]
40-
#collect [ctx [:persons [0 1 2] :name]]
44+
(get-in my-var [:persons 0 :name])
45+
(collect my-var [:persons [0 1 2] :name])
4146

42-
;; Scoped alteration of the current context.
43-
#let [[; Note: we can't bind the root symbol.
44-
ctx #get-in [ctx [:persons 0]]]
45-
[#get [ctx :name]]]
47+
;; Scoped redefinition of the current context.
48+
(let [hopen/ctx (get-in hopen/ctx [:persons 0])]
49+
(hopen/ctx :name))
4650

4751
;; Scoped binding on an arbitrary symbol.
48-
#let [[person #get-in [ctx [:persons 0]]]
49-
[#get [person :name]]]
52+
(let [person (get-in hopen/ctx [:persons 0])]
53+
(person :name))
5054

5155
;; Iterations, scoped binding on cartesian products.
52-
#for [[person #get [ctx :persons]]
53-
[#get [person :name]]]
56+
(for [person (hopen/ctx :persons)]
57+
(person :name))
5458

5559
;; Conditional rendering.
56-
#if [#get [ctx :cond?]
57-
[#get-in [ctx [:persons 0 :name]]] ; then clause
58-
[#get-in [ctx [:persons 1 :name]]]] ; else clause
60+
(if (hopen/ctx :something)
61+
(get-in hopen/ctx [:persons 0 :name]) ; then clause
62+
(get-in hopen/ctx [:persons 1 :name])) ; else clause
5963

60-
;; Conditional rendering, level up.
61-
#case [#get [ctx :chosen-one]
62-
[0 [#get-in [ctx [:persons 0 :name]]]
63-
1 [#get-in [ctx [:persons 1 :name]]]
64-
2 [#get-in [ctx [:persons 2 :name]]]
65-
3 [#get-in [ctx [:persons 3 :name]]]
66-
4 [#get-in [ctx [:persons 4 :name]]]]
67-
"else clause"]
64+
;; This works as well inline.
65+
(get-in hopen/ctx [:persons (if (hopen/ctx :something) 0 1) :name])
6866

69-
;; Filters
70-
#square [3]
67+
;; More conditional rendering.
68+
(case (hopen/ctx :chosen-one)
69+
0 nil
70+
:the-one (get-in hopen/ctx [:persons 1 :name])
71+
true {:name "Trueman"}
72+
"else clause")
7173

74+
;; Helper functions.
75+
(inc (hopen/ctx :index))
76+
(upper (person :name))
77+
78+
;; Helper functions can also be user-defined.
79+
(sum-of-squares 3 4) ; {'sum-of-squares (fn [x y] (+ (* x x) (* y y)))}
7280
]
7381
```
7482

75-
### Compiled template
83+
### Renderer
7684

77-
As a Clojure transducer, this template form is optimized for rendering.
78-
It outputs a serie of printable data (e.g. numbers, strings, etc ..).
85+
This template form is optimized for rendering. How it is doing it and
86+
how it is used depends on the implementation.
7987

80-
In addition to the default portable implementation of the
81-
template compiler, some environment specific implementations
82-
could be provided for better performance.
88+
Just like the JVM runtimes, there are some which are pure interpreters and some
89+
which optimize using Just-In-Time compilation techniques.
8390

8491
## Hopen's API
8592

93+
Something like that:
94+
8695
```clojure
87-
(require '[hopen.core :as hopen]
96+
(require '[hopen.renderer.xf :as rxf]
8897
'[hopen.syntax.silly-j :as silly-j])
8998

9099
;; Parse a template using one of the supported syntax.
91-
(def parsed-template
100+
(def data-template
92101
(silly-j/parse "Hello {@:name}, {@:n} * {@:n} = {square @:n}"))
93102

94-
;; Compile a template for rendering.
95-
(def template-xf
96-
(hopen/compile parsed-template
97-
(assoc hopen/builtin-filters
103+
;; Prepare a template for rendering using one of the supported renderer.
104+
(def renderer
105+
(rxf/renderer data-template
106+
(update rxf/default-env :bindings assoc
98107
'square (fn [x] (* x x)))))
99108

100-
;; Render your data into a string.
101-
(transduce template-xf str [{:name "Alice", :n 3}])
109+
;; Use the renderer to output the data into a string.
110+
(transduce renderer str [{:name "Alice", :n 3}])
102111
; => "Hello Alice, 3 * 3 = 9"
103112
```
104-
105-
# Implementation Requirements
106-
107-
- The filter functions should be standard clojure functions,
108-
they should be used in the same way by any of the different
109-
template syntax available.

README.md

Lines changed: 2 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -5,94 +5,6 @@
55

66
A simple, modern, flexible and portable template engine for Clojure environments.
77

8-
# Get started
8+
# Status
99

10-
The API is very simple.
11-
12-
```clojure
13-
(render "Hello {@:name}" {:name "john"})
14-
; "Hello john"
15-
16-
(def parsed-template (parse-template "Hello {@:name}")
17-
18-
parsed-template
19-
; [[:hopen/text "Hello "]
20-
; [:hopen/env [:name]]]
21-
22-
(render parsed-template {:name "john"})
23-
; "Hello john"
24-
```
25-
26-
# Default Template Syntax
27-
28-
This syntax is designed to be familiar to Clojure developers.
29-
30-
```clojure
31-
; Start and end of template expressions.
32-
{...}
33-
{@:name}
34-
35-
; Replace expression delimiters with something else.
36-
{}! "(" ")" ; It feels like in Clojure.
37-
{}! "<" ">" ; Possible when you are not rendering html.
38-
{}! "%" "%" ; They can be the same on each side.
39-
{}! "{{" "}}" ; Not limited to a char.
40-
{}! "{[" "]}"
41-
{}! "%[" "]%"
42-
43-
; Replace expression delimiters back to their default.
44-
{}! "{" "}"
45-
46-
; Note: the parenthesis usage is similar to Clojure, except that they are
47-
; implicit at the root level when there are more than 1 element.
48-
49-
; an immediate value, can be any clojure value except functions.
50-
42
51-
[1 5 7 {"hello" true}]
52-
53-
; data at provided path in the root context of the document.
54-
_@@[:persons 0 :name]
55-
_@:persons
56-
57-
; data at provided relative path in the current context.
58-
@@[:persons 0 :name]
59-
@:name
60-
61-
; thread operation
62-
-> val (add 10) (mul 2) str
63-
64-
; conditionally render val1
65-
if cond val1
66-
67-
; conditionally render val1 or val2
68-
if cond val1 val2
69-
70-
; Block start and end.
71-
{#for person @@[:persons]}
72-
Hi {person :name}, nice to meet you!
73-
{#end}
74-
75-
; Blocks that kills any content of the same line.
76-
dead text {##for person @@[:persons]} dead text
77-
Hi {person :name}, nice to meet you!
78-
dead here {##end} dead here
79-
80-
81-
; binds symbols to value in content.
82-
#let symb1 val1, symb2 val2 ...
83-
84-
; Symbols which are bound to a map or an array become an n-arity selector function.
85-
{##let persons @@[:persons]}
86-
Hi {persons 0 :name}, nice to meet you!
87-
The 3 next persons are: {-> (persons [1 2 3] :name) (join ", ")}.
88-
the rest of persons are: {-> (persons (range 4) :name) (join ", ")}.
89-
{##end}
90-
91-
; iteration of content with cartesian product bindings.
92-
#for symb1 coll1, symb2 coll2 ...
93-
94-
; conditionally render contents
95-
#if cond
96-
#elif cond
97-
#else
98-
```
10+
The project is currently a work in progress.

src/hopen/core.cljc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
(ns hopen.core
2-
(:refer-clojure :exclude [compile])
32
(:require [clojure.string :as str]))
43

54
(defn- tpl-eval [env element]

0 commit comments

Comments
 (0)