1// Copyright 2012 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// Here we demonstrate loading a set of templates from a directory.
41func ExampleTemplate_glob() {
42	// Here we create a temporary directory and populate it with our sample
43	// template definition files; usually the template files would already
44	// exist in some location known to the program.
45	dir := createTestDir([]templateFile{
46		// T0.tmpl is a plain template file that just invokes T1.
47		{"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
48		// T1.tmpl defines a template, T1 that invokes T2.
49		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
50		// T2.tmpl defines a template T2.
51		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
52	})
53	// Clean up after the test; another quirk of running as an example.
54	defer os.RemoveAll(dir)
55
56	// pattern is the glob pattern used to find all the template files.
57	pattern := filepath.Join(dir, "*.tmpl")
58
59	// Here starts the example proper.
60	// T0.tmpl is the first name matched, so it becomes the starting template,
61	// the value returned by ParseGlob.
62	tmpl := template.Must(template.ParseGlob(pattern))
63
64	err := tmpl.Execute(os.Stdout, nil)
65	if err != nil {
66		log.Fatalf("template execution: %s", err)
67	}
68	// Output:
69	// T0 invokes T1: (T1 invokes T2: (This is T2))
70}
71
72// This example demonstrates one way to share some templates
73// and use them in different contexts. In this variant we add multiple driver
74// templates by hand to an existing bundle of templates.
75func ExampleTemplate_helpers() {
76	// Here we create a temporary directory and populate it with our sample
77	// template definition files; usually the template files would already
78	// exist in some location known to the program.
79	dir := createTestDir([]templateFile{
80		// T1.tmpl defines a template, T1 that invokes T2.
81		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
82		// T2.tmpl defines a template T2.
83		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
84	})
85	// Clean up after the test; another quirk of running as an example.
86	defer os.RemoveAll(dir)
87
88	// pattern is the glob pattern used to find all the template files.
89	pattern := filepath.Join(dir, "*.tmpl")
90
91	// Here starts the example proper.
92	// Load the helpers.
93	templates := template.Must(template.ParseGlob(pattern))
94	// Add one driver template to the bunch; we do this with an explicit template definition.
95	_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
96	if err != nil {
97		log.Fatal("parsing driver1: ", err)
98	}
99	// Add another driver template.
100	_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
101	if err != nil {
102		log.Fatal("parsing driver2: ", err)
103	}
104	// We load all the templates before execution. This package does not require
105	// that behavior but html/template's escaping does, so it's a good habit.
106	err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
107	if err != nil {
108		log.Fatalf("driver1 execution: %s", err)
109	}
110	err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
111	if err != nil {
112		log.Fatalf("driver2 execution: %s", err)
113	}
114	// Output:
115	// Driver 1 calls T1: (T1 invokes T2: (This is T2))
116	// Driver 2 calls T2: (This is T2)
117}
118
119// This example demonstrates how to use one group of driver
120// templates with distinct sets of helper templates.
121func ExampleTemplate_share() {
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		// T0.tmpl is a plain template file that just invokes T1.
127		{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
128		// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
129		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "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 drivers.
139	drivers := template.Must(template.ParseGlob(pattern))
140
141	// We must define an implementation of the T2 template. First we clone
142	// the drivers, then add a definition of T2 to the template name space.
143
144	// 1. Clone the helper set to create a new name space from which to run them.
145	first, err := drivers.Clone()
146	if err != nil {
147		log.Fatal("cloning helpers: ", err)
148	}
149	// 2. Define T2, version A, and parse it.
150	_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
151	if err != nil {
152		log.Fatal("parsing T2: ", err)
153	}
154
155	// Now repeat the whole thing, using a different version of T2.
156	// 1. Clone the drivers.
157	second, err := drivers.Clone()
158	if err != nil {
159		log.Fatal("cloning drivers: ", err)
160	}
161	// 2. Define T2, version B, and parse it.
162	_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
163	if err != nil {
164		log.Fatal("parsing T2: ", err)
165	}
166
167	// Execute the templates in the reverse order to verify the
168	// first is unaffected by the second.
169	err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
170	if err != nil {
171		log.Fatalf("second execution: %s", err)
172	}
173	err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
174	if err != nil {
175		log.Fatalf("first: execution: %s", err)
176	}
177
178	// Output:
179	// T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
180	// T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
181}
182