examplefiles_test.go (7726B)
1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 //go:build go1.13
6 // +build go1.13
7
8 package template_test
9
10 import (
11 "io"
12 "log"
13 "os"
14 "path/filepath"
15
16 template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
17 )
18
19 // templateFile defines the contents of a template to be stored in a file, for testing.
20 type templateFile struct {
21 name string
22 contents string
23 }
24
25 func createTestDir(files []templateFile) string {
26 dir, err := os.MkdirTemp("", "template")
27 if err != nil {
28 log.Fatal(err)
29 }
30 for _, file := range files {
31 f, err := os.Create(filepath.Join(dir, file.name))
32 if err != nil {
33 log.Fatal(err)
34 }
35 defer f.Close()
36 _, err = io.WriteString(f, file.contents)
37 if err != nil {
38 log.Fatal(err)
39 }
40 }
41 return dir
42 }
43
44 // The following example is duplicated in text/template; keep them in sync.
45
46 // Here we demonstrate loading a set of templates from a directory.
47 func ExampleTemplate_glob() {
48 // Here we create a temporary directory and populate it with our sample
49 // template definition files; usually the template files would already
50 // exist in some location known to the program.
51 dir := createTestDir([]templateFile{
52 // T0.tmpl is a plain template file that just invokes T1.
53 {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
54 // T1.tmpl defines a template, T1 that invokes T2.
55 {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
56 // T2.tmpl defines a template T2.
57 {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
58 })
59 // Clean up after the test; another quirk of running as an example.
60 defer os.RemoveAll(dir)
61
62 // pattern is the glob pattern used to find all the template files.
63 pattern := filepath.Join(dir, "*.tmpl")
64
65 // Here starts the example proper.
66 // T0.tmpl is the first name matched, so it becomes the starting template,
67 // the value returned by ParseGlob.
68 tmpl := template.Must(template.ParseGlob(pattern))
69
70 err := tmpl.Execute(os.Stdout, nil)
71 if err != nil {
72 log.Fatalf("template execution: %s", err)
73 }
74 // Output:
75 // T0 invokes T1: (T1 invokes T2: (This is T2))
76 }
77
78 // Here we demonstrate loading a set of templates from files in different directories
79 func ExampleTemplate_parsefiles() {
80 // Here we create different temporary directories and populate them with our sample
81 // template definition files; usually the template files would already
82 // exist in some location known to the program.
83 dir1 := createTestDir([]templateFile{
84 // T1.tmpl is a plain template file that just invokes T2.
85 {"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},
86 })
87
88 dir2 := createTestDir([]templateFile{
89 // T2.tmpl defines a template T2.
90 {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
91 })
92
93 // Clean up after the test; another quirk of running as an example.
94 defer func(dirs ...string) {
95 for _, dir := range dirs {
96 os.RemoveAll(dir)
97 }
98 }(dir1, dir2)
99
100 // Here starts the example proper.
101 // Let's just parse only dir1/T0 and dir2/T2
102 paths := []string{
103 filepath.Join(dir1, "T1.tmpl"),
104 filepath.Join(dir2, "T2.tmpl"),
105 }
106 tmpl := template.Must(template.ParseFiles(paths...))
107
108 err := tmpl.Execute(os.Stdout, nil)
109 if err != nil {
110 log.Fatalf("template execution: %s", err)
111 }
112 // Output:
113 // T1 invokes T2: (This is T2)
114 }
115
116 // The following example is duplicated in text/template; keep them in sync.
117
118 // This example demonstrates one way to share some templates
119 // and use them in different contexts. In this variant we add multiple driver
120 // templates by hand to an existing bundle of templates.
121 func ExampleTemplate_helpers() {
122 // Here we create a temporary directory and populate it with our sample
123 // template definition files; usually the template files would already
124 // exist in some location known to the program.
125 dir := createTestDir([]templateFile{
126 // T1.tmpl defines a template, T1 that invokes T2.
127 {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
128 // T2.tmpl defines a template T2.
129 {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
130 })
131 // Clean up after the test; another quirk of running as an example.
132 defer os.RemoveAll(dir)
133
134 // pattern is the glob pattern used to find all the template files.
135 pattern := filepath.Join(dir, "*.tmpl")
136
137 // Here starts the example proper.
138 // Load the helpers.
139 templates := template.Must(template.ParseGlob(pattern))
140 // Add one driver template to the bunch; we do this with an explicit template definition.
141 _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
142 if err != nil {
143 log.Fatal("parsing driver1: ", err)
144 }
145 // Add another driver template.
146 _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
147 if err != nil {
148 log.Fatal("parsing driver2: ", err)
149 }
150 // We load all the templates before execution. This package does not require
151 // that behavior but html/template's escaping does, so it's a good habit.
152 err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
153 if err != nil {
154 log.Fatalf("driver1 execution: %s", err)
155 }
156 err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
157 if err != nil {
158 log.Fatalf("driver2 execution: %s", err)
159 }
160 // Output:
161 // Driver 1 calls T1: (T1 invokes T2: (This is T2))
162 // Driver 2 calls T2: (This is T2)
163 }
164
165 // The following example is duplicated in text/template; keep them in sync.
166
167 // This example demonstrates how to use one group of driver
168 // templates with distinct sets of helper templates.
169 func ExampleTemplate_share() {
170 // Here we create a temporary directory and populate it with our sample
171 // template definition files; usually the template files would already
172 // exist in some location known to the program.
173 dir := createTestDir([]templateFile{
174 // T0.tmpl is a plain template file that just invokes T1.
175 {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
176 // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
177 {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
178 })
179 // Clean up after the test; another quirk of running as an example.
180 defer os.RemoveAll(dir)
181
182 // pattern is the glob pattern used to find all the template files.
183 pattern := filepath.Join(dir, "*.tmpl")
184
185 // Here starts the example proper.
186 // Load the drivers.
187 drivers := template.Must(template.ParseGlob(pattern))
188
189 // We must define an implementation of the T2 template. First we clone
190 // the drivers, then add a definition of T2 to the template name space.
191
192 // 1. Clone the helper set to create a new name space from which to run them.
193 first, err := drivers.Clone()
194 if err != nil {
195 log.Fatal("cloning helpers: ", err)
196 }
197 // 2. Define T2, version A, and parse it.
198 _, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
199 if err != nil {
200 log.Fatal("parsing T2: ", err)
201 }
202
203 // Now repeat the whole thing, using a different version of T2.
204 // 1. Clone the drivers.
205 second, err := drivers.Clone()
206 if err != nil {
207 log.Fatal("cloning drivers: ", err)
208 }
209 // 2. Define T2, version B, and parse it.
210 _, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
211 if err != nil {
212 log.Fatal("parsing T2: ", err)
213 }
214
215 // Execute the templates in the reverse order to verify the
216 // first is unaffected by the second.
217 err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
218 if err != nil {
219 log.Fatalf("second execution: %s", err)
220 }
221 err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
222 if err != nil {
223 log.Fatalf("first: execution: %s", err)
224 }
225
226 // Output:
227 // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
228 // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
229 }