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
This commit is contained in:
Cameron Moore 2016-03-09 16:27:56 -06:00 committed by Bjørn Erik Pedersen
parent 0743646f32
commit 09c8c17bf0
3 changed files with 75 additions and 10 deletions

View file

@ -37,7 +37,7 @@ non-nil for any other types.
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.

View file

@ -1243,10 +1243,21 @@ func dateFormat(layout string, v interface{}) (string, error) {
// 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 @@ func dfault(dflt, given interface{}) interface{} {
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.

View file

@ -1844,7 +1844,10 @@ func TestDateFormat(t *testing.T) {
}
}
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,34 +1857,80 @@ func TestDefault(t *testing.T) {
{"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)
}
}
}
func TestSafeHTML(t *testing.T) {
for i, this := range []struct {
str string