tal

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 15, 2015 License: MIT Imports: 8 Imported by: 0

README

tal - Go Implementation of Template Attribute Language

TAL is a template language created by the Zope project and implemented in a number of different languages. The language is very compact, consisting of just 7 commands with a further 4 METAL commands available for macros. This makes learning and understanding TAL more straightforward than many other templating languages.

The tal library provides an implementation of TAL (and TALES and METAL) for Go. Performance of tal is similar to the standard library's html/template (benchmarks show faster execution but more allocations).

A simple example of how to use tal:

package main

import "github.com/owlfish/tal"
import "os"
import "strings"

func main() {
	template := `
		<html>
			<h1 tal:content="Title">Title Here</h1>
			<div tal:repeat="book Library">
				<h2 tal:content="book/Title">Book Title</h2>
				<b tal:content="book/Author">Author</b>
				<p tal:condition="book/Classification">Classification <b tal:replace="book/Classification">Book Type</b></p>
			</div>
		</html>
	`

	type Book struct {
		Title          string
		Author         string
		Classification string
	}

	books := []Book{{"Raising Steam", "Terry Pratchett", "Fiction"}, {Title: "My Life", Author: "Anon"}}

	context := make(map[string]interface{})
	context["Library"] = books
	context["Title"] = "Library"

	tmpl, _ := tal.CompileTemplate(strings.NewReader(template))
	tmpl.Render(context, os.Stdout)
}

The output from the above is:

		<html>
			<h1>Library</h1>
			<div>
				<h2>Raising Steam</h2>
				<b>Terry Pratchett</b>
				<p>Classification Fiction</p>
			</div>
			<div>
				<h2>My Life</h2>
				<b>Anon</b>
			</div>
		</html>

To get started with the latest version of tal use go get github.com/owlfish/tal. Documentation is provided through godoc and available online at godoc.org

Status

Development is now complete with a full implementation of TAL, TALES and METAL. The documentation, examples, tests and benchmarks are at a reasonable level of completeness (>94% test coverage).

No API changes are planned at this point, although behaviour may change if required to address defects. When I wrote a Python implementation of TAL, SimpleTAL, I found and fixed a great number of issues over time. The Go implementation uses a lot of the learning from SimpleTAL, so should hopefully mature more quickly.

Documentation

Overview

Package tal implements the TAL template language for generating HTML5 output.

TAL templates are HTML5 documents containing additional attributes that, using the TALES expression language, change the structure and content of the resulting document.

Here is a basic example (without error handling) that prints "<html><h1>Raising Steam</h1> by Author: Terry Pratchett"

type Book struct {
	Title  string
	Author string
}

book := Book{"Raising Steam", "Terry Pratchett"}
tmpl, _ := tal.CompileTemplate(strings.NewReader(`<html><h1 tal:content="title">Title goes here</h1> by <b tal:replace="string: Author: $Author"></b>`))
tmpl.Render(book, os.Stdout)

The tal package also supports METAL macros as described later on.

TAL Commands

The tal package supports all TAL commands defined by Zope (see http://docs.zope.org/zope2/zope2book/AppendixC.html).

Define

tal:define sets a local or global variable:

tal:define="[local | global] name expression [; define-expression...]

Description: Sets the value of "name" to "expression". By default the name will be applicable in the "local" scope, which consists of this tag, and all other tags nested inside this tag. If the "global" keyword is used then this name will keep its value for the rest of the document.

If the expression evaluates to nil, the variable will be set to the value nil.

Example:

<div tal:define="global title book/theTitle; local chapterTitle book/chapter/theTitle">

Condition

tal:condition makes output of an element conditional:

tal:condition="expression"

Description: If the expression evaluates to true then this tag and all its children will be output. If the expression evaluates to false then this tag and all its children will not be included in the output.

An expression is considered false if it is not found, evaluates to nil, is an empty string, is zero or is a boolean false. All other values are treated as true.

Example:

<h1 tal:condition="user/firstLogin">Welcome to this page!</h1>

Repeat

tal:repeat replicates an element a number of times:

tal:repeat="name expression"

Description: Evaluates "expression", and if it is a slice or array, repeats this tag and all children once for each item in the sequence. The "name" will be set to the value of the item in the current iteration, and is also the name of the repeat variable. The repeat variable is accessible using the TAL path: repeat/name and has the following properties:

index 		- Iteration number starting from zero
number 		- Iteration number starting from one
even 		- True if this is an even iteration
odd 		- True if this is an odd iteration
start 		- True if this is the first item in the sequence
end 		- True if this is the last item in the sequence
length 		- The length of the sequence
letter 		- The lower case letter for this iteration, starting at "a"
Letter		- Upper case version of letter
roman 		- Iteration number in Roman numerals, starting at i
Roman	 	- Upper case version of roman

The "first" and "last" properties are not supported.

If the expression evaluates to tal.Default, the contents of the template will be kept as-is with no repeat variable set. If the expression evaluates to an object other than a slice or array, or the slice or array is empty, the element and it's children are removed.

Example:

<table>
	<tr tal:repeat="fruit basket">
		<td tal:content="repeat/fruit/number"></td>
		<td tal:content="fruit/name"></td>
	</tr>
</table>

Content

tal:content replaces the content of an element:

tal:content="[text | structure] expression"

Description: Replaces the contents of the tag with the value of "expression". By default, and if the "text" keyword is present, then the value of the expression will be escaped as required (i.e. characters "&<> will be escaped). If the "structure" keyword is present then the value will be output with no escaping performed.

If the expression evaluates to tal.Default then the template content is kept as is. If the expression evaluates to nil then the child contents of the tag will be empty.

Example:

<h1 tal:content="user/firstName"></h1>

Replace

tal:replace replaces the whole element:

tal:replace="[text | structure] expression"

Description: Behaves identically to tal:content, except that the tag is removed from the output (as if tal:omit-tag had been used).

Example:

<h1>Welcome <b tal:replace="user/firstName"></b></h1>

Attributes

tal:attributes sets or removes attributes on the element:

tal:attributes="name expression[;attributes-expression]"

Description: Evaluates each "expression" and replaces the tag's attribute "name". If the expression evaluates to nil then the attribute is removed from the tag. If the expression evaluates to default then the original tag's attribute is kept. If the attribute is a HTML5 boolean attribute then it will be removed if the expression is not found, evaluates to nil, is an empty string, is zero or is a boolean false. All other expression values are treated as true and the attribute value is set to equal to the attribute name.

If the "expression" requires a semi-colon then it must be escaped by using ";;".

Example:

<a tal:attributes="href user/homepage;title user/fullname">Your Homepage</a>

Omit Tag

tal:omit-tag removes the start and end tags:

tal:omit-tag="expression"

Description: Removes the tag (leaving the tags content) if the expression evaluates to true. If expression is empty then it is taken as true.

An expression is considered false if it is not found, evaluates to nil, is an empty string, is zero or is a boolean false. All other values are treated as true.

Example:

<p><b tal:omit-tag="not:user/firstVisit">Welcome</b> to this page!</h1>

TALES Expressions

The expressions used in TAL are called TALES expressions. The simplest TALES expression is a path which references a value, e.g. page/body references the body property of the page object. Objects are passed as the first argument of the Render method on a compiled template and must be either a struct, pointer to a struct or a map with strings as keys.

The tal package does not support the python: and nocall: expression types.

Path

path: provides access to properties on objects.

Syntax: [path:]string[|TALES Expression]

Description: A path, optionally starting with the modifier 'path:', references a property of an object. The '/' delimiter is used to end the name of an object and start of the property name. Properties themselves may be objects that in turn have properties. The '|' ("or") character is used to find an alternative value to a path if the first path evaluates to nil or does not exist.

Example:

<p tal:content="book/chapter/title | string:Untitled"></p>

There are several built in variables that can be used in paths:

nothing	- acts as nil in Go
default	- keeps the existing value of the node (tag content or attribute value), the same value as tal.Default
repeat	- access to repeat variables (see tal:repeat)
attrs	- a dictionary of original attributes of the current tag

Path Variables

Path variables allow for indirection in paths.

Syntax: [path:]object/?attribute

Description: The "attribute" is evaluated as a local or global variable, and it's value is used as the attribute name to lookup on the object. Useful for accessing the contents of a map within a loop:

Example:

<div tal:content="myMap/?loopValue"/>

Exists

exists: Tests whether a path exists.

Syntax: exists:path

Description: Returns true if the path exists, false otherwise. Particularly useful for eliminating entire subtrees if a particular object is not available.

Example:

<div tal:condition="exists:book">...</div>

Not

not: Returns the inverse boolean value of a path.

Syntax: not:tales-path

Description: Returns the inverse of the tales-path. If the path returns true, not:path will return false.

Example:

<p tal:condition="not: user/firstLogin">Welcome to the site!</p>

String

string: Basic string substitution

Syntax: string:text

Description: Evaluates to a string with value text while substituting variables with the form ${pathName} and $pathName

Example:

<b tal:content="string:Welcome ${user/name}!"></b>

METAL Macro Language

METAL is a macro language commonly used with TAL & TALES. METAL allows part of a template to be used as a macro in later parts of the template, or shared across templates.

Macros defined in a template can be retrieved from a compiled template and added to the context by calling the Macros() method.

Define Macro

metal:define-macro marks an element and it's subtree as being a reusable macro.

Syntax: metal:define-macro="name"

Description: Defines a new macro that can be reference later as "name".

Example:

<div metal:define-macro="footer">Copyright <span tal:content="page/lastModified">2004</span></div>

Use Macro

metal:use-macro expands a macro into the template.

Syntax: metal:use-macro="expression"

Description: Evaluates "expression" and uses this as a macro.

Example:

<div metal:use-macro="macros/footer"></div>

Define Slot

metal:define-slot creates a customisation point within a macro.

Syntax: metal:define-slot="name"

Description: Defines a customisation point in a macro with the given name.

Example:

<div metal:define-macro="footer">
	<b>Standard disclaimer for the site.</b>
	<i metal:define-slot="Contact">Contact admin@site.com</i>
</div>

Fill Slot

metal:fill-slot overwrites the contents of a slot when using a macro.

Syntax: metal:fill-slot="name"

Description: Replaces the content of a slot with this element.

Example:

<div metal:use-macro="macros/footer">
	<i metal:fill-slot="Contact">Contact someone else</i>
</div>

Notes On HTML

The tal package supports html5 output. Void elements (such as <img>) are supported and will correctly suppress end tags. Templates must have balanced start and end tags for non-void elements. Even though HTML5 elements defines several elements as supporting optional end tags, for tal templates end tags must be provided.

Index

Examples

Constants

This section is empty.

Variables

View Source
var Default interface{} = struct{ Name string }{"Default"}

Default is a special value used by TAL to indicate that the default template content should be used in tal:content, etc.

Use the top level TALES variable "default" for the default value in a path.

For None use the path "nothing" and in Go use nil.

Functions

This section is empty.

Types

type CompileError

type CompileError struct {
	// LastToken is the last parsed HTML token seen.
	LastToken string
	// NextData contains some of the unparsed data that happens after the error.
	NextData string
	// ErrorType specifies the kind of compilation error that has occured.
	ErrorType CompileErrorKind
}

CompileErrors are returned from CompileTemplate for compilation errors.

func (*CompileError) Error

func (err *CompileError) Error() string

Error returns a text description of the compilation error.

type CompileErrorKind

type CompileErrorKind int

CompileErrorKind indicates the kind of error encountered while compiling

const (
	// ErrUnexpectedCloseTag is if a close tag is encountered for which an open tag was not seen.
	ErrUnexpectedCloseTag CompileErrorKind = iota
	// ErrUnknownTalCommand is if a tal: or metal: command is not one of the supported commands.
	ErrUnknownTalCommand
	// ErrExpressionMalformed is for expressions that don't match the tal command they are on.
	ErrExpressionMalformed
	// ErrExpressionMissing is if an expression is missing where one is expected.
	ErrExpressionMissing
	// ErrSlotOutsideMacro is if a metal:fill-slot is outside of a use-macro.
	ErrSlotOutsideMacro
)

type RenderConfig

type RenderConfig func(t *Template, rc *renderContext)

A RenderConfig function is one that can be passed as an option to Render.

func RenderDebugLogging

func RenderDebugLogging(logger func(fmt string, args ...interface{})) RenderConfig

RenderDebugLogging uses the given logger for debug output when rendering the template.

To use the standard log library pass RenderDebugLogging(log.Printf) to the Render method.

type TalesValue

type TalesValue interface {
	/*
		TalesValue handles all property lookups on the object.

		All fields & methods that would normally be automatically found by tal will
		instead be passed to TalesValue for handling.

		If the property is not supported by this object, nil should be returned.
	*/
	TalesValue(property string) (result interface{})
}

TalesValue provide a method allowing properties to be resolved to a value.

TalesValue can be used to provide custom lookups for resolving the last entry in a path.

Example
package main

import (
	"os"
	"strings"
)

type Person struct {
	name string
}

func (p *Person) TalesValue(property string) interface{} {
	switch property {
	case "Name":
		return p.name
	case "upper":
		return strings.ToUpper(p.name)
	case "lower":
		return strings.ToLower(p.name)
	}
	return nil
}

func main() {

	vals := make(map[string]interface{})
	vals["person"] = &Person{"Alice"}

	tmpl, _ := CompileTemplate(strings.NewReader(`<b tal:content="person/Name"></b> and <b tal:content="person/upper"></b> and <b tal:content="person/lower"></b>`))
	tmpl.Render(vals, os.Stdout)
}
Output:

<b>Alice</b> and <b>ALICE</b> and <b>alice</b>

type Template

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

Template holds the compiled version of a TAL template.

Once the Template has been compiled it is immutable and can be used by multiple goroutines simultaneously.

func CompileTemplate

func CompileTemplate(in io.Reader) (template *Template, err error)

CompileTemplate reads the template in and compiles it ready for execution.

If a compilation error (rather than IO error) occurs, the returned error will be a CompileError object.

The io.Reader must provide a stream of UTF-8 encoded text. Templates render into UTF-8, so any conversion to or from other character sets must be carried out in the io.Reader and io.Writer used.

func (*Template) Render

func (t *Template) Render(context interface{}, out io.Writer, config ...RenderConfig) error

Render a template contents with the given context to the io.Writer.

The Context object should be either a struct or a map with string keys. Resolution of TAL paths is done in the following order:

1 - nothing and default: built-in variables
2 - attrs: access to the original attributes on the element
3 - repeat: access to the repeat variables
4 - local variables
5 - global variables
6 - User provided context

When looking for a property on local, global and user provided context the following lookup rules are followed:

1 - If a map, look for a value with the property name as the key.
2 - If a struct or pointer to a struct:
	a) Look for an exported field with this name
	b) Look for a Pointer Method with this name and call it
	c) Look for a Value Method with this name and call it

If a value found in either a map or struct field is a function, it will be called. Functions and methods are called with no arguments and the first value returned is used as the resulting value.

A RenderConfig option can be provided to set debug logging.

Example
vals := make(map[string]interface{})
vals["colours"] = []string{"Red", "Green", "Blue"}
vals["name"] = "Alice"
vals["age"] = 21

tmpl, _ := CompileTemplate(strings.NewReader(`
		<html>
			<body>
				<h1 tal:content="name">Name Here</h1>
				<p tal:content="string: Age: ${age}">Age</p>
				<ul>
					<li tal:repeat="colour colours" tal:content="colour">Colours</li>
				</ul>
			</body>
		</html>`))
tmpl.Render(vals, os.Stdout)
/*
	Output:
	<html>
		<body>
			<h1>Alice</h1>
			<p>Age: 21</p>
			<ul>
				<li>Red</li><li>Green</li><li>Blue</li>
			</ul>
		</body>
	</html>
*/
Output:

		<html>
			<body>
				<h1>Alice</h1>
				<p>Age: 21</p>
				<ul>
					<li>Red</li><li>Green</li><li>Blue</li>
				</ul>
			</body>
		</html>

func (*Template) String

func (t *Template) String() string

String creates a full textual description of the compiled template.

func (*Template) TalesValue

func (t *Template) TalesValue(name string) interface{}

Templates are a TalesValue that provides its macros as properties.

A Templates own macros are made available to it under the "macros" object.

Example
vals := make(map[string]interface{})
macroTemplate, _ := CompileTemplate(strings.NewReader(`<html><body><h2 metal:define-macro="author">Author Name</h2></body></html>`))
vals["sharedmacros"] = macroTemplate

tmpl, _ := CompileTemplate(strings.NewReader(`
		<html><body>
		<p metal:define-macro="boiler">Boiler Plate Message</p>
		<h2 metal:use-macro="sharedmacros/author"></h2>
		<p metal:use-macro="macros/boiler"></p>
		</body></html>`))
tmpl.Render(vals, os.Stdout)
/*
	Output:
	<html><body>
	<p>Boiler Plate Message</p>
	<h2>Author Name</h2>
	<p>Boiler Plate Message</p>
	</body></html>
*/
Output:

		<html><body>
		<p>Boiler Plate Message</p>
		<h2>Author Name</h2>
		<p>Boiler Plate Message</p>
		</body></html>

Jump to

Keyboard shortcuts

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