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