# Gonja [![GoDoc](https://godoc.org/github.com/noirbizarre/gonja?status.svg)](https://godoc.org/github.com/noirbizarre/gonja) [![Build Status](https://travis-ci.org/noirbizarre/gonja.svg?branch=master)](https://travis-ci.org/noirbizarre/gonja) [![Coverage Status](https://codecov.io/gh/noirbizarre/gonja/branch/master/graph/badge.svg)](https://codecov.io/gh/noirbizarre/gonja) `gonja` is [`pongo2`](https://github.com/flosch/pongo2) fork intended to be aligned on `Jinja` template syntax instead of the `Django` one. Install/update using `go get` (no dependencies required by `gonja`): ``` go get github.com/noirbizarre/gonja ``` Please 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)). ## First impression of a template ```HTML+Django Our admins and users {# This is a short example to give you a quick overview of gonja's syntax. #} {% macro user_details(user, is_admin=false) %}

= 40) || (user.karma > calc_avg_karma(userlist)+5) %} class="karma-good"{% endif %}> {{ user }}

This user registered {{ user.register_date|naturaltime }}.

The user's biography:

{{ user.biography|markdown|truncatewords_html:15 }} read more

{% if is_admin %}

This user is an admin!

{% endif %}
{% endmacro %}

Our admins

{% for admin in adminlist %} {{ user_details(admin, true) }} {% endfor %}

Our members

{% for user in userlist %} {{ user_details(user) }} {% endfor %} ``` ## Features (and new in gonja) * Entirely rewritten from the ground-up. * [Advanced C-like expressions](https://github.com/noirbizarre/gonja/blob/master/template_tests/expressions.tpl). * [Complex function calls within expressions](https://github.com/noirbizarre/gonja/blob/master/template_tests/function_calls_wrapper.tpl). * [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)) * Additional features: * Macros including importing macros from other files (see [template_tests/macro.tpl](https://github.com/noirbizarre/gonja/blob/master/template_tests/macro.tpl)) * [Template sandboxing](https://godoc.org/github.com/noirbizarre/gonja#TemplateSet) ([directory patterns](http://golang.org/pkg/path/filepath/#Match), banned tags/filters) ## How you can help * 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) * 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)) * Write/improve template tests (see the `testData/` directory) * Write middleware, libraries and websites using gonja. :-) # Documentation For 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. You can access gonja's API documentation on [godoc](https://godoc.org/github.com/noirbizarre/gonja). ## Caveats ### Filters * **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)`. * **escape** / **force_escape**: Unlike Jinja's behaviour, the `escape`-filter is applied immediately. Therefore there is no need for a `force_escape`-filter yet. # API-usage examples Please see the documentation for a full list of provided API methods. ## A tiny example (template string) ```Go // Compile the template first (i. e. creating the AST) tpl, err := gonja.FromString("Hello {{ name|capfirst }}!") if err != nil { panic(err) } // Now you can render the template with the given // gonja.Context how often you want to. out, err := tpl.Execute(gonja.Context{"name": "axel"}) if err != nil { panic(err) } fmt.Println(out) // Output: Hello Axel! ``` ## Example server-usage (template file) ```Go package main import ( "github.com/noirbizarre/gonja" "net/http" ) // Pre-compiling the templates at application startup using the // little Must()-helper function (Must() will panic if FromFile() // or FromString() will return with an error - that's it). // It's faster to pre-compile it anywhere at startup and only // execute the template later. var tpl = gonja.Must(gonja.FromFile("example.html")) func examplePage(w http.ResponseWriter, r *http.Request) { // Execute the template per HTTP request out, err := tpl.Execute(gonja.Context{"query": r.FormValue("query")}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } w.WriteString(out) } func main() { http.HandleFunc("/", examplePage) http.ListenAndServe(":8080", nil) } ``` # Benchmark The benchmarks have been run on the my machine (`Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz`) using the command: go test -bench . -cpu 1,2,4,8 All benchmarks are compiling (depends on the benchmark) and executing the `testData/complex.tpl` template. The results are: BenchmarkFromCache 30000 41259 ns/op BenchmarkFromCache-2 30000 42776 ns/op BenchmarkFromCache-4 30000 44432 ns/op BenchmarkFromFile 3000 437755 ns/op BenchmarkFromFile-2 3000 472828 ns/op BenchmarkFromFile-4 2000 519758 ns/op BenchmarkExecute 30000 41984 ns/op BenchmarkExecute-2 30000 48546 ns/op BenchmarkExecute-4 20000 104469 ns/op BenchmarkCompileAndExecute 3000 428425 ns/op BenchmarkCompileAndExecute-2 3000 459058 ns/op BenchmarkCompileAndExecute-4 3000 488519 ns/op BenchmarkParallelExecute 30000 45262 ns/op BenchmarkParallelExecute-2 100000 23490 ns/op BenchmarkParallelExecute-4 100000 24206 ns/op Benchmarked on August 18th 2019.