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
5package template_test
6
7import (
8	"io"
9	"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 {
22	dir, err := os.MkdirTemp("", "template")
23	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}
226