Skip to content

Commit e26abb3

Browse files
committed
Generalize "where" using reflection
1 parent 318b5ee commit e26abb3

File tree

4 files changed

+47
-8
lines changed

4 files changed

+47
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ Within those templates, the object emitted by docker-gen will have [this structu
134134
* *`split $string $sep`*: Splits `$string` into a slice of substrings delimited by `$sep`. Alias for [`strings.Split`](http://golang.org/pkg/strings/#Split)
135135
* *`trimPrefix $prefix $string`*: If `$prefix` is a prefix of `$string`, return `$string` with `$prefix` trimmed from the beginning. Otherwise, return `$string` unchanged.
136136
* *`trimSuffix $suffix $string`*: If `$suffix` is a suffix of `$string`, return `$string` with `$suffix` trimmed from the end. Otherwise, return `$string` unchanged.
137-
* *`where $containers $fieldPath $value`*: Filters an array of `RuntimeContainer` instances based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value, which must be a string. Returns an array of containers having that value.
137+
* *`where $entries $fieldPath $value`*: Filters an array or slice based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value. Returns an array of items having that value.
138138
* *`whereExist $containers $fieldPath`*: Like `where`, but returns only containers where `$fieldPath` exists (is not nil).
139139
* *`whereNotExist $containers $fieldPath`*: Like `where`, but returns only containers where `$fieldPath` does not exist (is nil).
140140
* *`whereAny $containers $fieldPath $sep $values`*: Like `where`, but the string value specified by `$fieldPath` is first split by `$sep` into a list of strings. The comparison value is a string slice with possible matches. Returns containers which OR intersect these values.

reflect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func deepGet(item interface{}, path string) interface{} {
4040
return deepGet(mapValue.Interface(), strings.Join(parts[1:], "."))
4141
}
4242
default:
43-
log.Printf("can't group by %s\n", path)
43+
log.Printf("can't group by %s (value %v, kind %s)\n", path, itemValue, itemValue.Kind())
4444
}
4545
return nil
4646
}

template.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,27 @@ func groupByKeys(entries []*RuntimeContainer, key string) []string {
6767
}
6868

6969
// selects entries based on key
70-
func where(entries []*RuntimeContainer, key string, cmp string) []*RuntimeContainer {
71-
selection := []*RuntimeContainer{}
72-
for _, v := range entries {
73-
value := deepGet(*v, key)
74-
if value == cmp {
70+
func where(entries interface{}, key string, cmp interface{}) (interface{}, error) {
71+
entriesVal := reflect.ValueOf(entries)
72+
73+
switch entriesVal.Kind() {
74+
case reflect.Array, reflect.Slice:
75+
break
76+
default:
77+
return nil, fmt.Errorf("Must pass an array or slice to 'where'; received %v", entries)
78+
}
79+
80+
selection := make([]interface{}, 0)
81+
for i := 0; i < entriesVal.Len(); i++ {
82+
v := reflect.Indirect(entriesVal.Index(i)).Interface()
83+
84+
value := deepGet(v, key)
85+
if reflect.DeepEqual(value, cmp) {
7586
selection = append(selection, v)
7687
}
7788
}
78-
return selection
89+
90+
return selection, nil
7991
}
8092

8193
// selects entries where a key exists

template_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,26 @@ func TestWhere(t *testing.T) {
181181
"VIRTUAL_HOST": "demo1.localhost",
182182
},
183183
ID: "1",
184+
Addresses: []Address{
185+
Address{
186+
IP: "172.16.42.1",
187+
Port: "80",
188+
Proto: "tcp",
189+
},
190+
},
184191
},
185192
&RuntimeContainer{
186193
Env: map[string]string{
187194
"VIRTUAL_HOST": "demo2.localhost",
188195
},
189196
ID: "2",
197+
Addresses: []Address{
198+
Address{
199+
IP: "172.16.42.1",
200+
Port: "9999",
201+
Proto: "tcp",
202+
},
203+
},
190204
},
191205
&RuntimeContainer{
192206
Env: map[string]string{
@@ -211,6 +225,19 @@ func TestWhere(t *testing.T) {
211225
{`{{where . "Env.VIRTUAL_HOST" "demo2.localhost" | len}}`, containers, `2`},
212226
{`{{where . "Env.VIRTUAL_HOST" "demo3.localhost" | len}}`, containers, `1`},
213227
{`{{where . "Env.NOEXIST" "demo3.localhost" | len}}`, containers, `0`},
228+
{`{{where .Addresses "Port" "80" | len}}`, containers[0], `1`},
229+
{`{{where .Addresses "Port" "80" | len}}`, containers[1], `0`},
230+
{
231+
`{{where . "Value" 5 | len}}`,
232+
[]struct {
233+
Value int
234+
}{
235+
{Value: 5},
236+
{Value: 3},
237+
{Value: 5},
238+
},
239+
`2`,
240+
},
214241
}
215242

216243
for n, test := range tests {

0 commit comments

Comments
 (0)