liquid

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2017 License: MIT Imports: 8 Imported by: 0

README

Go Liquid Template Parser

“Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.” – Philip Greenspun

liquid ports Shopify Liquid templates to Go. It was developed for use in gojekyll.

Differences from Liquid

Refer to the feature parity board for a list of differences from Liquid.

In brief, these aren't implemented:

  • The cycle and tablerow tags
  • {% case %}…{% else %} and {% when a or b %}
  • The escape, sort_natural, truncatewords, url_decode, and url_encode filters
  • Loop ranges {% for a in 1...10 %}
  • Error modes
  • Whitespace control

Stability Guarantees

This library is at an early stage of development. It has been mostly used by its author.

Until it reaches 1.0, breaking changes will accompanied by a bump in the minor version, not the major version. For example, use go get gopkg.in/osteele/liquid.v0.2 to stay at versions that are compatible with the v0.2 API. The v0.3 release will not in general be compatible with version 0.2.

Even within these parameters, only the liquid package itself, and the sub-package APIs that it documents, are guaranteed stable. For example, render.Context is documented as the parameter type for tag definitions; it therefore has the same stability guarantees as liquid.Engine and liquid.Template. Other "public" definitions in render and other sub-packages are public only to the implementation of packages in the repo; they are not generally stable.

Install

go get gopkg.in/osteele/liquid.v0.2-- latest snapshot

go get -u github.com/osteele/goliquid -- development version

Usage

engine := NewEngine()
template := `<h1>{{ page.title }}</h1>`
bindings := map[string]interface{}{
    "page": map[string]string{
        "title": "Introduction",
    },
}
out, err := engine.ParseAndRenderString(template, bindings)
if err != nil { log.Fatalln(err) }
fmt.Println(out)
// Output: <h1>Introduction</h1>
Command-Line tool

go install gopkg.in/osteele/liquid.v0/cmd/liquid installs a command-line liquid program in your GO bin. This is intended to make it easier to create test cases for bug reports.

$ liquid --help
usage: liquid [FILE]
$ echo '{{ "Hello World" | downcase | split: " " | first | append: "!"}}' | liquid
hello!

Contributing

Bug reports, test cases, and code contributions are more than welcome. Please refer to the contribution guidelines.

References

Attribution

Package Author Description License
gopkg.in/yaml.v2 Canonical YAML support (for printing parse trees) Apache License 2.0
jeffjen/datefmt Jeffrey Jen Go bindings to GNU strftime and strptime MIT
Ragel Adrian Thurston scanning expressions MIT

Michael Hamrah's Lexing with Ragel and Parsing with Yacc using Go was essential to understanding go yacc.

The original Liquid engine, of course, for the design and documentation of the Liquid template language. Many of the tag and filter test cases are taken directly from the Liquid documentation.

Other Implementations

Go
Other Languages

See Shopify's ports of Liquid to other environments.

License

MIT License

Documentation

Overview

Package liquid is a pure Go implementation of Shopify Liquid templates, developed for use in https://github.com/osteele/gojekyll.

See the project README https://github.com/osteele/liquid for additional information and implementation status.

The liquid package itself is versioned in gopkg.in. Subpackages have no compatibility guarantees. Except where specifically documented, the “public” entities of subpackages are intended only for use by the liquid package and its subpackages.

Example
engine := NewEngine()
source := `<h1>{{ page.title }}</h1>`
bindings := map[string]interface{}{
	"page": map[string]string{
		"title": "Introduction",
	},
}
out, err := engine.ParseAndRenderString(source, bindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

<h1>Introduction</h1>

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsTemplateError

func IsTemplateError(err error) bool

IsTemplateError returns true iff the error represents an error in the template syntax or execution. It is used to distinguish errors in input values from errors in the Liquid implemtation, or the implementation of tags and filters, themselves.

Use this function to avoid coding the specific types of subpackage errors, which are likely to change.

Types

type Bindings

type Bindings map[string]interface{}

Bindings is a map of variable names to values.

Clients need not use this type. It is used solely for documentation. Callers can use unconverted instances of map[interface] itself as argument values to functions declared with this parameter type.

type Drop

type Drop interface {
	ToLiquid() interface{}
}

Drop indicates that the object will present to templates as its ToLiquid value.

Example
// type redConvertible struct{}
//
// func (c redConvertible) ToLiquid() interface{} {
// 	return "red"
// }
engine := NewEngine()
bindings := map[string]interface{}{
	"drop": redConvertible{},
}
template := `{{ drop }}`
out, err := engine.ParseAndRenderString(template, bindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

red

type Engine

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

An Engine parses template source into renderable text.

An engine can be configured with additional filters and tags.

func NewEngine

func NewEngine() *Engine

NewEngine returns a new Engine.

func (*Engine) ParseAndRender

func (e *Engine) ParseAndRender(source []byte, b Bindings) ([]byte, SourceError)

ParseAndRender parses and then renders the template.

func (*Engine) ParseAndRenderString

func (e *Engine) ParseAndRenderString(source string, b Bindings) (string, SourceError)

ParseAndRenderString is a convenience wrapper for ParseAndRender, that takes string input and returns a string.

Example
engine := NewEngine()
source := `{{ hello | capitalize | append: " Mundo" }}`
bindings := map[string]interface{}{"hello": "hola"}
out, err := engine.ParseAndRenderString(source, bindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

Hola Mundo

func (*Engine) ParseTemplate

func (e *Engine) ParseTemplate(source []byte) (*Template, SourceError)

ParseTemplate creates a new Template using the engine configuration.

Example
engine := NewEngine()
source := `{{ hello | capitalize | append: " Mundo" }}`
bindings := map[string]interface{}{"hello": "hola"}
tpl, err := engine.ParseTemplate([]byte(source))
if err != nil {
	log.Fatalln(err)
}
out, err := tpl.RenderString(bindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

Hola Mundo

func (*Engine) RegisterBlock

func (e *Engine) RegisterBlock(name string, td Renderer)

RegisterBlock defines a block e.g. {% tag %}…{% endtag %}.

Example
engine := NewEngine()
engine.RegisterBlock("length", func(c render.Context) (string, error) {
	s, err := c.InnerString()
	if err != nil {
		return "", err
	}
	n := len(s)
	return fmt.Sprint(n), nil
})

template := `{% length %}abc{% endlength %}`
out, err := engine.ParseAndRenderString(template, emptyBindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

3

func (*Engine) RegisterFilter

func (e *Engine) RegisterFilter(name string, fn interface{})

RegisterFilter defines a Liquid filter, for use as `{{ value | my_filter }}` or `{{ value | my_filter: arg }}`.

A filter is a function that takes at least one input, and returns one or two outputs. If it returns two outputs, the second must have type error.

Examples:

* https://github.com/osteele/liquid/blob/master/filters/filters.go

* https://github.com/osteele/gojekyll/blob/master/filters/filters.go

Example
engine := NewEngine()
engine.RegisterFilter("has_prefix", strings.HasPrefix)
template := `{{ title | has_prefix: "Intro" }}`
bindings := map[string]interface{}{
	"title": "Introduction",
}
out, err := engine.ParseAndRenderString(template, bindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

true
Example (Optional_argument)
engine := NewEngine()
// func(a, b int) int) would default the second argument to zero.
// Then we can't tell the difference between {{ n | inc }} and
// {{ n | inc: 0 }}. A function in the parameter list has a special
// meaning as a default parameter.
engine.RegisterFilter("inc", func(a int, b func(int) int) int {
	return a + b(1)
})
template := `10 + 1 = {{ m | inc }}; 20 + 5 = {{ n | inc: 5 }}`
bindings := map[string]interface{}{
	"m": 10,
	"n": "20",
}
out, err := engine.ParseAndRenderString(template, bindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

10 + 1 = 11; 20 + 5 = 25

func (*Engine) RegisterTag

func (e *Engine) RegisterTag(name string, td Renderer)

RegisterTag defines a tag e.g. {% tag %}.

RegisterTag defines a tag, for use as `{% tag args %}`.

Examples:

* https://github.com/osteele/gojekyll/blob/master/tags/tags.go

Example
engine := NewEngine()
engine.RegisterTag("echo", func(c render.Context) (string, error) {
	return c.TagArgs(), nil
})
template := `{% echo hello world %}`
out, err := engine.ParseAndRenderString(template, emptyBindings)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(out)
Output:

hello world

type Renderer

type Renderer func(render.Context) (string, error)

A Renderer returns the rendered string for a block.

type SourceError added in v0.2.0

type SourceError interface {
	error
	Cause() error
	Filename() string
	LineNumber() int
}

SourceError records an error with a source location and optional cause.

type Template

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

A Template is a compiled Liquid template. It knows how to evaluate itself within a variable binding environment, to create a rendered byte slice.

Use Engine.ParseTemplate to create a template.

func (*Template) Render

func (t *Template) Render(vars Bindings) ([]byte, SourceError)

Render executes the template with the specified variable bindings.

func (*Template) RenderString

func (t *Template) RenderString(b Bindings) (string, SourceError)

RenderString is a convenience wrapper for Render, that has string input and output.

func (*Template) SetSourceLocation

func (t *Template) SetSourceLocation(filename string, lineNo int)

SetSourceLocation sets the source path as SetSourcePath, and also the line number of the first line of the template text, for use in error reporting.

func (*Template) SetSourcePath

func (t *Template) SetSourcePath(filename string)

SetSourcePath sets the filename. This is used for error reporting, and as the reference path for relative pathnames in the {% include %} tag.

Directories

Path Synopsis
cmd
liquid
Package main defines a command-line interface to the Liquid engine.
Package main defines a command-line interface to the Liquid engine.
Package evaluator defines methods such as sorting, comparison, and type conversion, that apply to interface types.
Package evaluator defines methods such as sorting, comparison, and type conversion, that apply to interface types.
Package expression parses and evaluates the expression language.
Package expression parses and evaluates the expression language.
Package filters defines the standard Liquid filters.
Package filters defines the standard Liquid filters.
Package parser parses template source into an abstract syntax tree.
Package parser parses template source into an abstract syntax tree.
Package render renders a template parse tree.
Package render renders a template parse tree.
Package tags defines the standard Liquid tags.
Package tags defines the standard Liquid tags.

Jump to

Keyboard shortcuts

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