github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/html/template/examplefiles_test.go (about) 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 "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. 16 type templateFile struct { 17 name string 18 contents string 19 } 20 21 func 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. 43 func 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 75 func 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. 117 func 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. 165 func 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 }