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)
}
}
}