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