tmplfn

package module
v0.0.22 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2021 License: BSD-3-Clause Imports: 16 Imported by: 11

README

DEPRECATED

This package was original written to support mkpage but is no longer being maintained for that purpose. Eventually this repository will be archived. RSD 2020-08-05.

tmplfn package

tmplfn package provides an additional set of common functions (e.g. standardized date formatting) to use with Go's text/template and html/template packages.

It is also possible to use tmplfn to create filters for data records based on the pipeline that would be passed to an if clause in a text template.

Documentation

Overview

*

  • tmplfn are a collection of functions useful to add to the default Go template/text and template/html template definitions *
  • @author R. S. Doiel *
  • Copyright (c) 2017, Caltech
  • All rights not granted herein are expressly reserved by Caltech. *
  • Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: *
  • 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. *
  • 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. *
  • 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. *
  • THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *

Index

Constants

View Source
const Version = "0.0.21"

Variables

View Source
var (

	// Time provides a common set of time/date related functions for use in text/template or html/template
	Time = template.FuncMap{
		"year": func(s string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format("2006")
		},

		"timefmt": func(s string, fmt string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format(fmt)
		},
		"rfc3339": func(s string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format(time.RFC3339)
		},
		"rfc1123": func(s string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format(time.RFC1123)
		},
		"rfc1123z": func(s string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format(time.RFC1123Z)
		},
		"rfc822z": func(s string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format(time.RFC822Z)
		},
		"rfc822": func(s string) string {
			var (
				dt  time.Time
				err error
			)
			if s == "now" {
				dt = time.Now()
			} else {
				dt, err = time.Parse("2006-01-02", normalizeDate(s))
				if err != nil {
					return ""
				}
			}
			return dt.Format(time.RFC822)
		},

		"datefmt": func(dt, outputFmtYMD, outputFmtYM, outputFmtY string) string {
			var (
				inputFmt  string
				outputFmt string
			)

			switch {
			case len(dt) == 4:
				inputFmt = "2006"
				outputFmt = outputFmtY
			case len(dt) > 4 && len(dt) <= 7:
				inputFmt = "2006-01"
				outputFmt = outputFmtYM
			default:
				inputFmt = "2006-01-02"
				outputFmt = outputFmtYMD
			}

			d, err := time.Parse(inputFmt, dt)
			if err != nil {
				return fmt.Sprintf("%s, %s", dt, err.Error())
			}
			return d.Format(outputFmt)
		},
	}

	Math = template.FuncMap{
		"int":      numbers.Int,
		"int64":    numbers.Int64,
		"float32":  numbers.Float32,
		"float64":  numbers.Float64,
		"add":      numbers.Add,
		"sub":      numbers.Subtract,
		"multiply": numbers.Multiply,
		"divide":   numbers.Divide,
		"modulo":   numbers.Modulo,
		"addi":     numbers.Addi,
		"subi":     numbers.Subtract,
		"typeof": func(t interface{}) string {
			if t == nil {
				return "<nil>"
			}
			return fmt.Sprintf("%T", t)
		},
	}

	Strings = template.FuncMap{

		"concat": func(strs ...string) string {
			return strings.Join(strs, "")
		},

		"has_prefix":  strings.HasPrefix,
		"has_suffix":  strings.HasSuffix,
		"contains":    strings.Contains,
		"trim_prefix": strings.TrimPrefix,
		"trim_suffix": strings.TrimSuffix,
		"trimspace":   strings.TrimSpace,
		"trim":        strings.Trim,
		"trim_left":   strings.TrimLeft,
		"trim_right":  strings.TrimRight,
		"lowercase":   strings.ToLower,
		"uppercase":   strings.ToUpper,
		"title":       strings.Title,
		"replace":     strings.Replace,

		"join": func(li []interface{}, sep string) string {
			var l []string
			for _, item := range li {
				if item == nil {
					l = append(l, "")
				} else {
					l = append(l, fmt.Sprintf("%s", item))
				}
			}
			return strings.Join(l, sep)
		},
		"split": func(s string, delimiter string) []string {
			return strings.Split(s, delimiter)
		},
		"splitN": func(s string, delimiter string, count int) []string {
			return strings.SplitN(s, delimiter, count)
		},
	}

	Page = template.FuncMap{
		"nl2p": func(s string) string {
			return strings.Replace(strings.Replace(s, "\n\n", "<p>", -1), "\n", "<br />", -1)
		},
		"previ": func(pos, move_size, min_pos, max_pos int, wrap bool) int {
			prev := pos - move_size
			if prev < min_pos {
				if wrap == false {
					return min_pos
				}
				return max_pos
			}
			return prev
		},
		"nexti": func(pos, move_size, min_pos, max_pos int, wrap bool) int {
			next := pos + move_size
			if next > max_pos {
				if wrap == false {
					return pos
				}
				return min_pos
			}
			return next
		},
		"synopsis": func(s string) string {
			return doc.Synopsis(s)
		},
		"urldecode": func(s string) string {
			sDecoded, err := url.QueryUnescape(s)
			if err != nil {
				log.Printf("Bad encoding request: %q, %s\n", s, err)
				return ""
			}
			return sDecoded
		},
		"urlencode": func(s string) string {
			return url.QueryEscape(s)
		},
		"url_path_escape": func(s string) string {
			return url.PathEscape(s)
		},
		"url_path_unescape": func(s string) string {
			sDecoded, err := url.PathUnescape(s)
			if err != nil {
				log.Printf("Bad encoding request: %q, %s\n", s, err)
				return ""
			}
			return sDecoded
		},
		"stringify": func(data interface{}, prettyPrint bool) string {
			if prettyPrint == true {
				if buf, err := json.MarshalIndent(data, "", "\t"); err == nil {
					return string(buf)
				}
			} else if buf, err := json.Marshal(data); err == nil {
				return string(buf)
			}
			return ""
		},
		"codeblock": func(src string, start int, end int, hint string) string {
			result := []string{}
			lines := strings.Split(src, "\n")
			if start < 1 {
				start = 0
			}
			if end < 1 {
				end = len(lines)
			}
			if (end - start) > 0 {
				result = append(result, fmt.Sprintf("```%s", hint))
			}
			for i, line := range lines[start:end] {
				if len(line) > 0 {
					result = append(result, fmt.Sprintf("    %s", line))
				} else if i > 0 && i < (end-1) {
					result = append(result, "")
				}
			}
			if len(result) > 0 {
				result = append(result, "```")
			}
			return strings.Join(result, "\n")
		},
	}

	// Iterables produces lists that then can supply the template range function with values
	Iterables = template.FuncMap{

		"ints": func(start, end, inc int) []int {
			var result []int
			if start == end {
				return []int{start}
			} else if start < end {
				for i := start; i <= end; i = i + inc {
					result = append(result, i)
				}
			} else {
				for i := end; i >= start; i = i - inc {
					result = append(result, i)
				}
			}
			return result
		},

		"int64s": func(start, end, inc int64) []int64 {
			var result []int64
			if start == end {
				return []int64{start}
			} else if start < end {
				for i := start; i <= end; i = i + inc {
					result = append(result, i)
				}
			} else {
				for i := end; i >= start; i = i - inc {
					result = append(result, i)
				}
			}
			return result
		},

		"float32s": func(start, end, inc float32) []float32 {
			var result []float32
			if start == end {
				return []float32{start}
			} else if start < end {
				for i := start; i <= end; i = i + inc {
					result = append(result, i)
				}
			} else {
				for i := end; i >= start; i = i - inc {
					result = append(result, i)
				}
			}
			return result
		},

		"float64s": func(start, end, inc float64) []float64 {
			var result []float64
			if start == end {
				return []float64{start}
			} else if start < end {
				for i := start; i <= end; i = i + inc {
					result = append(result, i)
				}
			} else {
				for i := end; i >= start; i = i - inc {
					result = append(result, i)
				}
			}
			return result
		},

		"cols2rows": func(cols ...[]interface{}) [][]interface{} {
			var (
				row     []interface{}
				rows    [][]interface{}
				maxRows int
			)

			for _, col := range cols {
				if len(col) >= maxRows {
					maxRows = len(col)
				}
			}

			for i := 0; i < maxRows; i++ {

				row = []interface{}{}

				for _, col := range cols {
					if i < len(col) {
						row = append(row, col[i])
					} else {
						row = append(row, "")
					}
				}

				rows = append(rows, row)
			}

			return rows
		},

		"length": func(arg interface{}) int {
			switch arg.(type) {
			case string:
				return len(arg.(string))
			case []byte:
				return len(arg.([]byte))
			case []rune:
				return len(arg.([]rune))
			case []int:
				return len(arg.([]int))
			case []int64:
				return len(arg.([]int64))
			case []float64:
				return len(arg.([]float64))
			case []bool:
				return len(arg.([]float64))
			}
			return 0
		},
	}

	//Booleans provides a set of functions working with Boolean data
	Booleans = template.FuncMap{

		"count_true": func(booleans ...bool) int {
			cnt := 0
			for _, val := range booleans {
				if val == true {
					cnt++
				}
			}
			return cnt
		},
	}

	//Path methods for working with paths (E.g. path.Base(), path.Ext() and path.Dir() in Go path package)
	Path = template.FuncMap{

		"basename": func(args ...string) string {
			p := path.Base(args[0])
			if len(args) > 1 {
				for _, ext := range args[1:] {
					if strings.HasSuffix(p, ext) {
						p = strings.TrimSuffix(p, ext)
					}
				}
			}
			return p
		},
		"base": path.Base,
		"ext":  path.Ext,
		"dir":  path.Dir,
	}

	//Url methods are for working with URLs and extracting useful parts
	Url = template.FuncMap{

		"url_scheme": func(args ...string) string {
			if u, err := url.Parse(args[0]); err == nil {
				return u.Scheme
			}
			return ""
		},

		"url_host": func(args ...string) string {
			if u, err := url.Parse(args[0]); err == nil {
				return u.Host
			}
			return ""
		},

		"url_path": func(args ...string) string {
			if u, err := url.Parse(args[0]); err == nil {
				return u.Path
			}
			return ""
		},
	}

	//Dotpath methods from datatools/dotpath in templates
	Dotpath = template.FuncMap{

		"dotpath": func(data interface{}, p string, defaultVal interface{}) interface{} {
			if val, err := dotpath.Eval(p, data); err == nil {
				return val
			}
			return defaultVal
		},

		"has_dotpath": func(data interface{}, p string, existsVal interface{}, notExistsVal interface{}) interface{} {
			if _, err := dotpath.Eval(p, data); err == nil {
				return existsVal
			}
			return notExistsVal
		},
	}

	// Console holds functions that interact with the console where
	// the template processing is happening (e.g. for a web service
	// writing to the console log of the web server).
	Console = template.FuncMap{

		"version": func() string {
			return Version
		},

		"writelog": func(v ...interface{}) string {
			log.Println(v...)
			return ""
		},
	}

	// RegExp holds function that work off of Go's (not pcre) regular expression library.
	RegExp = template.FuncMap{
		"match": func(expr, val string) bool {
			re := regexp.MustCompile(expr)
			return re.MatchString(val)
		},
	}
)
View Source
var (
	TextTools = template.FuncMap{
		"slug":          slug,
		"unslug":        unslug,
		"english_title": englishTitle,
	}
)

Functions

func AllFuncs added in v0.0.10

func AllFuncs() template.FuncMap

AllFuncs() returns a Join of func maps available in tmplfn

func Join

func Join(maps ...template.FuncMap) template.FuncMap

Join take one or more func maps and returns an aggregate one.

Types

type Filter added in v0.0.18

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

Filter holds the parsed filter source that can be applied to a JSON record to determine if the record is included or exluded NOTE: We're riding on the back of a text template to render true or false

func ParseFilter added in v0.0.18

func ParseFilter(src string) (*Filter, error)

ParseFilter parses a byte slice and returns a Filter struct and error

func (*Filter) Apply added in v0.0.18

func (f *Filter) Apply(data interface{}) (bool, error)

Apply uses Filter Struct and takes an interface{} object returns true if filter matched or false

type Tmpl added in v0.0.18

type Tmpl struct {
	// Holds the function map for templates
	FuncMap template.FuncMap

	// Code holds a map of names to byte arrays, the byte arrays hold the template source code
	// the names can be either filename or other names defined by the implementor
	Code map[string][]byte
}

Src is a mapping of template source to names and byte arrays. It is useful to create a series of defaults templates that can be overwritten by user supplied template files.

func New added in v0.0.18

func New(fm template.FuncMap) *Tmpl

New creates a pointer to a template.Template and empty map of names to byte arrays pointing at an empty byte array

func (Tmpl) Add added in v0.0.18

func (t Tmpl) Add(name string, src []byte) error

Add takes a name and source (byte array) and updates t.Code with it. It is like Merge but for a single file. The name provided in Add is used as the key to the template source code map.

func (Tmpl) Assemble added in v0.0.18

func (t Tmpl) Assemble() (*template.Template, error)

Assemble mimics template.ParseFiles() but works with the properties of a Tmpl struct.

func (*Tmpl) ReadFiles added in v0.0.18

func (t *Tmpl) ReadFiles(fNames ...string) error

ReadFiles takes the given file, filenames or directory name(s) and reads the byte array(s) into the Code map. If a filename is a directory then the directory is scanned for files ending in ".tmpl" and those are loaded into the Code map. It does NOT parse/assemble templates. The basename in the path is used as the name of the template (e.g. templates/page.tmpl would be stored as page.tmpl.

func (Tmpl) ReadMap added in v0.0.18

func (t Tmpl) ReadMap(sourceMap map[string][]byte) error

ReadMap works like ReadFiles but takes the name/source pairs from a map rather than the file system. It expected template names to end in ".tmpl" like ReadFiles() Note the basename of the key provided in the sourceMap is used as the key in the Code source code map (e.g. /templates/page.tmpl is stored as page.tmpl)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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