gobuffalo/velvet

View on GitHub
helpers.go

Summary

Maintainability
A
0 mins
Test Coverage
package velvet

import (
    "encoding/json"
    "fmt"
    "html/template"
    "reflect"
    "strconv"
    "strings"
    "sync"

    "github.com/markbates/inflect"
    "github.com/pkg/errors"
)

// Helpers contains all of the default helpers for velvet.
// These will be available to all templates. You should add
// any custom global helpers to this list.
var Helpers = HelperMap{
    moot: &sync.Mutex{},
}

func init() {
    Helpers.Add("if", ifHelper)
    Helpers.Add("unless", unlessHelper)
    Helpers.Add("each", eachHelper)
    Helpers.Add("eq", equalHelper)
    Helpers.Add("equal", equalHelper)
    Helpers.Add("neq", notEqualHelper)
    Helpers.Add("notequal", notEqualHelper)
    Helpers.Add("json", toJSONHelper)
    Helpers.Add("js_escape", template.JSEscapeString)
    Helpers.Add("html_escape", template.HTMLEscapeString)
    Helpers.Add("upcase", strings.ToUpper)
    Helpers.Add("downcase", strings.ToLower)
    Helpers.Add("content_for", contentForHelper)
    Helpers.Add("content_of", contentOfHelper)
    Helpers.Add("markdown", markdownHelper)
    Helpers.Add("len", lenHelper)
    Helpers.Add("debug", debugHelper)
    Helpers.Add("inspect", inspectHelper)
    Helpers.AddMany(inflect.Helpers)
}

// HelperContext is an optional context that can be passed
// as the last argument to helper functions.
type HelperContext struct {
    Context     *Context
    Args        []interface{}
    evalVisitor *evalVisitor
}

// Block executes the block of template associated with
// the helper, think the block inside of an "if" or "each"
// statement.
func (h HelperContext) Block() (string, error) {
    return h.BlockWith(h.Context)
}

// BlockWith executes the block of template associated with
// the helper, think the block inside of an "if" or "each"
// statement. It takes a new context with which to evaluate
// the block.
func (h HelperContext) BlockWith(ctx *Context) (string, error) {
    nev := newEvalVisitor(h.evalVisitor.template, ctx)
    nev.blockParams = h.evalVisitor.blockParams
    dd := nev.VisitProgram(h.evalVisitor.curBlock.Program)
    switch tp := dd.(type) {
    case string:
        return tp, nil
    case error:
        return "", errors.WithStack(tp)
    case nil:
        return "", nil
    default:
        return "", errors.WithStack(errors.Errorf("unknown return value %T %+v", dd, dd))
    }
}

// ElseBlock executes the "inverse" block of template associated with
// the helper, think the "else" block of an "if" or "each"
// statement.
func (h HelperContext) ElseBlock() (string, error) {
    return h.ElseBlockWith(h.Context)
}

// ElseBlockWith executes the "inverse" block of template associated with
// the helper, think the "else" block of an "if" or "each"
// statement. It takes a new context with which to evaluate
// the block.
func (h HelperContext) ElseBlockWith(ctx *Context) (string, error) {
    if h.evalVisitor.curBlock.Inverse == nil {
        return "", nil
    }
    nev := newEvalVisitor(h.evalVisitor.template, ctx)
    nev.blockParams = h.evalVisitor.blockParams
    dd := nev.VisitProgram(h.evalVisitor.curBlock.Inverse)
    switch tp := dd.(type) {
    case string:
        return tp, nil
    case error:
        return "", errors.WithStack(tp)
    case nil:
        return "", nil
    default:
        return "", errors.WithStack(errors.Errorf("unknown return value %T %+v", dd, dd))
    }
}

// Helpers returns a HelperMap containing all of the known helpers
func (h HelperContext) Helpers() *HelperMap {
    return &h.evalVisitor.template.Helpers
}

// Get is a convenience method that calls the underlying Context.
func (h HelperContext) Get(key string) interface{} {
    return h.Context.Get(key)
}

// toJSONHelper converts an interface into a string.
func toJSONHelper(v interface{}) (template.HTML, error) {
    b, err := json.Marshal(v)
    if err != nil {
        return "", errors.WithStack(err)
    }
    return template.HTML(b), nil
}

func lenHelper(v interface{}) string {
    rv := reflect.ValueOf(v)
    if rv.Kind() == reflect.Ptr {
        rv = rv.Elem()
    }
    return strconv.Itoa(rv.Len())
}

// Debug by verbosely printing out using 'pre' tags.
func debugHelper(v interface{}) template.HTML {
    return template.HTML(fmt.Sprintf("<pre>%+v</pre>", v))
}

func inspectHelper(v interface{}) string {
    return fmt.Sprintf("%+v", v)
}