pongo2

package module
v1.0.0-...-d0c4746 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2014 License: MIT Imports: 17 Imported by: 0

README

pongo2

GoDoc Build Status Coverage Status GitTip

pongo2 is the successor of pongo, a Django-syntax like templating-language.

Install/update using go get (no dependencies required by pongo2):

go get -u github.com/flosch/pongo2

Please use the issue tracker if you're encountering any problems with pongo2 or if you need help with implementing tags or filters (create a ticket!). If possible, please use playground to create a short test case on what's wrong and include the link to the snippet in your issue.

New: Try pongo2 out in the pongo2 playground.

First impression of a template

<html><head><title>Our admins and users</title></head>
{# This is a short example to give you a quick overview of pongo2's syntax. #}

{% macro user_details(user, is_admin=false) %}
	<div class="user_item">
		<!-- Let's indicate a user's good karma -->
		<h2 {% if (user.karma >= 40) || (user.karma > calc_avg_karma(userlist)+5) %}
			class="karma-good"{% endif %}>
			
		     	<!-- This will call user.String() automatically if available: -->
			{{ user }}
		</h2>

		<!-- Will print a human-readable time duration like "3 weeks ago" -->
		<p>This user registered {{ user.register_date|naturaltime }}.</p>
		
		<!-- Let's allow the users to write down their biography using markdown;
		     we will only show the first 15 words as a preview -->
		<p>The user's biography:</p>
		<p>{{ user.biography|markdown|truncatewords_html:15|safe }}
			<a href="/user/{{ user.id }}/">read more</a></p>
		
		{% if is_admin %}<p>This user is an admin!</p>{% endif %}
	</div>
{% endmacro %}

<body>
	<!-- Make use of the macro defined above to avoid repetitive HTML code
	     since we want to use the same code for admins AND members -->
	
	<h1>Our admins</h1>
	{% for admin in adminlist %}
		{{ user_details(admin, true) }}
	{% endfor %}
	
	<h1>Our members</h1>
	{% for user in userlist %}
		{{ user_details(user) }}
	{% endfor %}
</body>
</html>

Development status

Latest stable release: v1.0 (go get -u gopkg.in/flosch/pongo2.v1 / v1-branch) [read the announcement]

Current development: v1.1 (master-branch)

Topic Status
Django version compatibility: 1.7
Missing (planned) filters: none (hints)
Missing (planned) tags: none (hints)

Please also have a look on the caveats and on the official add-ons.

Features (and new in pongo2)

Recent API changes within pongo2

If you're using pongo2, you might be interested in this section. Since pongo2 is still in beta, there could be (backwards-incompatible) API changes over time. To keep track of these and therefore make it painless for you to adapt your codebase, I'll list them here.

How you can help

  • Write filters / tags (see tutorial) by forking pongo2 and sending pull requests
  • 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)
  • Write/improve template tests (see the template_tests/ directory)
  • Write middleware, libraries and websites using pongo2. :-)

Documentation

For a documentation on how the templating language works you can head over to the Django documentation. pongo2 aims to be compatible with it.

You can access pongo2's API documentation on godoc.

Blog post series

Caveats

Filters

In general, if any filter is outputting unsafe characters (e. g. HTML tags in filter linebreaks), you will have to apply the "safe" filter on it afterwards currently. It is not done automatically.

  • date / time: The date and time filter are taking the Golang specific time- and date-format (not Django's one) currently. Take a look on the format here.
  • stringformat: stringformat 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 Django's behaviour, the escape-filter is applied immediately. Therefore there is no need for a force_escape-filter yet.
Tags
  • for: All the forloop fields (like forloop.counter) are written with a capital letter at the beginning. For example, the counter can be accessed by forloop.Counter and the parentloop by forloop.Parentloop.
  • now: takes Go's time format (see date and time-filter).
Misc
  • not in-operator: You can check whether a map/struct/string contains a key/field/substring by using the in-operator (or the negation of it): {% if key in map %}Key is in map{% else %}Key not in map{% endif %} or {% if !(key in map) %}Key is NOT in map{% else %}Key is in map{% endif %}.

Add-ons, libraries and helpers

Official

  • ponginae - A web-framework for Go (using pongo2).
  • pongo2-addons - Official additional filters/tags for pongo2 (for example a markdown-filter). They are in their own repository because they're relying on 3rd-party-libraries.

3rd-party

Please add your project to this list and send me a pull request when you've developed something nice for pongo2.

API-usage examples

Please see the documentation for a full list of provided API methods.

A tiny example (template string)

// Compile the template first (i. e. creating the AST)
tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
if err != nil {
	panic(err)
}
// Now you can render the template with the given 
// pongo2.Context how often you want to.
out, err := tpl.Execute(pongo2.Context{"name": "florian"})
if err != nil {
	panic(err)
}
fmt.Println(out) // Output: Hello Florian!

Example server-usage (template file)

package main

import (
	"github.com/flosch/pongo2"
	"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 tplExample = pongo2.Must(pongo2.FromFile("example.html"))

func examplePage(w http.ResponseWriter, r *http.Request) {
	// Execute the template per HTTP request
	err := tplExample.ExecuteWriter(pongo2.Context{"query": r.FormValue("query")}, w)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

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 template_tests/complex.tpl template.

The results are:

BenchmarkExecuteComplex                    50000             57419 ns/op
BenchmarkExecuteComplex-2                  50000             55087 ns/op
BenchmarkExecuteComplex-4                  50000             58348 ns/op
BenchmarkExecuteComplex-8                  50000             58805 ns/op
BenchmarkCompileAndExecuteComplex          10000            154818 ns/op
BenchmarkCompileAndExecuteComplex-2        10000            141209 ns/op
BenchmarkCompileAndExecuteComplex-4        10000            153821 ns/op
BenchmarkCompileAndExecuteComplex-8        10000            160542 ns/op
BenchmarkParallelExecuteComplex            50000             60640 ns/op
BenchmarkParallelExecuteComplex-2          50000             32646 ns/op
BenchmarkParallelExecuteComplex-4         100000             21752 ns/op
BenchmarkParallelExecuteComplex-8         100000             18713 ns/op

Documentation

Overview

A Django-syntax like template-engine

Blog posts about pongo2 (including introduction and migration): https://www.florian-schlachter.de/?tag=pongo2

Complete documentation on the template language: https://docs.djangoproject.com/en/dev/topics/templates/

Try out pongo2 live in the pongo2 playground: https://www.florian-schlachter.de/pongo2/

Make sure to read README.md in the repository as well.

A tiny example with template strings:

(Snippet on playground: https://www.florian-schlachter.de/pongo2/?id=1206546277)

// Compile the template first (i. e. creating the AST)
tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
if err != nil {
    panic(err)
}
// Now you can render the template with the given
// pongo2.Context how often you want to.
out, err := tpl.Execute(pongo2.Context{"name": "fred"})
if err != nil {
    panic(err)
}
fmt.Println(out) // Output: Hello Fred!

Index

Constants

View Source
const (
	TokenError = iota
	EOF

	TokenHTML

	TokenKeyword
	TokenIdentifier
	TokenString
	TokenNumber
	TokenSymbol
)
View Source
const Version = "1.0"

Version string

Variables

View Source
var (

	// Available symbols in pongo2 (within filters/tag)
	TokenSymbols = []string{

		"==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",

		"(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
	}

	// Available keywords in pongo2
	TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as"}
)

Functions

func Logf

func Logf(sender string, format string, items ...interface{})

func RegisterFilter

func RegisterFilter(name string, fn FilterFunction)

Registers a new filter. If there's already a filter with the same name, RegisterFilter will panic. You usually want to call this function in the filter's init() function: http://golang.org/doc/effective_go.html#init

See http://www.florian-schlachter.de/post/pongo2/ for more about writing filters and tags.

func RegisterTag

func RegisterTag(name string, parserFn TagParser)

Registers a new tag. If there's already a tag with the same name, RegisterTag will panic. You usually want to call this function in the tag's init() function: http://golang.org/doc/effective_go.html#init

See http://www.florian-schlachter.de/post/pongo2/ for more about writing filters and tags.

func RenderTemplateFile

func RenderTemplateFile(fn string, ctx Context) string

Shortcut; renders a template file directly. Panics when providing a malformed template or an error occurs during execution.

func RenderTemplateString

func RenderTemplateString(s string, ctx Context) string

Shortcut; renders a template string directly. Panics when providing a malformed template or an error occurs during execution.

func SetDebug

func SetDebug(b bool)

Types

type Context

type Context map[string]interface{}

Use this Context type to provide constants, variables, instances or functions to your template.

pongo2 automatically provides meta-information or functions through the "pongo2"-key. Currently, context["pongo2"] contains the following keys:

  1. version: returns the version string

Template examples for accessing items from your context:

{{ myconstant }}
{{ myfunc("test", 42) }}
{{ user.name }}
{{ pongo2.version }}

func (Context) Update

func (c Context) Update(other Context) Context

type ExecutionContext

type ExecutionContext struct {
	Autoescape bool
	Public     Context
	Private    Context
	Shared     Context
	// contains filtered or unexported fields
}

If you're writing a custom tag, your tag's Execute()-function will have access to the ExecutionContext. This struct stores anything about the current rendering process's Context including the Context provided by the user (field Public). You can safely use the Private context to provide data to the user's template (like a 'forloop'-information). The Shared-context is used to share data between tags. All ExecutionContexts share this context.

Please be careful when accessing the Public data. PLEASE DO NOT MODIFY THE PUBLIC CONTEXT (read-only).

To create your own execution context within tags, use the NewExecutionContext(parent) function.

func NewChildExecutionContext

func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext

func (*ExecutionContext) Error

func (ctx *ExecutionContext) Error(msg string, token *Token) error

type Expression

type Expression struct {
	// contains filtered or unexported fields
}

func (*Expression) Evaluate

func (expr *Expression) Evaluate(ctx *ExecutionContext) (*Value, error)

func (*Expression) Execute

func (expr *Expression) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) error

func (*Expression) FilterApplied

func (expr *Expression) FilterApplied(name string) bool

type FilterFunction

type FilterFunction func(in *Value, param *Value) (out *Value, err error)

type IEvaluator

type IEvaluator interface {
	Evaluate(*ExecutionContext) (*Value, error)
	FilterApplied(name string) bool
}

type INode

type INode interface {
	Execute(*ExecutionContext, *bytes.Buffer) error
}

type INodeEvaluator

type INodeEvaluator interface {
	INode
	IEvaluator
}

type INodeTag

type INodeTag interface {
	INode
}

type NodeWrapper

type NodeWrapper struct {
	Endtag string
	// contains filtered or unexported fields
}

func (*NodeWrapper) Execute

func (wrapper *NodeWrapper) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) error

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

The parser provides you a comprehensive and easy tool to work with the template document and arguments provided by the user for your custom tag.

The parser works on a token list which will be provided by pongo2. A token is a unit you can work with. Tokens are either of type identifier, string, number, keyword, HTML or symbol.

(See Token's documentation for more about tokens)

func (*Parser) Consume

func (p *Parser) Consume()

Consume one token. It will be gone forever.

func (*Parser) ConsumeN

func (p *Parser) ConsumeN(count int)

Consume N tokens. They will be gone forever.

func (*Parser) Count

func (p *Parser) Count() int

Returns the total token count.

func (*Parser) Current

func (p *Parser) Current() *Token

Returns the current token.

func (*Parser) Error

func (p *Parser) Error(msg string, token *Token) error

Produces a nice error message and returns an error-object. The 'token'-argument is optional. If provided, it will take the token's position information. If not provided, it will automatically use the CURRENT token's position information.

func (*Parser) Get

func (p *Parser) Get(i int) *Token

Returns tokens[i] or NIL (if i >= len(tokens))

func (*Parser) GetR

func (p *Parser) GetR(shift int) *Token

Returns tokens[current-position + shift] or NIL (if (current-position + i) >= len(tokens))

func (*Parser) Match

func (p *Parser) Match(typ TokenType, val string) *Token

Returns the CURRENT token if the given type AND value matches. Consumes this token on success.

func (*Parser) MatchOne

func (p *Parser) MatchOne(typ TokenType, vals ...string) *Token

Returns the CURRENT token if the given type AND *one* of the given values matches. Consumes this token on success.

func (*Parser) MatchType

func (p *Parser) MatchType(typ TokenType) *Token

Returns the CURRENT token if the given type matches. Consumes this token on success.

func (*Parser) ParseExpression

func (p *Parser) ParseExpression() (INodeEvaluator, error)

func (*Parser) Peek

func (p *Parser) Peek(typ TokenType, val string) *Token

Returns the CURRENT token if the given type AND value matches. It DOES NOT consume the token.

func (*Parser) PeekN

func (p *Parser) PeekN(shift int, typ TokenType, val string) *Token

Returns the tokens[current position + shift] token if the given type AND value matches for that token. DOES NOT consume the token.

func (*Parser) PeekOne

func (p *Parser) PeekOne(typ TokenType, vals ...string) *Token

Returns the CURRENT token if the given type AND *one* of the given values matches. It DOES NOT consume the token.

func (*Parser) PeekType

func (p *Parser) PeekType(typ TokenType) *Token

Returns the CURRENT token if the given type matches. It DOES NOT consume the token.

func (*Parser) PeekTypeN

func (p *Parser) PeekTypeN(shift int, typ TokenType) *Token

Returns the tokens[current position + shift] token if the given type matches. DOES NOT consume the token for that token.

func (*Parser) Remaining

func (p *Parser) Remaining() int

Returns the UNCONSUMED token count.

func (*Parser) WrapUntilTag

func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, error)

Wraps all nodes between starting tag and "{% endtag %}" and provides one simple interface to execute the wrapped nodes. It returns a parser to process provided arguments to the tag.

type TagParser

type TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, error)

This is the function signature of the tag's parser you will have to implement in order to create a new tag.

'doc' is providing access to the whole document while 'arguments' is providing access to the user's arguments to the tag:

{% your_tag_name some "arguments" 123 %}

start_token will be the *Token with the tag's name in it (here: your_tag_name).

Please see the Parser documentation on how to use the parser. See RegisterTag()'s documentation for more information about writing a tag as well.

type Template

type Template struct {
	// contains filtered or unexported fields
}

func FromFile

func FromFile(filename string) (*Template, error)

Loads a template from a filename and returns a Template instance. The filename must either be relative to the application's directory or be an absolute path.

func FromString

func FromString(tpl string) (*Template, error)

Loads a template from string and returns a Template instance.

func Must

func Must(tpl *Template, err error) *Template

Helper function which panics, if a Template couldn't successfully parsed. This is how you would use it:

var baseTemplate = pongo2.Must(pongo2.FromFile("templates/base.html"))

func (*Template) Execute

func (tpl *Template) Execute(context Context) (string, error)

Executes the template and returns the rendered template as a string

func (*Template) ExecuteBytes

func (tpl *Template) ExecuteBytes(context Context) ([]byte, error)

Executes the template and returns the rendered template as a []byte

func (*Template) ExecuteWriter

func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error

Executes the template with the given context and writes to writer (io.Writer) on success. Context can be nil. Nothing is written on error; instead the error is being returned.

type Token

type Token struct {
	Filename string
	Typ      TokenType
	Val      string
	Line     int
	Col      int
}

func (*Token) String

func (t *Token) String() string

type TokenType

type TokenType int

type Value

type Value struct {
	// contains filtered or unexported fields
}

func ApplyFilter

func ApplyFilter(name string, value *Value, param *Value) (*Value, error)

Applies a filter to a given value using the given parameters. Returns a *pongo2.Value or an error.

func AsValue

func AsValue(i interface{}) *Value

Converts any given value to a pongo2.Value Usually being used within own functions passed to a template through a Context or within filter functions.

Example:

AsValue("my string")

func MustApplyFilter

func MustApplyFilter(name string, value *Value, param *Value) *Value

Like ApplyFilter, but panics on an error

func (*Value) Bool

func (v *Value) Bool() bool

Returns the underlying value as bool. If the value is not bool, false will always be returned. If you're looking for true/false-evaluation of the underlying value, have a look on the IsTrue()-function.

func (*Value) CanSlice

func (v *Value) CanSlice() bool

Checks whether the underlying value is of type array, slice or string. You normally would use CanSlice() before using the Slice() operation.

func (*Value) Contains

func (v *Value) Contains(other *Value) bool

Checks whether the underlying value (which must be of type struct, map, string, array or slice) contains of another Value (e. g. used to check whether a struct contains of a specific field or a map contains a specific key).

Example:

AsValue("Hello, World!").Contains(AsValue("World")) == true

func (*Value) EqualValueTo

func (v *Value) EqualValueTo(other *Value) bool

Checks whether two values are containing the same value or object.

func (*Value) Float

func (v *Value) Float() float64

Returns the underlying value as a float (converts the underlying value, if necessary). If it's not possible to convert the underlying value, it will return 0.0.

func (*Value) Index

func (v *Value) Index(i int) *Value

Get the i-th item of an array, slice or string. Otherwise it will return NIL.

func (*Value) Integer

func (v *Value) Integer() int

Returns the underlying value as an integer (converts the underlying value, if necessary). If it's not possible to convert the underlying value, it will return 0.

func (*Value) Interface

func (v *Value) Interface() interface{}

Gives you access to the underlying value.

func (*Value) IsBool

func (v *Value) IsBool() bool

Checks whether the underlying value is a bool

func (*Value) IsFloat

func (v *Value) IsFloat() bool

Checks whether the underlying value is a float

func (*Value) IsInteger

func (v *Value) IsInteger() bool

Checks whether the underlying value is an integer

func (*Value) IsNil

func (v *Value) IsNil() bool

Checks whether the underlying value is NIL

func (*Value) IsNumber

func (v *Value) IsNumber() bool

Checks whether the underlying value is either an integer or a float.

func (*Value) IsString

func (v *Value) IsString() bool

Checks whether the underlying value is a string

func (*Value) IsTrue

func (v *Value) IsTrue() bool

Tries to evaluate the underlying value the Pythonic-way:

Returns TRUE in one the following cases:

  • int != 0
  • uint != 0
  • float != 0.0
  • len(array/chan/map/slice/string) > 0
  • bool == true
  • underlying value is a struct

Otherwise returns always FALSE.

func (*Value) Iterate

func (v *Value) Iterate(fn func(idx, count int, key, value *Value) bool, empty func())

Iterates over a map, array, slice or a string. It calls the function's first argument for every value with the following arguments:

idx      current 0-index
count    total amount of items
key      *Value for the key or item
value    *Value (only for maps, the respective value for a specific key)

If the underlying value has no items or is not one of the types above, the empty function (function's second argument) will be called.

func (*Value) Len

func (v *Value) Len() int

Returns the length for an array, chan, map, slice or string. Otherwise it will return 0.

func (*Value) Negate

func (v *Value) Negate() *Value

Tries to negate the underlying value. It's mainly used for the NOT-operator and in conjunction with a call to return_value.IsTrue() afterwards.

Example:

AsValue(1).Negate().IsTrue() == false

func (*Value) Slice

func (v *Value) Slice(i, j int) *Value

Slices an array, slice or string. Otherwise it will return an empty []int.

func (*Value) String

func (v *Value) String() string

Returns a string for the underlying value. If this value is not of type string, pongo2 tries to convert it. Currently the following types for underlying values are supported:

  1. string
  2. int/uint (any size)
  3. float (any precision)
  4. bool
  5. time.Time
  6. String() will be called on the underlying value if provided

NIL values will lead to an empty string. Unsupported types are leading to their respective type name.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL