Skip to content

Commit 3f66d8c

Browse files
odeke-emadg
authored andcommitted
html/template: add examples of loading templates from files
Adds examples showing loading templates from files and executing them. Shows examples: - Using ParseGlob. - Using ParseFiles. - Using helper functions to share and use templates in different contexts by adding them to an existing bundle of templates. - Using a group of driver templates with distinct sets of helper templates. Almost all of the code was directly copied from text/template. Fixes #8500 Change-Id: Ic3d91d5232afc5a1cd2d8cd3d9a5f3b754c64225 Reviewed-on: https://go-review.googlesource.com/21854 Reviewed-by: Andrew Gerrand <[email protected]>
1 parent db5338f commit 3f66d8c

File tree

1 file changed

+226
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)