...
1# Gonja
2
3[![GoDoc](https://godoc.org/github.com/noirbizarre/gonja?status.svg)](https://godoc.org/github.com/noirbizarre/gonja)
4[![Build Status](https://travis-ci.org/noirbizarre/gonja.svg?branch=master)](https://travis-ci.org/noirbizarre/gonja)
5[![Coverage Status](https://codecov.io/gh/noirbizarre/gonja/branch/master/graph/badge.svg)](https://codecov.io/gh/noirbizarre/gonja)
6
7`gonja` is [`pongo2`](https://github.com/flosch/pongo2) fork intended to be aligned on `Jinja` template syntax instead of the `Django` one.
8
9Install/update using `go get` (no dependencies required by `gonja`):
10```
11go get github.com/noirbizarre/gonja
12```
13
14Please use the [issue tracker](https://github.com/noirbizarre/gonja/issues) if you're encountering any problems with gonja or if you need help with implementing tags or filters ([create a ticket!](https://github.com/noirbizarre/gonja/issues/new)).
15
16## First impression of a template
17
18```HTML+Django
19<html><head><title>Our admins and users</title></head>
20{# This is a short example to give you a quick overview of gonja's syntax. #}
21
22{% macro user_details(user, is_admin=false) %}
23 <div class="user_item">
24 <!-- Let's indicate a user's good karma -->
25 <h2 {% if (user.karma >= 40) || (user.karma > calc_avg_karma(userlist)+5) %}
26 class="karma-good"{% endif %}>
27
28 <!-- This will call user.String() automatically if available: -->
29 {{ user }}
30 </h2>
31
32 <!-- Will print a human-readable time duration like "3 weeks ago" -->
33 <p>This user registered {{ user.register_date|naturaltime }}.</p>
34
35 <!-- Let's allow the users to write down their biography using markdown;
36 we will only show the first 15 words as a preview -->
37 <p>The user's biography:</p>
38 <p>{{ user.biography|markdown|truncatewords_html:15 }}
39 <a href="/user/{{ user.id }}/">read more</a></p>
40
41 {% if is_admin %}<p>This user is an admin!</p>{% endif %}
42 </div>
43{% endmacro %}
44
45<body>
46 <!-- Make use of the macro defined above to avoid repetitive HTML code
47 since we want to use the same code for admins AND members -->
48
49 <h1>Our admins</h1>
50 {% for admin in adminlist %}
51 {{ user_details(admin, true) }}
52 {% endfor %}
53
54 <h1>Our members</h1>
55 {% for user in userlist %}
56 {{ user_details(user) }}
57 {% endfor %}
58</body>
59</html>
60```
61
62## Features (and new in gonja)
63
64 * Entirely rewritten from the ground-up.
65 * [Advanced C-like expressions](https://github.com/noirbizarre/gonja/blob/master/template_tests/expressions.tpl).
66 * [Complex function calls within expressions](https://github.com/noirbizarre/gonja/blob/master/template_tests/function_calls_wrapper.tpl).
67 * [Easy API to create new filters and tags](http://godoc.org/github.com/noirbizarre/gonja#RegisterFilter) ([including parsing arguments](http://godoc.org/github.com/noirbizarre/gonja#Parser))
68 * Additional features:
69 * Macros including importing macros from other files (see [template_tests/macro.tpl](https://github.com/noirbizarre/gonja/blob/master/template_tests/macro.tpl))
70 * [Template sandboxing](https://godoc.org/github.com/noirbizarre/gonja#TemplateSet) ([directory patterns](http://golang.org/pkg/path/filepath/#Match), banned tags/filters)
71
72
73## How you can help
74
75 * Write [filters](https://github.com/noirbizarre/gonja/blob/master/builtins/filters.go#L3) / [statements](https://github.com/noirbizarre/gonja/blob/master/builtins/statements.go#L4)
76 * Write/improve code tests (use the following command to see what tests are missing: `go test -v -cover -covermode=count -coverprofile=cover.out && go tool cover -html=cover.out` or have a look on [gocover.io/github.com/noirbizarre/gonja](http://gocover.io/github.com/noirbizarre/gonja))
77 * Write/improve template tests (see the `testData/` directory)
78 * Write middleware, libraries and websites using gonja. :-)
79
80# Documentation
81
82For a documentation on how the templating language works you can [head over to the Jinja documentation](https://jinja.palletsprojects.com). gonja aims to be compatible with it.
83
84You can access gonja's API documentation on [godoc](https://godoc.org/github.com/noirbizarre/gonja).
85
86## Caveats
87
88### Filters
89
90 * **format**: `format` does **not** take Python's string format syntax as a parameter, instead it takes Go's. Essentially `{{ 3.14|stringformat:"pi is %.2f" }}` is `fmt.Sprintf("pi is %.2f", 3.14)`.
91 * **escape** / **force_escape**: Unlike Jinja's behaviour, the `escape`-filter is applied immediately. Therefore there is no need for a `force_escape`-filter yet.
92
93# API-usage examples
94
95Please see the documentation for a full list of provided API methods.
96
97## A tiny example (template string)
98
99```Go
100// Compile the template first (i. e. creating the AST)
101tpl, err := gonja.FromString("Hello {{ name|capfirst }}!")
102if err != nil {
103 panic(err)
104}
105// Now you can render the template with the given
106// gonja.Context how often you want to.
107out, err := tpl.Execute(gonja.Context{"name": "axel"})
108if err != nil {
109 panic(err)
110}
111fmt.Println(out) // Output: Hello Axel!
112```
113
114## Example server-usage (template file)
115
116```Go
117package main
118
119import (
120 "github.com/noirbizarre/gonja"
121 "net/http"
122)
123
124// Pre-compiling the templates at application startup using the
125// little Must()-helper function (Must() will panic if FromFile()
126// or FromString() will return with an error - that's it).
127// It's faster to pre-compile it anywhere at startup and only
128// execute the template later.
129var tpl = gonja.Must(gonja.FromFile("example.html"))
130
131func examplePage(w http.ResponseWriter, r *http.Request) {
132 // Execute the template per HTTP request
133 out, err := tpl.Execute(gonja.Context{"query": r.FormValue("query")})
134 if err != nil {
135 http.Error(w, err.Error(), http.StatusInternalServerError)
136 }
137 w.WriteString(out)
138}
139
140func main() {
141 http.HandleFunc("/", examplePage)
142 http.ListenAndServe(":8080", nil)
143}
144```
145
146# Benchmark
147
148The benchmarks have been run on the my machine (`Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz`) using the command:
149
150 go test -bench . -cpu 1,2,4,8
151
152All benchmarks are compiling (depends on the benchmark) and executing the `testData/complex.tpl` template.
153
154The results are:
155
156 BenchmarkFromCache 30000 41259 ns/op
157 BenchmarkFromCache-2 30000 42776 ns/op
158 BenchmarkFromCache-4 30000 44432 ns/op
159 BenchmarkFromFile 3000 437755 ns/op
160 BenchmarkFromFile-2 3000 472828 ns/op
161 BenchmarkFromFile-4 2000 519758 ns/op
162 BenchmarkExecute 30000 41984 ns/op
163 BenchmarkExecute-2 30000 48546 ns/op
164 BenchmarkExecute-4 20000 104469 ns/op
165 BenchmarkCompileAndExecute 3000 428425 ns/op
166 BenchmarkCompileAndExecute-2 3000 459058 ns/op
167 BenchmarkCompileAndExecute-4 3000 488519 ns/op
168 BenchmarkParallelExecute 30000 45262 ns/op
169 BenchmarkParallelExecute-2 100000 23490 ns/op
170 BenchmarkParallelExecute-4 100000 24206 ns/op
171
172Benchmarked on August 18th 2019.
View as plain text