shithub: hugo

Download patch

ref: 09c8c17bf06ed5f215922b0755558e58a8723802
parent: 0743646f3232b55b4df2690cbb11682794884243
author: Cameron Moore <[email protected]>
date: Wed Mar 9 11:27:56 EST 2016

tpl: fix default function

This commit fixes a few things:

1. `given` is now a variadic parameter so that piping works properly
2. add separate template tests to make sure piping works
3. support time values
4. add more tests of the dfault function

--- a/docs/content/templates/functions.md
+++ b/docs/content/templates/functions.md
@@ -37,7 +37,7 @@
 e.g.
 
     {{ index .Params "font" | default "Roboto" }} → default is "Roboto"
-    {{ default "Roboto" .Params.font }} → default is "Roboto"
+    {{ default "Roboto" (index .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.
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -1243,10 +1243,21 @@
 // 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)
+func dfault(dflt interface{}, given ...interface{}) (interface{}, error) {
+	// given is variadic because the following construct will not pass a piped
+	// argument when the key is missing:  {{ index . "key" | default "foo" }}
+	// The Go template will complain that we got 1 argument when we expectd 2.
+
+	if given == nil || len(given) == 0 {
+		return dflt, nil
+	}
+	if len(given) != 1 {
+		return nil, fmt.Errorf("wrong number of args for default: want 2 got %d", len(given)+1)
+	}
+
+	g := reflect.ValueOf(given[0])
 	if !g.IsValid() {
-		return dflt
+		return dflt, nil
 	}
 
 	set := false
@@ -1265,16 +1276,21 @@
 	case reflect.Complex64, reflect.Complex128:
 		set = g.Complex() != 0
 	case reflect.Struct:
-		set = true
+		switch actual := given[0].(type) {
+		case time.Time:
+			set = !actual.IsZero()
+		default:
+			set = true
+		}
 	default:
 		set = !g.IsNil()
 	}
 
 	if set {
-		return given
+		return given[0], nil
 	}
 
-	return dflt
+	return dflt, nil
 }
 
 // safeHTMLAttr returns a given string as html/template HTMLAttr content.
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -1844,7 +1844,10 @@
 	}
 }
 
-func TestDefault(t *testing.T) {
+func TestDefaultFunc(t *testing.T) {
+	then := time.Now()
+	now := time.Now()
+
 	for i, this := range []struct {
 		dflt     interface{}
 		given    interface{}
@@ -1854,30 +1857,76 @@
 
 		{"test1", "set", "set"},
 		{"test2", "", "test2"},
+		{"test3", nil, "test3"},
 
 		{[2]int{10, 20}, [2]int{1, 2}, [2]int{1, 2}},
 		{[2]int{10, 20}, [0]int{}, [2]int{10, 20}},
+		{[2]int{100, 200}, nil, [2]int{100, 200}},
 
 		{[]string{"one"}, []string{"uno"}, []string{"uno"}},
-		{[]string{"one"}, []string{}, []string{"one"}},
+		{[]string{"two"}, []string{}, []string{"two"}},
+		{[]string{"three"}, nil, []string{"three"}},
 
 		{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}},
+		{map[string]int{"two": 2}, nil, map[string]int{"two": 2}},
 
 		{10, 1, 1},
 		{10, 0, 10},
+		{20, nil, 20},
 
 		{float32(10), float32(1), float32(1)},
 		{float32(10), 0, float32(10)},
+		{float32(20), nil, float32(20)},
 
 		{complex(2, -2), complex(1, -1), complex(1, -1)},
 		{complex(2, -2), complex(0, 0), complex(2, -2)},
+		{complex(3, -3), nil, complex(3, -3)},
 
 		{struct{ f string }{f: "one"}, struct{ f string }{}, struct{ f string }{}},
+		{struct{ f string }{f: "two"}, nil, struct{ f string }{f: "two"}},
+
+		{then, now, now},
+		{then, time.Time{}, then},
 	} {
-		res := dfault(this.dflt, this.given)
+		res, err := dfault(this.dflt, this.given)
+		if err != nil {
+			t.Errorf("[%d] default returned an error: %s", i, err)
+			continue
+		}
 		if !reflect.DeepEqual(this.expected, res) {
 			t.Errorf("[%d] default returned %v, but expected %v", i, res, this.expected)
+		}
+	}
+}
+
+func TestDefault(t *testing.T) {
+	for i, this := range []struct {
+		input    interface{}
+		tpl      string
+		expected string
+		ok       bool
+	}{
+		{map[string]string{"foo": "bar"}, `{{ index . "foo" | default "nope" }}`, `bar`, true},
+		{map[string]string{"foo": "pop"}, `{{ index . "bar" | default "nada" }}`, `nada`, true},
+		{map[string]string{"foo": "cat"}, `{{ default "nope" .foo }}`, `cat`, true},
+		{map[string]string{"foo": "dog"}, `{{ default "nope" .foo "extra" }}`, ``, false},
+	} {
+		tmpl, err := New().New("test").Parse(this.tpl)
+		if err != nil {
+			t.Errorf("[%d] unable to create new html template %q: %s", i, this.tpl, err)
+			continue
+		}
+
+		buf := new(bytes.Buffer)
+		err = tmpl.Execute(buf, this.input)
+		if (err == nil) != this.ok {
+			t.Errorf("[%d] execute template returned unexpected error: %s", i, err)
+			continue
+		}
+
+		if buf.String() != this.expected {
+			t.Errorf("[%d] execute template got %v, but expected %v", i, buf.String(), this.expected)
 		}
 	}
 }