Skip to content

Commit 33f2537

Browse files
Add coll.GoSlice and deprecate slice alias
Signed-off-by: Dave Henderson <[email protected]>
1 parent ea83b8f commit 33f2537

File tree

12 files changed

+349
-21
lines changed

12 files changed

+349
-21
lines changed

docs-src/content/functions/coll.yml

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ funcs:
4343
Hello world!
4444
Hello everybody!
4545
- name: coll.Slice
46+
deprecated: The `slice` alias is deprecated, use the full name `coll.Slice` instead.
4647
alias: slice
4748
description: |
4849
Creates a slice (like an array or list). Useful when needing to `range` over a bunch of variables.
@@ -53,10 +54,39 @@ funcs:
5354
description: the elements of the slice
5455
examples:
5556
- |
56-
$ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
57+
$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
5758
Hello, Bart
5859
Hello, Lisa
5960
Hello, Maggie
61+
- name: coll.GoSlice
62+
description: |
63+
This exposes the `slice` function from Go's [`text/template`](https://golang.org/pkg/text/template/#hdr-Functions)
64+
package. Note that using `slice` will use the `coll.Slice` function instead,
65+
which may not be desired.
66+
For some background on this, see [this issue](https://github.com/hairyhenderson/gomplate/issues/1461).
67+
68+
Here is the upstream documentation:
69+
70+
```
71+
slice returns the result of slicing its first argument by the
72+
remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2],
73+
while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
74+
is x[1:2:3]. The first argument must be a string, slice, or array.
75+
```
76+
77+
See the [Go language spec](https://go.dev/ref/spec#Slice_expressions) for
78+
more details.
79+
pipeline: false
80+
arguments:
81+
- name: item
82+
required: true
83+
description: the string, slice, or array to slice
84+
- name: indexes...
85+
required: false
86+
description: the indexes to slice the item by (0 to 3 arguments)
87+
examples:
88+
- |
89+
$ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}{{ if has $l "bar" }}a{{else}}no{{end}} bar'
6090
- name: coll.Has
6191
alias: has
6292
description: |
@@ -71,7 +101,7 @@ funcs:
71101
description: The item to search for
72102
examples:
73103
- |
74-
$ gomplate -i '{{ $l := slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
104+
$ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
75105
there is a bar
76106
- |
77107
$ export DATA='{"foo": "bar"}'
@@ -163,7 +193,7 @@ funcs:
163193
description: the slice or array to append to
164194
examples:
165195
- |
166-
$ gomplate -i '{{ slice 1 1 2 3 | append 5 }}'
196+
$ gomplate -i '{{ coll.Slice 1 1 2 3 | append 5 }}'
167197
[1 1 2 3 5]
168198
- name: coll.Prepend
169199
alias: prepend
@@ -183,7 +213,7 @@ funcs:
183213
description: the slice or array to prepend to
184214
examples:
185215
- |
186-
$ gomplate -i '{{ slice 4 3 2 1 | prepend 5 }}'
216+
$ gomplate -i '{{ coll.Slice 4 3 2 1 | prepend 5 }}'
187217
[5 4 3 2 1]
188218
- name: coll.Uniq
189219
alias: uniq
@@ -198,7 +228,7 @@ funcs:
198228
description: the input list
199229
examples:
200230
- |
201-
$ gomplate -i '{{ slice 1 2 3 2 3 4 1 5 | uniq }}'
231+
$ gomplate -i '{{ coll.Slice 1 2 3 2 3 4 1 5 | uniq }}'
202232
[1 2 3 4 5]
203233
- name: coll.Flatten
204234
alias: flatten
@@ -235,7 +265,7 @@ funcs:
235265
description: the list to reverse
236266
examples:
237267
- |
238-
$ gomplate -i '{{ slice 4 3 2 1 | reverse }}'
268+
$ gomplate -i '{{ coll.Slice 4 3 2 1 | reverse }}'
239269
[1 2 3 4]
240270
- name: coll.Sort
241271
alias: sort
@@ -257,7 +287,7 @@ funcs:
257287
description: the slice or array to sort
258288
examples:
259289
- |
260-
$ gomplate -i '{{ slice "foo" "bar" "baz" | coll.Sort }}'
290+
$ gomplate -i '{{ coll.Slice "foo" "bar" "baz" | coll.Sort }}'
261291
[bar baz foo]
262292
- |
263293
$ gomplate -i '{{ sort (slice 3 4 1 2 5) }}'

docs-src/content/functions/conv.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ funcs:
6565
6666
For creating more complex maps, see [`data.JSON`](../data/#data-json) or [`data.YAML`](../data/#data-yaml).
6767
68-
For creating arrays, see [`conv.Slice`](#conv-slice).
68+
For creating arrays, see [`coll.Slice`](#coll-slice).
6969
arguments:
7070
- name: in...
7171
required: true
@@ -97,7 +97,7 @@ funcs:
9797
description: the elements of the slice
9898
examples:
9999
- |
100-
$ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
100+
$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
101101
Hello, Bart
102102
Hello, Lisa
103103
Hello, Maggie
@@ -116,7 +116,7 @@ funcs:
116116
description: The item to search for
117117
examples:
118118
- |
119-
$ gomplate -i '{{ $l := slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
119+
$ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
120120
there is a bar
121121
- |
122122
$ export DATA='{"foo": "bar"}'
@@ -141,7 +141,7 @@ funcs:
141141
description: the separator
142142
examples:
143143
- |
144-
$ gomplate -i '{{ $a := slice 1 2 3 }}{{ join $a "-" }}'
144+
$ gomplate -i '{{ $a := coll.Slice 1 2 3 }}{{ join $a "-" }}'
145145
1-2-3
146146
- name: conv.URL
147147
alias: urlParse

docs-src/content/functions/func_doc.md.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{{ define "argName" }}{{ if not .required }}[{{ .name }}]{{else}}{{ .name }}{{end}}{{ end }}
22

33
{{- define "usage" }}### Usage
4-
{{- $arguments := index . "arguments" | default slice }}
4+
{{- $arguments := index . "arguments" | default coll.Slice }}
55
{{ if has . "rawUsage" }}{{ .rawUsage | strings.TrimSpace }}{{ else }}
66
```go
77
{{ .name }}{{ range $a := $arguments }} {{template "argName" $a }}{{end}}

docs-src/content/functions/strings.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ funcs:
135135
description: The list to sort
136136
examples:
137137
- |
138-
$ gomplate -i '{{ (slice "foo" "bar" "baz") | strings.Sort }}'
138+
$ gomplate -i '{{ (coll.Slice "foo" "bar" "baz") | strings.Sort }}'
139139
[bar baz foo]
140140
- name: strings.Split
141141
description: |
@@ -267,7 +267,7 @@ funcs:
267267
description: The input to quote
268268
examples:
269269
- |
270-
$ gomplate -i "{{ slice \"one word\" \"foo='bar baz'\" | shellQuote }}"
270+
$ gomplate -i "{{ coll.Slice \"one word\" \"foo='bar baz'\" | shellQuote }}"
271271
'one word' 'foo='"'"'bar baz'"'"''
272272
- |
273273
$ gomplate -i "{{ strings.ShellQuote \"it's a banana\" }}"

docs/content/functions/coll.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ Hello world!
6060
Hello everybody!
6161
```
6262

63-
## `coll.Slice`
63+
## `coll.Slice` _(deprecated)_
64+
**Deprecation Notice:** `slice` alias is deprecated, use `coll.Slice` instead
6465

6566
**Alias:** `slice`
6667

@@ -81,7 +82,45 @@ coll.Slice in...
8182
### Examples
8283

8384
```console
84-
$ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
85+
$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
86+
Hello, Bart
87+
Hello, Lisa
88+
Hello, Maggie
89+
```
90+
91+
## `coll.GoSlice`
92+
93+
This exposes the `slice` function from Go's [`text/template`](https://golang.org/pkg/text/template/#hdr-Functions)
94+
package. Note that using `slice` will use the `coll.Slice` function instead,
95+
which may not be desired.
96+
For some background on this, see [this issue](https://github.com/hairyhenderson/gomplate/issues/1461).
97+
98+
Here is the upstream documentation:
99+
100+
```
101+
slice returns the result of slicing its first argument by the
102+
remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2],
103+
while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
104+
is x[1:2:3]. The first argument must be a string, slice, or array.
105+
```
106+
107+
### Usage
108+
109+
```go
110+
coll.GoSlice item [indexes...]
111+
```
112+
113+
### Arguments
114+
115+
| name | description |
116+
|------|-------------|
117+
| `item` | _(required)_ the string, slice, or array to slice |
118+
| `indexes...` | _(optional)_ the indexes to slice the item by |
119+
120+
### Examples
121+
122+
```console
123+
$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
85124
Hello, Bart
86125
Hello, Lisa
87126
Hello, Maggie

funcs/coll.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package funcs
22

33
import (
44
"context"
5+
"reflect"
56

67
"github.com/hairyhenderson/gomplate/v3/conv"
8+
"github.com/hairyhenderson/gomplate/v3/internal/deprecated"
9+
"github.com/hairyhenderson/gomplate/v3/internal/texttemplate"
710

811
"github.com/hairyhenderson/gomplate/v3/coll"
912
"github.com/pkg/errors"
@@ -31,7 +34,7 @@ func CreateCollFuncs(ctx context.Context) map[string]interface{} {
3134
f["coll"] = func() interface{} { return ns }
3235

3336
f["has"] = ns.Has
34-
f["slice"] = ns.Slice
37+
f["slice"] = ns.deprecatedSlice
3538
f["dict"] = ns.Dict
3639
f["keys"] = ns.Keys
3740
f["values"] = ns.Values
@@ -56,6 +59,18 @@ func (CollFuncs) Slice(args ...interface{}) []interface{} {
5659
return coll.Slice(args...)
5760
}
5861

62+
// deprecatedSlice -
63+
// Deprecated: use coll.Slice instead
64+
func (f *CollFuncs) deprecatedSlice(args ...interface{}) []interface{} {
65+
deprecated.WarnDeprecated(f.ctx, "the 'slice' alias for coll.Slice is deprecated - use coll.Slice instead")
66+
return coll.Slice(args...)
67+
}
68+
69+
// GoSlice - same as text/template's 'slice' function
70+
func (CollFuncs) GoSlice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) {
71+
return texttemplate.GoSlice(item, indexes...)
72+
}
73+
5974
// Has -
6075
func (CollFuncs) Has(in interface{}, key string) bool {
6176
return coll.Has(in, key)

funcs/coll_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package funcs
22

33
import (
44
"context"
5+
"reflect"
56
"strconv"
67
"testing"
78

@@ -142,3 +143,36 @@ func TestOmit(t *testing.T) {
142143
assert.NoError(t, err)
143144
assert.EqualValues(t, map[string]interface{}{}, out)
144145
}
146+
147+
func TestGoSlice(t *testing.T) {
148+
t.Parallel()
149+
150+
c := &CollFuncs{}
151+
152+
in := reflect.ValueOf(nil)
153+
_, err := c.GoSlice(in)
154+
assert.Error(t, err)
155+
156+
in = reflect.ValueOf(42)
157+
_, err = c.GoSlice(in)
158+
assert.Error(t, err)
159+
160+
// invalid index type
161+
in = reflect.ValueOf([]interface{}{1})
162+
_, err = c.GoSlice(in, reflect.ValueOf([]interface{}{[]int{2}}))
163+
assert.Error(t, err)
164+
165+
// valid slice, no slicing
166+
in = reflect.ValueOf([]int{1})
167+
out, err := c.GoSlice(in)
168+
assert.NoError(t, err)
169+
assert.Equal(t, reflect.TypeOf([]int{}), out.Type())
170+
assert.EqualValues(t, []int{1}, out.Interface())
171+
172+
// valid slice, slicing
173+
in = reflect.ValueOf([]string{"foo", "bar", "baz"})
174+
out, err = c.GoSlice(in, reflect.ValueOf(1), reflect.ValueOf(3))
175+
assert.NoError(t, err)
176+
assert.Equal(t, reflect.TypeOf([]string{}), out.Type())
177+
assert.EqualValues(t, []string{"bar", "baz"}, out.Interface())
178+
}

funcs/conv.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (ConvFuncs) ToBools(in ...interface{}) []bool {
6161
}
6262

6363
// Slice -
64-
// Deprecated: use coll.Slice instead
64+
// Deprecated: use [coll.Slice] instead
6565
func (f *ConvFuncs) Slice(args ...interface{}) []interface{} {
6666
deprecated.WarnDeprecated(f.ctx, "conv.Slice is deprecated - use coll.Slice instead")
6767
return coll.Slice(args...)

internal/tests/integration/collection_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ func TestColl_Sort(t *testing.T) {
6161
`, "foo\nbaz\nbar\n")
6262

6363
inOutTest(t, `
64-
{{- coll.Sort (slice "b" "a" "c" "aa") }}
65-
{{ coll.Sort (slice "b" 14 "c" "aa") }}
66-
{{ coll.Sort (slice 3.14 3.0 4.0) }}
64+
{{- coll.Sort (coll.Slice "b" "a" "c" "aa") }}
65+
{{ coll.Sort (coll.Slice "b" 14 "c" "aa") }}
66+
{{ coll.Sort (coll.Slice 3.14 3.0 4.0) }}
6767
{{ coll.Sort "Scheme" (coll.Slice (conv.URL "zzz:///") (conv.URL "https:///") (conv.URL "http:///")) }}
6868
`, `[a aa b c]
6969
[b 14 c aa]

internal/texttemplate/exec.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Taken and adapted from the stdlib text/template/funcs.go.
2+
// Copyright 2011 The Go Authors. All rights reserved.
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
6+
package texttemplate
7+
8+
import (
9+
"reflect"
10+
)
11+
12+
// indirectInterface returns the concrete value in an interface value,
13+
// or else the zero reflect.Value.
14+
// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x):
15+
// the fact that x was an interface value is forgotten.
16+
func indirectInterface(v reflect.Value) reflect.Value {
17+
if v.Kind() != reflect.Interface {
18+
return v
19+
}
20+
if v.IsNil() {
21+
return reflect.Value{}
22+
}
23+
return v.Elem()
24+
}

0 commit comments

Comments
 (0)