Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/content/templates/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ and other basic tools; these are listed in the

## General

### default
Checks whether a given value is set and returns a default value if it is not.
"Set" in this context means true for booleans; non-zero for numeric types;
non-zero length for strings, arrays, slices, and maps; any struct value; or
non-nil for any other types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should add Zero for time.Time (time.IsZero) to the list of not set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


e.g.

{{ .Params.font | default "Roboto" }} → default is "Roboto"
{{ default "Roboto" .Params.font }} → default is "Roboto"

### delimit
Loops through any array, slice or map and returns a string of all the values separated by the delimiter. There is an optional third parameter that lets you choose a different delimiter to go between the last two values.
Maps will be sorted by the keys, and only a slice of the values will be returned, keeping a consistent output order.
Expand Down
39 changes: 39 additions & 0 deletions tpl/template_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,44 @@ func dateFormat(layout string, v interface{}) (string, error) {
return t.Format(layout), nil
}

// dfault checks whether a given value is set and returns a default value if it
// is not. "Set" in this context means true for booleans; non-zero for numeric
// types; non-zero length for strings, arrays, slices, and maps; any struct
// value; or non-nil for any other types.
func dfault(dflt, given interface{}) interface{} {
g := reflect.ValueOf(given)
if !g.IsValid() {
return dflt
}

set := false

switch g.Kind() {
case reflect.Bool:
set = g.Bool()
case reflect.String, reflect.Array, reflect.Slice, reflect.Map:
set = g.Len() != 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
set = g.Int() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
set = g.Uint() != 0
case reflect.Float32, reflect.Float64:
set = g.Float() != 0
case reflect.Complex64, reflect.Complex128:
set = g.Complex() != 0
case reflect.Struct:
set = true
default:
set = !g.IsNil()
}

if set {
return given
}

return dflt
}

// safeHTMLAttr returns a given string as html/template HTMLAttr content.
//
// safeHTMLAttr is currently disabled, pending further discussion
Expand Down Expand Up @@ -1512,6 +1550,7 @@ func init() {
"chomp": chomp,
"countrunes": countRunes,
"countwords": countWords,
"default": dfault,
"dateFormat": dateFormat,
"delimit": delimit,
"dict": dictionary,
Expand Down
45 changes: 42 additions & 3 deletions tpl/template_funcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/spf13/cast"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"html/template"
"math/rand"
"path"
Expand All @@ -29,6 +26,10 @@ import (
"strings"
"testing"
"time"

"github.com/spf13/cast"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)

type tstNoStringer struct {
Expand Down Expand Up @@ -1839,6 +1840,44 @@ func TestDateFormat(t *testing.T) {
}
}

func TestDefault(t *testing.T) {
for i, this := range []struct {
dflt interface{}
given interface{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need nil-variants for these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

expected interface{}
}{
{"5", 0, "5"},

{"test1", "set", "set"},
{"test2", "", "test2"},

{[2]int{10, 20}, [2]int{1, 2}, [2]int{1, 2}},
{[2]int{10, 20}, [0]int{}, [2]int{10, 20}},

{[]string{"one"}, []string{"uno"}, []string{"uno"}},
{[]string{"one"}, []string{}, []string{"one"}},

{map[string]int{"one": 1}, map[string]int{"uno": 1}, map[string]int{"uno": 1}},
{map[string]int{"one": 1}, map[string]int{}, map[string]int{"one": 1}},

{10, 1, 1},
{10, 0, 10},

{float32(10), float32(1), float32(1)},
{float32(10), 0, float32(10)},

{complex(2, -2), complex(1, -1), complex(1, -1)},
{complex(2, -2), complex(0, 0), complex(2, -2)},

{struct{ f string }{f: "one"}, struct{ f string }{}, struct{ f string }{}},
} {
res := dfault(this.dflt, this.given)
if !reflect.DeepEqual(this.expected, res) {
t.Errorf("[%d] default returned %v, but expected %v", i, res, this.expected)
}
}
}

func TestSafeHTML(t *testing.T) {
for i, this := range []struct {
str string
Expand Down