diff --git a/README.md b/README.md index f61774e..acbe5ee 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,13 @@ cd expressions ragel -Z scanner.rl && go test ``` +Preview the documentation: + +```bash +godoc -http=:6060& +open http://localhost:6060/pkg/github.com/osteele/liquid/ +``` + ## References diff --git a/chunks/chunk.go b/chunks/chunk.go index 65b334f..9f2667a 100644 --- a/chunks/chunk.go +++ b/chunks/chunk.go @@ -1,3 +1,7 @@ +// Package chunks parses and evaluates template strings. +// +// A template string is composed of a sequence of chunk. +// Each chunk is either an object {{a.b}}, a tag {%if a>b%}, or a text chunk (anything outside of {{}} and {%%}.) package chunks import "fmt" diff --git a/engine.go b/engine.go index 00c0ef1..7b64051 100644 --- a/engine.go +++ b/engine.go @@ -1,7 +1,5 @@ /* -Package liquid is a pure Go implementation of Shopify Liquid templates. - -It's intended for use in for use in https://github.com/osteele/gojekyll. +Package liquid is a pure Go implementation of Shopify Liquid templates, for use in https://github.com/osteele/gojekyll. See the project README https://github.com/osteele/liquid for additional information and implementation status. */ @@ -23,16 +21,38 @@ func init() { } // Engine parses template source into renderable text. +// +// An engine can be configured with additional filters and tags. +// +// Filters +// +// DefineFilter defines a Liquid filter. +// +// A filter is any function that takes at least one input, and returns one or two outputs. +// If it returns two outputs, the second must be an error. type Engine interface { + // DefineFilter defines a filter function e.g. {{ value | filter: arg }}. + // + // Note: Although this function is defined on the engine, its effect is currently global. DefineFilter(name string, fn interface{}) - DefineTag(string, func(form string) (func(io.Writer, chunks.Context) error, error)) + // DefineTag defines a tag function e.g. {% tag %}. + // + // Note: Although this function is defined on the engine, its effect is currently global. + DefineTag(string, TagDefinition) - ParseTemplate(text []byte) (Template, error) - ParseAndRender(text []byte, bindings map[string]interface{}) ([]byte, error) - ParseAndRenderString(text string, bindings map[string]interface{}) (string, error) + ParseTemplate(b []byte) (Template, error) + // ParseAndRender parses and then renders the template. + ParseAndRender(b []byte, bindings map[string]interface{}) ([]byte, error) + // ParseAndRenderString is a convenience wrapper for ParseAndRender, that has string input and output. + ParseAndRenderString(s string, bindings map[string]interface{}) (string, error) } -type TagDefinition func(expr string) (func(io.Writer, chunks.Context) error, error) +// Renderer is the type of a function that is evaluated within a context and writes to output. +type Renderer func(io.Writer, chunks.Context) error + +// TagDefinition is the type of a function that parses the argument string "args" from a tag "{% tagname args %}", +// and returns a renderer. +type TagDefinition func(parameters string) (func(io.Writer, chunks.Context) error, error) type engine struct{} @@ -40,35 +60,24 @@ type template struct { ast chunks.ASTNode } -// NewEngine returns a new engine. +// NewEngine returns a new template engine. func NewEngine() Engine { return engine{} } -// DefineFilter defines a Liquid filter. -// -// A filter is any function that takes at least one input, and returns one or two outputs. -// If it returns two outputs, the second must be an error. -// -// Note: Although this function is defined on the engine, its effect is currently global. +// DefineFilter is in the Engine interface. func (e engine) DefineFilter(name string, fn interface{}) { // TODO define this on the engine, not globally expressions.DefineFilter(name, fn) } -// DefineTag defines a Liquid filter. -// -// A tag is any function that takes at least one input, and returns one or two outputs. -// If it returns two outputs, the second must be an error. -// -// Note: This interface is likely to change. -// -// Note: Although this function is defined on the engine, its effect is currently global. -func (e engine) DefineTag(name string, td func(form string) (func(io.Writer, chunks.Context) error, error)) { +// ParseAndRenderString is in the Engine interface. +func (e engine) DefineTag(name string, td TagDefinition) { // TODO define this on the engine, not globally chunks.DefineTag(name, chunks.TagDefinition(td)) } +// ParseTemplate is in the Engine interface. func (e engine) ParseTemplate(text []byte) (Template, error) { tokens := chunks.Scan(string(text), "") ast, err := chunks.Parse(tokens) @@ -78,7 +87,7 @@ func (e engine) ParseTemplate(text []byte) (Template, error) { return &template{ast}, nil } -// ParseAndRender parses and then renders the template. +// ParseAndRender is in the Engine interface. func (e engine) ParseAndRender(text []byte, bindings map[string]interface{}) ([]byte, error) { t, err := e.ParseTemplate(text) if err != nil { @@ -87,7 +96,7 @@ func (e engine) ParseAndRender(text []byte, bindings map[string]interface{}) ([] return t.Render(bindings) } -// ParseAndRenderString is a convenience wrapper for ParseAndRender, that has string input and output. +// ParseAndRenderString is in the Engine interface. func (e engine) ParseAndRenderString(text string, bindings map[string]interface{}) (string, error) { b, err := e.ParseAndRender([]byte(text), bindings) if err != nil { diff --git a/expressions/expressions.go b/expressions/expressions.go index 55835f0..92db05f 100644 --- a/expressions/expressions.go +++ b/expressions/expressions.go @@ -1,3 +1,4 @@ +// Package expressions parses and evaluates the expression language that is used in objects {{a.b[c]}} and tag parameters {%assign pages = site.pages | reverse%}. package expressions import ( diff --git a/expressions/scanner.rl b/expressions/scanner.rl index 6c4d1e7..084777c 100644 --- a/expressions/scanner.rl +++ b/expressions/scanner.rl @@ -1,5 +1,5 @@ -// Adapted from https://github.com/mhamrah/thermostat package expressions +// Adapted from https://github.com/mhamrah/thermostat import "fmt" import "strconv" diff --git a/generics/generics.go b/generics/generics.go index 5be0749..8fa29d6 100644 --- a/generics/generics.go +++ b/generics/generics.go @@ -1,3 +1,10 @@ +// Package generics defines methods such as sorting, comparison, and type conversion, that apply to interface types. +// +// It is similar to, and makes heavy use of, the reflect package. +// +// Since the intent is to provide runtime services for the Liquid expression interpreter, +// this package does not implement "generic" generics. +// It attempts to implement Liquid semantics (which are largely Ruby semantics). package generics import ( diff --git a/liquid_test.go b/liquid_test.go index c29ef94..55bd347 100644 --- a/liquid_test.go +++ b/liquid_test.go @@ -40,12 +40,12 @@ func TestChunkParser(t *testing.T) { func Example() { engine := NewEngine() template := `

{{page.title}}

` - scope := map[string]interface{}{ + bindings := map[string]interface{}{ "page": map[string]interface{}{ "title": "Introduction", }, } - out, err := engine.ParseAndRenderString(template, scope) + out, err := engine.ParseAndRenderString(template, bindings) if err != nil { log.Fatalln(err) }