| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package template_test |
| |
| import ( |
| "io" |
| "log" |
| "os" |
| "path/filepath" |
| "text/template" |
| ) |
| |
| // templateFile defines the contents of a template to be stored in a file, for testing. |
| type templateFile struct { |
| name string |
| contents string |
| } |
| |
| func createTestDir(files []templateFile) string { |
| dir, err := os.MkdirTemp("", "template") |
| if err != nil { |
| log.Fatal(err) |
| } |
| for _, file := range files { |
| f, err := os.Create(filepath.Join(dir, file.name)) |
| if err != nil { |
| log.Fatal(err) |
| } |
| defer f.Close() |
| _, err = io.WriteString(f, file.contents) |
| if err != nil { |
| log.Fatal(err) |
| } |
| } |
| return dir |
| } |
| |
| // The following example is duplicated in text/template; keep them in sync. |
| |
| // Here we demonstrate loading a set of templates from a directory. |
| func ExampleTemplate_glob() { |
| // Here we create a temporary directory and populate it with our sample |
| // template definition files; usually the template files would already |
| // exist in some location known to the program. |
| dir := createTestDir([]templateFile{ |
| // T0.tmpl is a plain template file that just invokes T1. |
| {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`}, |
| // T1.tmpl defines a template, T1 that invokes T2. |
| {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, |
| // T2.tmpl defines a template T2. |
| {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, |
| }) |
| // Clean up after the test; another quirk of running as an example. |
| defer os.RemoveAll(dir) |
| |
| // pattern is the glob pattern used to find all the template files. |
| pattern := filepath.Join(dir, "*.tmpl") |
| |
| // Here starts the example proper. |
| // T0.tmpl is the first name matched, so it becomes the starting template, |
| // the value returned by ParseGlob. |
| tmpl := template.Must(template.ParseGlob(pattern)) |
| |
| err := tmpl.Execute(os.Stdout, nil) |
| if err != nil { |
| log.Fatalf("template execution: %s", err) |
| } |
| // Output: |
| // T0 invokes T1: (T1 invokes T2: (This is T2)) |
| } |
| |
| // Here we demonstrate loading a set of templates from files in different directories |
| func ExampleTemplate_parsefiles() { |
| // Here we create different temporary directories and populate them with our sample |
| // template definition files; usually the template files would already |
| // exist in some location known to the program. |
| dir1 := createTestDir([]templateFile{ |
| // T1.tmpl is a plain template file that just invokes T2. |
| {"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`}, |
| }) |
| |
| dir2 := createTestDir([]templateFile{ |
| // T2.tmpl defines a template T2. |
| {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, |
| }) |
| |
| // Clean up after the test; another quirk of running as an example. |
| defer func(dirs ...string) { |
| for _, dir := range dirs { |
| os.RemoveAll(dir) |
| } |
| }(dir1, dir2) |
| |
| // Here starts the example proper. |
| // Let's just parse only dir1/T0 and dir2/T2 |
| paths := []string{ |
| filepath.Join(dir1, "T1.tmpl"), |
| filepath.Join(dir2, "T2.tmpl"), |
| } |
| tmpl := template.Must(template.ParseFiles(paths...)) |
| |
| err := tmpl.Execute(os.Stdout, nil) |
| if err != nil { |
| log.Fatalf("template execution: %s", err) |
| } |
| // Output: |
| // T1 invokes T2: (This is T2) |
| } |
| |
| // The following example is duplicated in text/template; keep them in sync. |
| |
| // This example demonstrates one way to share some templates |
| // and use them in different contexts. In this variant we add multiple driver |
| // templates by hand to an existing bundle of templates. |
| func ExampleTemplate_helpers() { |
| // Here we create a temporary directory and populate it with our sample |
| // template definition files; usually the template files would already |
| // exist in some location known to the program. |
| dir := createTestDir([]templateFile{ |
| // T1.tmpl defines a template, T1 that invokes T2. |
| {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, |
| // T2.tmpl defines a template T2. |
| {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, |
| }) |
| // Clean up after the test; another quirk of running as an example. |
| defer os.RemoveAll(dir) |
| |
| // pattern is the glob pattern used to find all the template files. |
| pattern := filepath.Join(dir, "*.tmpl") |
| |
| // Here starts the example proper. |
| // Load the helpers. |
| templates := template.Must(template.ParseGlob(pattern)) |
| // Add one driver template to the bunch; we do this with an explicit template definition. |
| _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}") |
| if err != nil { |
| log.Fatal("parsing driver1: ", err) |
| } |
| // Add another driver template. |
| _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}") |
| if err != nil { |
| log.Fatal("parsing driver2: ", err) |
| } |
| // We load all the templates before execution. This package does not require |
| // that behavior but html/template's escaping does, so it's a good habit. |
| err = templates.ExecuteTemplate(os.Stdout, "driver1", nil) |
| if err != nil { |
| log.Fatalf("driver1 execution: %s", err) |
| } |
| err = templates.ExecuteTemplate(os.Stdout, "driver2", nil) |
| if err != nil { |
| log.Fatalf("driver2 execution: %s", err) |
| } |
| // Output: |
| // Driver 1 calls T1: (T1 invokes T2: (This is T2)) |
| // Driver 2 calls T2: (This is T2) |
| } |
| |
| // The following example is duplicated in text/template; keep them in sync. |
| |
| // This example demonstrates how to use one group of driver |
| // templates with distinct sets of helper templates. |
| func ExampleTemplate_share() { |
| // Here we create a temporary directory and populate it with our sample |
| // template definition files; usually the template files would already |
| // exist in some location known to the program. |
| dir := createTestDir([]templateFile{ |
| // T0.tmpl is a plain template file that just invokes T1. |
| {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"}, |
| // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined |
| {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, |
| }) |
| // Clean up after the test; another quirk of running as an example. |
| defer os.RemoveAll(dir) |
| |
| // pattern is the glob pattern used to find all the template files. |
| pattern := filepath.Join(dir, "*.tmpl") |
| |
| // Here starts the example proper. |
| // Load the drivers. |
| drivers := template.Must(template.ParseGlob(pattern)) |
| |
| // We must define an implementation of the T2 template. First we clone |
| // the drivers, then add a definition of T2 to the template name space. |
| |
| // 1. Clone the helper set to create a new name space from which to run them. |
| first, err := drivers.Clone() |
| if err != nil { |
| log.Fatal("cloning helpers: ", err) |
| } |
| // 2. Define T2, version A, and parse it. |
| _, err = first.Parse("{{define `T2`}}T2, version A{{end}}") |
| if err != nil { |
| log.Fatal("parsing T2: ", err) |
| } |
| |
| // Now repeat the whole thing, using a different version of T2. |
| // 1. Clone the drivers. |
| second, err := drivers.Clone() |
| if err != nil { |
| log.Fatal("cloning drivers: ", err) |
| } |
| // 2. Define T2, version B, and parse it. |
| _, err = second.Parse("{{define `T2`}}T2, version B{{end}}") |
| if err != nil { |
| log.Fatal("parsing T2: ", err) |
| } |
| |
| // Execute the templates in the reverse order to verify the |
| // first is unaffected by the second. |
| err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second") |
| if err != nil { |
| log.Fatalf("second execution: %s", err) |
| } |
| err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first") |
| if err != nil { |
| log.Fatalf("first: execution: %s", err) |
| } |
| |
| // Output: |
| // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B)) |
| // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A)) |
| } |