gerb

package module
v0.0.0-...-a6088cc Latest Latest
Warning

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

Go to latest
Published: Feb 9, 2015 License: MIT Imports: 12 Imported by: 3

README

Gerb

Gerb is a erb-inspired templating engine for Go.

Usage

template, err := gerb.ParseString(true, "....")
if err != nil {
  panic(err)
}
data := map[string]interface{}{
  "name": ....
}
template.Render(os.Stdout, data)

There are three available methods for creating a template:

  1. Parse(cache bool, data [][]byte)
  2. ParseString(cache bool, data []string)
  3. ParseFile(cache bool, paths []string)

Unless cache is set to false, an internal cache is used to avoid having to parse the same content (based on the content's hash). The cache will automatically evict old items.

Once you have a template, you use the Render method to output the template to the specified io.Writer using the specified data. Data must be a map[string]interface{} or nil in the rare case where you have no template data.

It's safe to call Render from multiple threads.

Output Tags

Gerb supports two types of output tags: escaped and non-escaped. The only difference is that escaped tags will have < and > characters HTML-escaped.

<%= "<script>alert('this will be escaped')</script>" %>
<%! "<script>alert('this won't be escaped')</script>" %>

Variables

Gerb attempts to behave as close to Go as possible. The biggest difference is that method calls and field access is case-insensitive. Gerb supports:

  • ints
  • float64
  • strings
  • byte
  • fields
  • methods
  • arrays
  • basic operations

For example, the following works:

<%= user.Analysis(count)[start+7:] %>

+, -, /, * and % are the only support operations. Currently (and sadly) order of precedence is left to right and parenthesis cannot be used (parenthesis can be used in if/elseif statements).

Gerb might occasionally be a little less strict than Go with type conversions, but not by much.

Builtins

Go's builtins aren't natively available. However, custom builtin functions can be registered. The custom buitlin len comes pre-registered and behaves much like the real len builtin. You can register your own builtin:

import "github.com/karlseguin/gerb/core"
func init() {
  RegisterBuiltin("add", func(a, b int) int {
    return a + b
  })
}

RegisterBuiltin isn't threadsafe. It's expected that you'll register your builtins at startup and then leave it alone.

Aliases and Package Functions

In addition to custom builtins, it is possible to alias package functions. This makes functions such as strings.ToUpper available to use.

By default, many functions from the strings package in addition to fmt.Sprintf and strconv.Atoi are available.

Since Go doesn't make it possible to automatically reflect functions exposed from a package, registration must manually be done on a per-method basis. Like builtins, this process is not thread safe:

func init() {
  core.RegisterAliases("strings",
    "ToUpper", strings.ToUpper,
    "ToLower", strings.ToLower,
    ...
  )
  core.RegisterAliases("fmt",
    "Sprintf", fmt.Sprintf
  )
}

You can see a list of what's currently aliased by looking at https://github.com/karlseguin/gerb/blob/master/core/aliases.go

Multiple Return Values

If you call a function which returns multiple values, only the first value is considered/returned. The exception to this rule is with any assignment.

Assignments

It's possible to create/assign to new or existing variables within a template:

<% name := "leto" %>

Assignment supports multiple values, either via an explicit list of values or a function which returns multiple values:

<% name, power := "goku", 9000 %>
<% name, power := sayan.Stats() %>

In fact, you can (but probably shouldn't) mix the two:

<% name, power := sayan.Name(), 9000 %>

This is also true for assignments within an if or else if tag:

<% if n, err := strconv.Atoi(value); err != nil { %>
  The number is <%= n %>
<% } %>

if/elseif/else

If tag closely mimic Go's if statements. Assignments within an if/elseif is allowed and braces are mandatory.

for

For tag supports ranged iteration (over slices, arrays, maps and strings) as well as the traditional C-style (including an empty for for { ... }).

The continue and break tag work as expected within a for loop.

++, --, += and -=

There's limited support for these four operators. As a general rule, they should only be used on simple values (support was added to support the i++ in a for loop).

Here's a couple examples of what is not supported:

<% user.PowerLevel++ %>
<% ranks[4]++ %>

Put differently, these 4 operators should only ever be used as such:

<% counter++ %>

Newlines

Use <%% to trim newlines from literals before a code block. Use %%> to trim newlines from a literal following a codeblock.

Comments

<% can be used for comments within templates:

<%# my comment %>

Newlines can be trimmed around comments using <%%# and %%>

Errors and Logs

Render should never fail. By default, Render will log errors to stdout. This behavior can be changed. To disable all logging:

gerb.Configure().Logger(nil)

Alternatively, to use your own logger:

gerb.Configure().Logger(new(MyLogger))

Your logger must implement Error(v ...interface{}).

Template Inheritance

The Parse, ParseString and ParseFile methods accept a variable length of parameters. The purpose of this is to support template inheritance:

t := gerb.ParseFile(true, "update.gerb", "member.gerb", "main.gerb")

Templates should be specified from innermost to outermost.

The <% capture NAME {%>...<% } %> and builtin yield can be used to manage content:

layout := `<title><%= yield("title")%></title> <%= yield %> <footer>...</footer>`
about := `<% content "title" { %>about us<% } %> We are ...`
t, _ := gerb.ParseString(true, about, layout)

Configuration

Gerb can be configured via the fluent interface exposed by gerb.Configure().

  • Logger: specify the logger to use when an error is encountered rendering (defaults to stdout)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Cache = ccache.New(ccache.Configure().MaxSize(5000))
View Source
var CodeFactories = map[string]CodeFactory{
	"if":       IfFactory,
	"content":  ContentFactory,
	"for":      ForFactory,
	"break":    BreakFactory,
	"continue": ContinueFactory,
}

Functions

func BreakFactory

func BreakFactory(p *core.Parser) (core.Code, error)

func ContentFactory

func ContentFactory(p *core.Parser) (core.Code, error)

func ContinueFactory

func ContinueFactory(p *core.Parser) (core.Code, error)

func ElseFactory

func ElseFactory(p *core.Parser) (core.Code, error)

func ExplicitForFactory

func ExplicitForFactory(p *core.Parser) (core.Code, error)

func ForFactory

func ForFactory(p *core.Parser) (core.Code, error)

func IfFactory

func IfFactory(p *core.Parser) (core.Code, error)

func RangedForFactory

func RangedForFactory(p *core.Parser) (core.Code, error)

Types

type CodeFactory

type CodeFactory func(*core.Parser) (core.Code, error)

type Configuration

type Configuration struct{}

func Configure

func Configure() *Configuration

func (*Configuration) Logger

func (c *Configuration) Logger(logger core.Logger)

type ContentCode

type ContentCode struct {
	*core.NormalContainer
	// contains filtered or unexported fields
}

func (*ContentCode) AddCode

func (c *ContentCode) AddCode(core.Code) error

func (*ContentCode) Execute

func (c *ContentCode) Execute(context *core.Context) core.ExecutionState

func (*ContentCode) IsCodeContainer

func (c *ContentCode) IsCodeContainer() bool

func (*ContentCode) IsContentContainer

func (c *ContentCode) IsContentContainer() bool

func (*ContentCode) IsSibling

func (c *ContentCode) IsSibling() bool

type ControlFlowCode

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

func (*ControlFlowCode) AddCode

func (c *ControlFlowCode) AddCode(code core.Code) error

func (*ControlFlowCode) AddExecutable

func (c *ControlFlowCode) AddExecutable(core.Executable)

func (*ControlFlowCode) Execute

func (c *ControlFlowCode) Execute(context *core.Context) core.ExecutionState

func (*ControlFlowCode) IsCodeContainer

func (c *ControlFlowCode) IsCodeContainer() bool

func (*ControlFlowCode) IsContentContainer

func (c *ControlFlowCode) IsContentContainer() bool

func (*ControlFlowCode) IsSibling

func (c *ControlFlowCode) IsSibling() bool

type ElseCode

type ElseCode struct {
	*core.NormalContainer
	// contains filtered or unexported fields
}

func (*ElseCode) AddCode

func (c *ElseCode) AddCode(core.Code) error

func (*ElseCode) IsCodeContainer

func (c *ElseCode) IsCodeContainer() bool

func (*ElseCode) IsContentContainer

func (c *ElseCode) IsContentContainer() bool

func (*ElseCode) IsSibling

func (c *ElseCode) IsSibling() bool

type EndScope

type EndScope struct{}

func (*EndScope) AddCode

func (c *EndScope) AddCode(core.Code) error

func (*EndScope) AddExecutable

func (c *EndScope) AddExecutable(e core.Executable)

func (*EndScope) Execute

func (c *EndScope) Execute(context *core.Context) core.ExecutionState

func (*EndScope) IsCodeContainer

func (c *EndScope) IsCodeContainer() bool

func (*EndScope) IsContentContainer

func (c *EndScope) IsContentContainer() bool

func (*EndScope) IsSibling

func (c *EndScope) IsSibling() bool

type ForCode

type ForCode struct {
	*core.NormalContainer
	// contains filtered or unexported fields
}

func (*ForCode) AddCode

func (c *ForCode) AddCode(code core.Code) error

func (*ForCode) Execute

func (c *ForCode) Execute(context *core.Context) core.ExecutionState

func (*ForCode) IsCodeContainer

func (c *ForCode) IsCodeContainer() bool

func (*ForCode) IsContentContainer

func (c *ForCode) IsContentContainer() bool

func (*ForCode) IsSibling

func (c *ForCode) IsSibling() bool

type IfCode

type IfCode struct {
	*core.NormalContainer
	// contains filtered or unexported fields
}

func (*IfCode) AddCode

func (c *IfCode) AddCode(code core.Code) error

func (*IfCode) Execute

func (c *IfCode) Execute(context *core.Context) core.ExecutionState

func (*IfCode) IsCodeContainer

func (c *IfCode) IsCodeContainer() bool

func (*IfCode) IsContentContainer

func (c *IfCode) IsContentContainer() bool

func (*IfCode) IsSibling

func (c *IfCode) IsSibling() bool

type OutputTag

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

func (*OutputTag) Execute

func (o *OutputTag) Execute(context *core.Context) core.ExecutionState

type RangedForCode

type RangedForCode struct {
	*core.NormalContainer
	// contains filtered or unexported fields
}

func (*RangedForCode) AddCode

func (c *RangedForCode) AddCode(code core.Code) error

func (*RangedForCode) Execute

func (c *RangedForCode) Execute(context *core.Context) core.ExecutionState

func (*RangedForCode) IsCodeContainer

func (c *RangedForCode) IsCodeContainer() bool

func (*RangedForCode) IsContentContainer

func (c *RangedForCode) IsContentContainer() bool

func (*RangedForCode) IsSibling

func (c *RangedForCode) IsSibling() bool

type Template

type Template struct {
	*core.NormalContainer
}

func (*Template) AddCode

func (t *Template) AddCode(core.Code) error

func (*Template) Close

func (t *Template) Close(*core.Context) error

func (*Template) IsCodeContainer

func (t *Template) IsCodeContainer() bool

func (*Template) IsContentContainer

func (t *Template) IsContentContainer() bool

func (*Template) IsSibling

func (t *Template) IsSibling() bool

type TemplateChain

type TemplateChain []core.Executable

a chain of templates to render from inner-most to outer-most

func Parse

func Parse(cache bool, data ...[]byte) (TemplateChain, error)

Parse the bytes into a gerb template

func ParseFile

func ParseFile(cache bool, paths ...string) (TemplateChain, error)

Turn the contents of the specified file into a gerb template

func ParseString

func ParseString(cache bool, data ...string) (TemplateChain, error)

Parse the string into a erb template

func (TemplateChain) Render

func (t TemplateChain) Render(writer io.Writer, data map[string]interface{})

Directories

Path Synopsis
r

Jump to

Keyboard shortcuts

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