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