diff options
| author | Dave Henderson <dhenderson@gmail.com> | 2022-04-09 16:36:58 -0400 |
|---|---|---|
| committer | Dave Henderson <dhenderson@gmail.com> | 2022-04-10 15:27:15 -0400 |
| commit | ae1620810cbd82eadd71abb51ba2fea97cf50a59 (patch) | |
| tree | a907ef15a3959cf1ac7101ea1bc5becc038b6605 | |
| parent | 6c66d16b7ccb32636688c644b670141b5e5341aa (diff) | |
New tmpl.Path/tmpl.PathDir functions
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
| -rw-r--r-- | docs-src/content/functions/tmpl.yml | 39 | ||||
| -rw-r--r-- | docs/content/functions/tmpl.md | 55 | ||||
| -rw-r--r-- | internal/tests/integration/tmpl_test.go | 33 | ||||
| -rw-r--r-- | template.go | 25 | ||||
| -rw-r--r-- | tmpl/tmpl.go | 22 | ||||
| -rw-r--r-- | tmpl/tmpl_test.go | 33 |
6 files changed, 196 insertions, 11 deletions
diff --git a/docs-src/content/functions/tmpl.yml b/docs-src/content/functions/tmpl.yml index 3bf2f9fa..236ceb99 100644 --- a/docs-src/content/functions/tmpl.yml +++ b/docs-src/content/functions/tmpl.yml @@ -55,3 +55,42 @@ funcs: ' hello world goodbye world + - name: tmpl.Path + description: | + Output the path of the current template, if it came from a file. For + inline templates, this will be an empty string. + + Note that if this function is called from a nested template, the path + of the main template will be returned instead. + pipeline: false + rawExamples: + - | + _`subdir/input.tpl`:_ + ``` + this template is in {{ tmpl.Path }} + ``` + + ```console + $ gomplate -f subdir/input.tpl + this template is in subdir/input.tpl + ``` + ``` + - name: tmpl.PathDir + description: | + Output the current template's directory. For inline templates, this will + be an empty string. + + Note that if this function is called from a nested template, the path + of the main template will be used instead. + pipeline: false + rawExamples: + - | + _`subdir/input.tpl`:_ + ``` + this template is in {{ tmpl.Dir }} + ``` + + ```console + $ gomplate -f subdir/input.tpl + this template is in subdir + ``` diff --git a/docs/content/functions/tmpl.md b/docs/content/functions/tmpl.md index 527fa2b6..d7ce870e 100644 --- a/docs/content/functions/tmpl.md +++ b/docs/content/functions/tmpl.md @@ -80,3 +80,58 @@ $ gomplate -i ' hello world goodbye world ``` + +## `tmpl.Path` + +Output the path of the current template, if it came from a file. For +inline templates, this will be an empty string. + +Note that if this function is called from a nested template, the path +of the main template will be returned instead. + +### Usage + +```go +tmpl.Path +``` + + +### Examples + +_`subdir/input.tpl`:_ +``` +this template is in {{ tmpl.Path }} +``` + +```console +$ gomplate -f subdir/input.tpl +this template is in subdir/input.tpl +``` +``` + +## `tmpl.PathDir` + +Output the current template's directory. For inline templates, this will +be an empty string. + +Note that if this function is called from a nested template, the path +of the main template will be used instead. + +### Usage + +```go +tmpl.PathDir +``` + + +### Examples + +_`subdir/input.tpl`:_ +``` +this template is in {{ tmpl.Dir }} +``` + +```console +$ gomplate -f subdir/input.tpl +this template is in subdir +``` diff --git a/internal/tests/integration/tmpl_test.go b/internal/tests/integration/tmpl_test.go index 37d20e17..c35f0167 100644 --- a/internal/tests/integration/tmpl_test.go +++ b/internal/tests/integration/tmpl_test.go @@ -8,7 +8,7 @@ import ( "gotest.tools/v3/fs" ) -func setupTestTmplTest(t *testing.T) *fs.Dir { +func setupTmplTest(t *testing.T) *fs.Dir { tmpDir := fs.NewDir(t, "gomplate-tmpltests", fs.WithFiles(map[string]string{ "toyaml.tmpl": `{{ . | data.ToYAML }}{{"\n"}}`, @@ -21,13 +21,20 @@ func setupTestTmplTest(t *testing.T) *fs.Dir { replicas: 18 `, }), + fs.WithDir("a", + fs.WithFiles(map[string]string{ + "pathtest.tpl": "{{ tmpl.Path }}\n{{ template `nested` }}", + "a.tpl": "{{ tmpl.PathDir }}", + "b.tpl": "{{ tmpl.Path }}", + }), + ), ) t.Cleanup(tmpDir.Remove) return tmpDir } -func TestTmpl_TestInline(t *testing.T) { +func TestTmpl_Inline(t *testing.T) { inOutTest(t, ` {{- $nums := dict "first" 5 "second" 10 }} {{- tpl "{{ add .first .second }}" $nums }}`, @@ -41,8 +48,8 @@ func TestTmpl_TestInline(t *testing.T) { "1510") } -func TestTmpl_TestExec(t *testing.T) { - tmpDir := setupTestTmplTest(t) +func TestTmpl_Exec(t *testing.T) { + tmpDir := setupTmplTest(t) _, _, err := cmd(t, "-i", `{{ tmpl.Exec "Nope" }}`).run() assert.ErrorContains(t, err, "Nope") @@ -89,3 +96,21 @@ func TestTmpl_TestExec(t *testing.T) { "replicas": 18 }`, string(out)) } + +func TestTmpl_Path(t *testing.T) { + tmpDir := setupTmplTest(t) + + o, e, err := cmd(t, + "-f", "a/pathtest.tpl", + "-t", "nested=a/a.tpl", + ).withDir(tmpDir.Path()).run() + assertSuccess(t, o, e, err, "a/pathtest.tpl\na") + + o, e, err = cmd(t, + "-f", "a/a.tpl", + "-f", "a/b.tpl", + "-o", "-", + "-o", "-", + ).withDir(tmpDir.Path()).run() + assertSuccess(t, o, e, err, "aa/b.tpl") +} diff --git a/template.go b/template.go index 9d66dee0..4f4d6d36 100644 --- a/template.go +++ b/template.go @@ -32,13 +32,27 @@ type tplate struct { modeOverride bool } -func addTmplFuncs(f template.FuncMap, root *template.Template, ctx interface{}) { - t := tmpl.New(root, ctx) +func addTmplFuncs(f template.FuncMap, root *template.Template, tctx interface{}, path string) { + t := tmpl.New(root, tctx, path) tns := func() *tmpl.Template { return t } f["tmpl"] = tns f["tpl"] = t.Inline } +// copyFuncMap - copies the template.FuncMap into a new map so we can modify it +// without affecting the original +func copyFuncMap(funcMap template.FuncMap) template.FuncMap { + if funcMap == nil { + return nil + } + + newFuncMap := make(template.FuncMap, len(funcMap)) + for k, v := range funcMap { + newFuncMap[k] = v + } + return newFuncMap +} + func (t *tplate) toGoTemplate(g *gomplate) (tmpl *template.Template, err error) { if g.rootTemplate != nil { tmpl = g.rootTemplate.New(t.name) @@ -47,9 +61,12 @@ func (t *tplate) toGoTemplate(g *gomplate) (tmpl *template.Template, err error) g.rootTemplate = tmpl } tmpl.Option("missingkey=error") + + funcMap := copyFuncMap(g.funcMap) + // the "tmpl" funcs get added here because they need access to the root template and context - addTmplFuncs(g.funcMap, g.rootTemplate, g.tmplctx) - tmpl.Funcs(g.funcMap) + addTmplFuncs(funcMap, g.rootTemplate, g.tmplctx, t.name) + tmpl.Funcs(funcMap) tmpl.Delims(g.leftDelim, g.rightDelim) _, err = tmpl.Parse(t.contents) if err != nil { diff --git a/tmpl/tmpl.go b/tmpl/tmpl.go index e0c46c20..7021b72b 100644 --- a/tmpl/tmpl.go +++ b/tmpl/tmpl.go @@ -3,6 +3,7 @@ package tmpl import ( "bytes" + "path/filepath" "text/template" "github.com/pkg/errors" @@ -12,11 +13,28 @@ import ( type Template struct { root *template.Template defaultCtx interface{} + path string } // New - -func New(root *template.Template, ctx interface{}) *Template { - return &Template{root, ctx} +func New(root *template.Template, tctx interface{}, path string) *Template { + return &Template{root, tctx, path} +} + +// Path - returns the path to the current template if it came from a file. +// An empty string is returned for inline templates. +func (t *Template) Path() (string, error) { + return t.path, nil +} + +// PathDir - returns the directory of the template, if it came from a file. An empty +// string is returned for inline templates. If the template was loaded from the +// current working directory, "." is returned. +func (t *Template) PathDir() (string, error) { + if t.path == "" { + return "", nil + } + return filepath.Dir(t.path), nil } // Inline - a template function to do inline template processing diff --git a/tmpl/tmpl_test.go b/tmpl/tmpl_test.go index 729c99fb..7109a660 100644 --- a/tmpl/tmpl_test.go +++ b/tmpl/tmpl_test.go @@ -29,7 +29,7 @@ func TestInline(t *testing.T) { func TestParseArgs(t *testing.T) { defaultCtx := map[string]string{"hello": "world"} - tmpl := New(nil, defaultCtx) + tmpl := New(nil, defaultCtx, "") name, in, ctx, err := tmpl.parseArgs("foo") assert.NoError(t, err) assert.Equal(t, "<inline>", name) @@ -88,3 +88,34 @@ func TestExec(t *testing.T) { _, err = tmpl.Exec("bogus") assert.Error(t, err) } + +func TestPath(t *testing.T) { + tmpl := New(nil, nil, "") + + p, err := tmpl.Path() + assert.NoError(t, err) + assert.Equal(t, "", p) + + tmpl = New(nil, nil, "foo") + p, err = tmpl.Path() + assert.NoError(t, err) + assert.Equal(t, "foo", p) +} + +func TestPathDir(t *testing.T) { + tmpl := New(nil, nil, "") + + p, err := tmpl.PathDir() + assert.NoError(t, err) + assert.Equal(t, "", p) + + tmpl = New(nil, nil, "foo") + p, err = tmpl.PathDir() + assert.NoError(t, err) + assert.Equal(t, ".", p) + + tmpl = New(nil, nil, "foo/bar") + p, err = tmpl.PathDir() + assert.NoError(t, err) + assert.Equal(t, "foo", p) +} |
