shithub: hugo

Download patch

ref: ce9ee3cf490ab5485b6ad7aa7a47d9fa9d176fa3
parent: 0962470850ed6802b645d5bd6663db60807c8f5b
author: Cameron Moore <[email protected]>
date: Wed Mar 9 09:40:00 EST 2016

tpl: Add default function

--- a/docs/content/templates/functions.md
+++ b/docs/content/templates/functions.md
@@ -28,6 +28,17 @@
 
 ## 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.
+
+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.
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -1239,6 +1239,44 @@
 	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
@@ -1537,6 +1575,7 @@
 		"chomp":        chomp,
 		"countrunes":   countRunes,
 		"countwords":   countWords,
+		"default":      dfault,
 		"dateFormat":   dateFormat,
 		"delimit":      delimit,
 		"dict":         dictionary,
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -18,9 +18,6 @@
 	"encoding/base64"
 	"errors"
 	"fmt"
-	"github.com/spf13/cast"
-	"github.com/spf13/viper"
-	"github.com/stretchr/testify/assert"
 	"html/template"
 	"math/rand"
 	"path"
@@ -29,6 +26,10 @@
 	"strings"
 	"testing"
 	"time"
+
+	"github.com/spf13/cast"
+	"github.com/spf13/viper"
+	"github.com/stretchr/testify/assert"
 )
 
 type tstNoStringer struct {
@@ -1839,6 +1840,44 @@
 			if result != this.expect {
 				t.Errorf("[%d] DateFormat got %v but expected %v", i, result, this.expect)
 			}
+		}
+	}
+}
+
+func TestDefault(t *testing.T) {
+	for i, this := range []struct {
+		dflt     interface{}
+		given    interface{}
+		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)
 		}
 	}
 }