mario

package module
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2020 License: MIT Imports: 11 Imported by: 3

README

Mario

Build Status GoDoc

Continues work of raymond to support Handlebars for golang

Handlebars

Handlebars is a superset of mustache but it differs on those points:

  • Alternative delimiters are not supported
  • There is no recursive lookup

Install

go get -u github.com/imantung/mario

Usage

Mario's function implementation mimic go template for convenience.

source := `Hello {{name}}`

ctx := map[string]string{
  "name": "World",
}

tpl, err := mario.New().Parse(source)
if err != nil {
  panic(err)
}

var b strings.Builder
if err := tpl.Execute(&b, ctx); err != nil {
  panic(err)
}

fmt.Println(b.String())

// Output: 
// Hello World

For shortcut, using Must to create template object without error

var t = mario.Must(mario.New().Parse(source))

Rendering Context

The rendering context can contain any type of values, including array, slice, map, struct and func.

Map
source := `{ "name": "{{name}}", "age": {{age}} }`
data := map[string]interface{}{
	"name": "Mario",
	"age":  12,
}
Struct
source := `<div class="post">
  <h1>By {{author.firstName}} {{author.lastName}}</h1>
  <div class="body">{{body}}</div>

  <h1>Comments</h1>

  {{#each comments}}
  <h2>By {{author.firstName}} {{author.lastName}}</h2>
  <div class="body">{{content}}</div>
  {{/each}}
</div>`

ctx := Post{
  Person{"Jean", "Valjean"},
  "Life is difficult",
  []Comment{
    Comment{
      Person{"Marcel", "Beliveau"},
      "LOL!",
    },
  },
}

Note:

  • When using structs, be warned that only exported fields are accessible.

    type Person struct {
      FirstName  string
      LastName   string
      motherName string // NOTE: Unexported and can't be access
    }
    
  • However you can access exported fields in template with their lowercase names. For example, both {{author.firstName}} and {{Author.FirstName}} references give the same result, as long as Author and FirstName are exported struct fields.

  • More, you can use the handlebars struct tag to specify a template variable name different from the struct field name.

Function

Randomly renders I hate you or I love you.

source := "I {{feeling}} you"

ctx := map[string]interface{}{
    "feeling": func() string {
        rand.Seed(time.Now().UTC().UnixNano())

        feelings := []string{"hate", "love"}
        return feelings[rand.Intn(len(feelings))]
    },
}

Note:

  • Those context functions behave like helper functions: they can be called with parameters and they can have an Options argument.

Helpers

Handlebarjs built-in helpers:

  • if to conditionally render a block.

    <div class="entry">
      {{#if author}}
        <h1>{{firstName}} {{lastName}}</h1>
      {{/if}}
    </div>
    
  • unless inverse of the if helper.

    <div class="entry">
      {{#unless license}}
      <h3 class="warning">WARNING: This entry does not have a license!</h3>
      {{/unless}}
    </div>
    
  • each: iterate over an array, a slice, a map or a struct instance using this built-in each helper. Inside the block, you can use this to reference the element being iterated over.

    <ul class="people">
      {{#each people}}
        <li>{{this}}</li>
      {{/each}}
    </ul>
    
  • with: shift the context for a section of a template by using the built-in with block helper.

    <div class="entry">
      <h1>{{title}}</h1>
    
      {{#with author}}
      <h2>By {{firstName}} {{lastName}}</h2>
      {{/with}}
    </div>
    
  • lookup: allows for dynamic parameter resolution using handlebars variables.

    {{#each bar}}
      {{lookup ../foo @index}}
    {{/each}}
    
  • log: allows for logging while rendering a template.

    {{log "Look at me!"}}
    

Additional helper:

  • equal: renders a block if the string version of both arguments are equals.

    {{#equal foo "bar"}}foo is bar{{/equal}}
    {{#equal foo baz}}foo is the same as baz{{/equal}}
    {{#equal nb 0}}nothing{{/equal}}
    {{#equal nb 1}}there is one{{/equal}}
    {{#equal nb "1"}}everything is stringified before comparison{{/equal}}
    

Custom Helper

TODO: Implementation of custom helper

Language Features

TODO: https://handlebarsjs.com/guide/#language-features

Limitations

These handlebars options are currently NOT implemented:

  • compat - enables recursive field lookup
  • knownHelpers - list of helpers that are known to exist (truthy) at template execution time
  • knownHelpersOnly - allows further optimizations based on the known helpers list
  • trackIds - include the id names used to resolve parameters for helpers
  • noEscape - disables HTML escaping globally
  • strict - templates will throw rather than silently ignore missing fields
  • assumeObjects - removes object existence checks when traversing paths
  • preventIndent - disables the auto-indententation of nested partials
  • stringParams - resolves a parameter to it's name if the value isn't present in the context stack

These handlebars features are currently NOT implemented:

  • raw block content is not passed as a parameter to helper
  • blockHelperMissing - helper called when a helper can not be directly resolved
  • helperMissing - helper called when a potential helper expression was not found
  • @contextPath - value set in trackIds mode that records the lookup path for the current context
  • @level - log level

References

Others Implementations

Documentation

Overview

Example
package main

import (
	"fmt"
	"strings"

	"github.com/imantung/mario"
)

func main() {
	source := `Hello {{name}}`
	data := map[string]string{
		"name": "World",
	}

	tpl, err := mario.New().Parse(source)
	if err != nil {
		panic(err)
	}

	var b strings.Builder
	if err := tpl.Execute(&b, data); err != nil {
		panic(err)
	}

	fmt.Println(b.String())

}
Output:

Hello World
Example (Html_escape)
package main

import (
	"fmt"
	"strings"

	"github.com/imantung/mario"
)

func main() {
	source := "{{text}}\n{{{text}}}"

	data := map[string]string{
		"text": "This is <html> Tags",
	}

	var b strings.Builder
	if err := mario.Must(mario.New().Parse(source)).Execute(&b, data); err != nil {
		panic(err)
	}

	fmt.Println(b.String())

}
Output:

This is &lt;html&gt; Tags
This is <html> Tags
Example (Map)
package main

import (
	"fmt"
	"strings"

	"github.com/imantung/mario"
)

func main() {
	source := `{ "name": "{{name}}", "age": {{age}} }`
	data := map[string]interface{}{
		"name": "Mario",
		"age":  12,
	}

	tpl, err := mario.New().Parse(source)
	if err != nil {
		panic(err)
	}

	var b strings.Builder
	if err := tpl.Execute(&b, data); err != nil {
		panic(err)
	}

	fmt.Println(b.String())

}
Output:

{ "name": "Mario", "age": 12 }
Example (Rendering_context)
package main

import (
	"fmt"
	"strings"

	"github.com/imantung/mario"
)

func main() {
	source := `<div class="post">
  <h1>By {{author.firstName}} {{author.lastName}}</h1>
  <div class="body">{{body}}</div>

  <h1>Comments</h1>

  {{#each comments}}
  <h2>By {{author.firstName}} {{author.lastName}}</h2>
  <div class="body">{{content}}</div>
  {{/each}}
</div>`

	data := Post{
		Person{"Jean", "Valjean"},
		"Life is difficult",
		[]Comment{
			Comment{
				Person{"Marcel", "Beliveau"},
				"LOL!",
			},
		},
	}

	var b strings.Builder
	if err := mario.Must(mario.New().Parse(source)).Execute(&b, data); err != nil {
		panic(err)
	}

	fmt.Println(b.String())

}

type Person struct {
	FirstName string
	LastName  string
}

type Comment struct {
	Author Person
	Body   string `handlebars:"content"`
}

type Post struct {
	Author   Person
	Body     string
	Comments []Comment
}
Output:

<div class="post">
  <h1>By Jean Valjean</h1>
  <div class="body">Life is difficult</div>

  <h1>Comments</h1>

  <h2>By Marcel Beliveau</h2>
  <div class="body">LOL!</div>
</div>

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Escape

func Escape(s string) string

Escape escapes special HTML characters.

It can be used by helpers that return a SafeString and that need to escape some content by themselves.

func IsTrue

func IsTrue(obj interface{}) bool

IsTrue returns true if obj is a truthy value.

func RegisterHelper

func RegisterHelper(name string, fn interface{})

RegisterHelper to register new build-in helpers

func ResetHelpers

func ResetHelpers()

ResetHelpers to return current build-in helpers

func Str

func Str(value interface{}) string

Str returns string representation of any basic type value.

Types

type DataFrame

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

DataFrame represents a private data frame.

Cf. private variables documentation at: http://handlebarsjs.com/block_helpers.html

func NewDataFrame

func NewDataFrame() *DataFrame

NewDataFrame instanciates a new private data frame.

func (*DataFrame) Copy

func (p *DataFrame) Copy() *DataFrame

Copy instanciates a new private data frame with receiver as parent.

func (*DataFrame) Get

func (p *DataFrame) Get(key string) interface{}

Get gets a data value.

func (*DataFrame) Set

func (p *DataFrame) Set(key string, val interface{})

Set sets a data value.

type Helper

type Helper struct {
	reflect.Value
}

Helper implement functionality that is not part of the Handlebars language itself https://handlebarsjs.com/guide/expressions.html#helpers

Example (Safe_string)
package main

import (
	"fmt"
	"strings"

	"github.com/imantung/mario"
)

func main() {
	tpl, err := mario.New().
		WithHelperFunc("link", func(url, text string) mario.SafeString {
			return mario.SafeString("<a href='" + mario.Escape(url) + "'>" + mario.Escape(text) + "</a>")
		}).
		Parse("{{link url text}}")
	if err != nil {
		panic(err)
	}

	data := map[string]string{
		"url":  "http://www.aymerick.com/",
		"text": "This is a <em>cool</em> website",
	}

	var b strings.Builder
	if err := tpl.Execute(&b, data); err != nil {
		panic(err)
	}

	fmt.Println(b.String())

}
Output:

<a href='http://www.aymerick.com/'>This is a &lt;em&gt;cool&lt;/em&gt; website</a>

func CreateHelper

func CreateHelper(fn interface{}) *Helper

CreateHelper from function

type Options

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

Options represents the options argument provided to helpers and context functions.

func (*Options) Ctx

func (options *Options) Ctx() interface{}

Ctx returns current evaluation context.

func (*Options) Data

func (options *Options) Data(name string) interface{}

Data returns private data value.

func (*Options) DataFrame

func (options *Options) DataFrame() *DataFrame

DataFrame returns current private data frame.

func (*Options) DataStr

func (options *Options) DataStr(name string) string

DataStr returns string representation of private data value.

func (*Options) Eval

func (options *Options) Eval(ctx interface{}, field string) interface{}

Eval evaluates field for given context.

func (*Options) Fn

func (options *Options) Fn() string

Fn evaluates block with current evaluation context.

func (*Options) FnCtxData

func (options *Options) FnCtxData(ctx interface{}, data *DataFrame) string

FnCtxData evaluates block with given context and private data frame.

func (*Options) FnData

func (options *Options) FnData(data *DataFrame) string

FnData evaluates block with given private data frame.

func (*Options) FnWith

func (options *Options) FnWith(ctx interface{}) string

FnWith evaluates block with given context.

func (*Options) Hash

func (options *Options) Hash() map[string]interface{}

Hash returns entire hash.

func (*Options) HashProp

func (options *Options) HashProp(name string) interface{}

HashProp returns hash property.

func (*Options) HashStr

func (options *Options) HashStr(name string) string

HashStr returns string representation of hash property.

func (*Options) Inverse

func (options *Options) Inverse() string

Inverse evaluates "else block".

func (*Options) NewDataFrame

func (options *Options) NewDataFrame() *DataFrame

NewDataFrame instanciates a new data frame that is a copy of current evaluation data frame.

Parent of returned data frame is set to current evaluation data frame.

func (*Options) Param

func (options *Options) Param(pos int) interface{}

Param returns parameter at given position.

func (*Options) ParamStr

func (options *Options) ParamStr(pos int) string

ParamStr returns string representation of parameter at given position.

func (*Options) Params

func (options *Options) Params() []interface{}

Params returns all parameters.

func (*Options) Value

func (options *Options) Value(name string) interface{}

Value returns field value from current context.

func (*Options) ValueStr

func (options *Options) ValueStr(name string) string

ValueStr returns string representation of field value from current context.

type SafeString

type SafeString string

SafeString represents a string that must not be escaped.

A SafeString can be returned by helpers to disable escaping.

type Template

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

Template represents a handlebars template.

func Must

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

Must is a helper that wraps a call to a function returning (*Template, error) and panics if the error is non-nil. It is intended for use in variable initializations such as

Example
package main

import (
	"fmt"
	"strings"

	"github.com/imantung/mario"
)

func main() {
	source := `Hello {{name}}`
	data := map[string]string{
		"name": "World",
	}

	var b strings.Builder
	if err := mario.Must(mario.New().Parse(source)).Execute(&b, data); err != nil {
		panic(err)
	}

	fmt.Println(b.String())

}
Output:

Hello World

func New

func New() *Template

New mustache handlebars template

func (*Template) Execute

func (tpl *Template) Execute(w io.Writer, ctx interface{}) error

Execute evaluates template with given context.

func (*Template) ExecuteWith

func (tpl *Template) ExecuteWith(w io.Writer, ctx interface{}, frame *DataFrame) (err error)

ExecuteWith evaluates template with given context and private data frame.

func (*Template) Parse

func (tpl *Template) Parse(source string) (*Template, error)

Parse the template

func (*Template) Program

func (tpl *Template) Program() *ast.Program

Program return program

func (*Template) WithHelper

func (tpl *Template) WithHelper(name string, helper *Helper) *Template

WithHelper to set helper

func (*Template) WithHelperFunc

func (tpl *Template) WithHelperFunc(name string, fn interface{}) *Template

WithHelperFunc to create and set helper

func (*Template) WithPartial

func (tpl *Template) WithPartial(name string, template *Template) *Template

WithPartial registers an already parsed partial for that template.

Directories

Path Synopsis
Package ast provides structures to represent a handlebars Abstract Syntax Tree, and a Visitor interface to visit that tree.
Package ast provides structures to represent a handlebars Abstract Syntax Tree, and a Visitor interface to visit that tree.
Package handlebars contains all the tests that come from handlebars.js project.
Package handlebars contains all the tests that come from handlebars.js project.
Package lexer provides a handlebars tokenizer.
Package lexer provides a handlebars tokenizer.
Package parser provides a handlebars syntax analyser.
Package parser provides a handlebars syntax analyser.

Jump to

Keyboard shortcuts

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