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	"io/ioutil"
10	"log"
11	"os"
12	"path/filepath"
13
14	"github.com/alecthomas/template"
15)
16
17// templateFile defines the contents of a template to be stored in a file, for testing.
18type templateFile struct {
19	name     string
20	contents string
21}
22
23func createTestDir(files []templateFile) string {
24	dir, err := ioutil.TempDir("", "template")
25	if err != nil {
26		log.Fatal(err)
27	}
28	for _, file := range files {
29		f, err := os.Create(filepath.Join(dir, file.name))
30		if err != nil {
31			log.Fatal(err)
32		}
33		defer f.Close()
34		_, err = io.WriteString(f, file.contents)
35		if err != nil {
36			log.Fatal(err)
37		}
38	}
39	return dir
40}
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// This example demonstrates one way to share some templates
75// and use them in different contexts. In this variant we add multiple driver
76// templates by hand to an existing bundle of templates.
77func ExampleTemplate_helpers() {
78	// Here we create a temporary directory and populate it with our sample
79	// template definition files; usually the template files would already
80	// exist in some location known to the program.
81	dir := createTestDir([]templateFile{
82		// T1.tmpl defines a template, T1 that invokes T2.
83		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
84		// T2.tmpl defines a template T2.
85		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
86	})
87	// Clean up after the test; another quirk of running as an example.
88	defer os.RemoveAll(dir)
89
90	// pattern is the glob pattern used to find all the template files.
91	pattern := filepath.Join(dir, "*.tmpl")
92
93	// Here starts the example proper.
94	// Load the helpers.
95	templates := template.Must(template.ParseGlob(pattern))
96	// Add one driver template to the bunch; we do this with an explicit template definition.
97	_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
98	if err != nil {
99		log.Fatal("parsing driver1: ", err)
100	}
101	// Add another driver template.
102	_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
103	if err != nil {
104		log.Fatal("parsing driver2: ", err)
105	}
106	// We load all the templates before execution. This package does not require
107	// that behavior but html/template's escaping does, so it's a good habit.
108	err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
109	if err != nil {
110		log.Fatalf("driver1 execution: %s", err)
111	}
112	err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
113	if err != nil {
114		log.Fatalf("driver2 execution: %s", err)
115	}
116	// Output:
117	// Driver 1 calls T1: (T1 invokes T2: (This is T2))
118	// Driver 2 calls T2: (This is T2)
119}
120
121// This example demonstrates how to use one group of driver
122// templates with distinct sets of helper templates.
123func ExampleTemplate_share() {
124	// Here we create a temporary directory and populate it with our sample
125	// template definition files; usually the template files would already
126	// exist in some location known to the program.
127	dir := createTestDir([]templateFile{
128		// T0.tmpl is a plain template file that just invokes T1.
129		{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
130		// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
131		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
132	})
133	// Clean up after the test; another quirk of running as an example.
134	defer os.RemoveAll(dir)
135
136	// pattern is the glob pattern used to find all the template files.
137	pattern := filepath.Join(dir, "*.tmpl")
138
139	// Here starts the example proper.
140	// Load the drivers.
141	drivers := template.Must(template.ParseGlob(pattern))
142
143	// We must define an implementation of the T2 template. First we clone
144	// the drivers, then add a definition of T2 to the template name space.
145
146	// 1. Clone the helper set to create a new name space from which to run them.
147	first, err := drivers.Clone()
148	if err != nil {
149		log.Fatal("cloning helpers: ", err)
150	}
151	// 2. Define T2, version A, and parse it.
152	_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
153	if err != nil {
154		log.Fatal("parsing T2: ", err)
155	}
156
157	// Now repeat the whole thing, using a different version of T2.
158	// 1. Clone the drivers.
159	second, err := drivers.Clone()
160	if err != nil {
161		log.Fatal("cloning drivers: ", err)
162	}
163	// 2. Define T2, version B, and parse it.
164	_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
165	if err != nil {
166		log.Fatal("parsing T2: ", err)
167	}
168
169	// Execute the templates in the reverse order to verify the
170	// first is unaffected by the second.
171	err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
172	if err != nil {
173		log.Fatalf("second execution: %s", err)
174	}
175	err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
176	if err != nil {
177		log.Fatalf("first: execution: %s", err)
178	}
179
180	// Output:
181	// T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
182	// T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
183}
184