summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2019-02-16 00:00:28 -0500
committerGitHub <noreply@github.com>2019-02-16 00:00:28 -0500
commitffb7d693baa8f95df96484e9b1a9c6267be066cf (patch)
tree7408c5892fad19dd0b09ce63c8481254ab22873e
parent4a88a3ed4c2880994975cd7ae5323c066507983a (diff)
parent8d9ab1cffb4d0e5b0a7c70a01ad6c142952362a0 (diff)
Merge pull request #492 from hairyhenderson/tmpl-exec-func-444-485
New tmpl.Exec function
-rw-r--r--docs-src/content/functions/tmpl.yml20
-rw-r--r--docs/content/functions/tmpl.md33
-rw-r--r--tests/integration/tmpl_test.go88
-rw-r--r--tmpl/tmpl.go23
-rw-r--r--tmpl/tmpl_test.go21
5 files changed, 183 insertions, 2 deletions
diff --git a/docs-src/content/functions/tmpl.yml b/docs-src/content/functions/tmpl.yml
index 962fefaf..3bf2f9fa 100644
--- a/docs-src/content/functions/tmpl.yml
+++ b/docs-src/content/functions/tmpl.yml
@@ -3,6 +3,26 @@ title: template functions
preamble: |
Functions for defining or executing templates.
funcs:
+ - name: tmpl.Exec
+ description: |
+ Execute (render) the named template. This is equivalent to using the [`template`](https://golang.org/pkg/text/template/#hdr-Actions) action, except the result is returned as a string.
+
+ This allows for post-processing of templates.
+ pipeline: true
+ arguments:
+ - name: name
+ required: true
+ description: The template's name.
+ - name: context
+ required: false
+ description: The context to use.
+ examples:
+ - |
+ $ gomplate -i '{{define "T1"}}hello, world!{{end}}{{ tmpl.Exec "T1" | strings.ToUpper }}'
+ HELLO, WORLD!
+ - |
+ $ gomplate -i '{{define "T1"}}hello, {{.}}{{end}}{{ tmpl.Exec "T1" "world!" | strings.Title }}'
+ Hello, World!
- name: tmpl.Inline
alias: tpl
description: |
diff --git a/docs/content/functions/tmpl.md b/docs/content/functions/tmpl.md
index 64a0bf64..f6da5f4d 100644
--- a/docs/content/functions/tmpl.md
+++ b/docs/content/functions/tmpl.md
@@ -7,6 +7,39 @@ menu:
Functions for defining or executing templates.
+## `tmpl.Exec`
+
+Execute (render) the named template. This is equivalent to using the [`template`](https://golang.org/pkg/text/template/#hdr-Actions) action, except the result is returned as a string.
+
+This allows for post-processing of templates.
+
+### Usage
+```go
+tmpl.Exec name [context]
+```
+
+```go
+context | tmpl.Exec name
+```
+
+### Arguments
+
+| name | description |
+|------|-------------|
+| `name` | _(required)_ The template's name. |
+| `context` | _(optional)_ The context to use. |
+
+### Examples
+
+```console
+$ gomplate -i '{{define "T1"}}hello, world!{{end}}{{ tmpl.Exec "T1" | strings.ToUpper }}'
+HELLO, WORLD!
+```
+```console
+$ gomplate -i '{{define "T1"}}hello, {{.}}{{end}}{{ tmpl.Exec "T1" "world!" | strings.Title }}'
+Hello, World!
+```
+
## `tmpl.Inline`
**Alias:** `tpl`
diff --git a/tests/integration/tmpl_test.go b/tests/integration/tmpl_test.go
index ca6e4ad8..3ac2e689 100644
--- a/tests/integration/tmpl_test.go
+++ b/tests/integration/tmpl_test.go
@@ -3,13 +3,42 @@
package integration
import (
+ "bytes"
+ "io/ioutil"
+ "os"
+
+ "github.com/gotestyourself/gotestyourself/assert"
+ "github.com/gotestyourself/gotestyourself/fs"
+ "github.com/gotestyourself/gotestyourself/icmd"
. "gopkg.in/check.v1"
)
-type TmplSuite struct{}
+type TmplSuite struct {
+ tmpDir *fs.Dir
+}
var _ = Suite(&TmplSuite{})
+func (s *TmplSuite) SetUpTest(c *C) {
+ s.tmpDir = fs.NewDir(c, "gomplate-tmpltests",
+ fs.WithFiles(map[string]string{
+ "toyaml.tmpl": `{{ . | data.ToYAML }}{{"\n"}}`,
+ "services.yaml": `services:
+ - name: users
+ config:
+ replicas: 2
+ - name: products
+ config:
+ replicas: 18
+`,
+ }),
+ )
+}
+
+func (s *TmplSuite) TearDownTest(c *C) {
+ s.tmpDir.Remove()
+}
+
func (s *TmplSuite) TestInline(c *C) {
inOutTest(c, `
{{- $nums := dict "first" 5 "second" 10 }}
@@ -23,3 +52,60 @@ func (s *TmplSuite) TestInline(c *C) {
{{- template "T" $othernums }}`,
"1510")
}
+
+func (s *TmplSuite) TestExec(c *C) {
+ result := icmd.RunCmd(icmd.Command(GomplateBin,
+ "-i", `{{ tmpl.Exec "Nope" }}`,
+ ))
+ result.Assert(c, icmd.Expected{ExitCode: 1, Err: `template "Nope" not defined`})
+
+ result = icmd.RunCmd(icmd.Command(GomplateBin,
+ "-i", `{{define "T1"}}hello world{{end}}{{ tmpl.Exec "T1" | strings.ToUpper }}`,
+ ))
+ result.Assert(c, icmd.Expected{ExitCode: 0, Out: `HELLO WORLD`})
+
+ result = icmd.RunCmd(icmd.Command(GomplateBin,
+ "-c", "in=stdin:///in.json",
+ "-t", "toyaml="+s.tmpDir.Join("toyaml.tmpl"),
+ "-i", `foo:
+{{ tmpl.Exec "toyaml" .in | strings.Indent 2 }}`,
+ ), func(cmd *icmd.Cmd) {
+ in := bytes.NewBufferString(`{"a":{"nested": "object"},"b":true}`)
+ cmd.Stdin = in
+ })
+ result.Assert(c, icmd.Expected{ExitCode: 0, Out: `foo:
+ a:
+ nested: object
+ b: true
+`})
+
+ outDir := s.tmpDir.Join("out")
+ err := os.MkdirAll(outDir, 0755)
+ if err != nil {
+ assert.NilError(c, err)
+ }
+ result = icmd.RunCmd(icmd.Command(GomplateBin,
+ "-d", "services="+s.tmpDir.Join("services.yaml"),
+ "-i", `{{- define "config" }}{{ .config | data.ToJSONPretty " " }}{{ end }}
+{{- range (ds "services").services -}}
+{{- $outPath := path.Join .name "config.json" }}
+{{- tmpl.Exec "config" . | file.Write $outPath }}
+{{- end -}}`,
+ ), func(cmd *icmd.Cmd) {
+ cmd.Dir = outDir
+ })
+ result.Assert(c, icmd.Expected{ExitCode: 0})
+ assert.Equal(c, "", result.Stdout())
+ assert.Equal(c, "", result.Stderr())
+
+ out, err := ioutil.ReadFile(s.tmpDir.Join("out", "users", "config.json"))
+ assert.NilError(c, err)
+ assert.Equal(c, `{
+ "replicas": 2
+}`, string(out))
+ out, err = ioutil.ReadFile(s.tmpDir.Join("out", "products", "config.json"))
+ assert.NilError(c, err)
+ assert.Equal(c, `{
+ "replicas": 18
+}`, string(out))
+}
diff --git a/tmpl/tmpl.go b/tmpl/tmpl.go
index 59c1a6a0..97ce0fe6 100644
--- a/tmpl/tmpl.go
+++ b/tmpl/tmpl.go
@@ -30,12 +30,33 @@ func (t *Template) Inline(args ...interface{}) (string, error) {
if err != nil {
return "", err
}
+ return t.inline(name, in, ctx)
+}
+
+func (t *Template) inline(name, in string, ctx interface{}) (string, error) {
tmpl, err := t.root.New(name).Parse(in)
if err != nil {
return "", err
}
+ return render(tmpl, ctx)
+}
+
+// Exec - execute (render) a template - this is the built-in `template` action, except with output...
+func (t *Template) Exec(name string, context ...interface{}) (string, error) {
+ ctx := t.defaultCtx
+ if len(context) == 1 {
+ ctx = context[0]
+ }
+ tmpl := t.root.Lookup(name)
+ if tmpl == nil {
+ return "", errors.Errorf(`template "%s" not defined`, name)
+ }
+ return render(tmpl, ctx)
+}
+
+func render(tmpl *template.Template, ctx interface{}) (string, error) {
out := &bytes.Buffer{}
- err = tmpl.Execute(out, ctx)
+ err := tmpl.Execute(out, ctx)
if err != nil {
return "", err
}
diff --git a/tmpl/tmpl_test.go b/tmpl/tmpl_test.go
index 0ed21111..729c99fb 100644
--- a/tmpl/tmpl_test.go
+++ b/tmpl/tmpl_test.go
@@ -67,3 +67,24 @@ func TestParseArgs(t *testing.T) {
assert.Equal(t, "bar", in)
assert.Equal(t, c, ctx)
}
+
+func TestExec(t *testing.T) {
+ root := template.New("root")
+ t1 := root.New("T1")
+ t1.Parse("hello, {{ . }}")
+ tmpl := &Template{
+ defaultCtx: map[string]string{"foo": "bar"},
+ root: root,
+ }
+
+ out, err := tmpl.Exec("T1")
+ assert.NoError(t, err)
+ assert.Equal(t, "hello, map[foo:bar]", out)
+
+ out, err = tmpl.Exec("T1", "world")
+ assert.NoError(t, err)
+ assert.Equal(t, "hello, world", out)
+
+ _, err = tmpl.Exec("bogus")
+ assert.Error(t, err)
+}