]> Cypherpunks repositories - gostls13.git/commitdiff
html/template: add examples of loading templates from files
authorEmmanuel Odeke <emm.odeke@gmail.com>
Tue, 12 Apr 2016 08:55:14 +0000 (01:55 -0700)
committerAndrew Gerrand <adg@golang.org>
Wed, 13 Apr 2016 02:28:28 +0000 (02:28 +0000)
Adds examples showing loading templates from files and
executing them.

Shows examples:
- Using ParseGlob.
- Using ParseFiles.
- Using helper functions to share and use templates
in different contexts by adding them to an existing
bundle of templates.
- Using a group of driver templates with distinct sets
of helper templates.

Almost all of the code was directly copied from text/template.

Fixes #8500

Change-Id: Ic3d91d5232afc5a1cd2d8cd3d9a5f3b754c64225
Reviewed-on: https://go-review.googlesource.com/21854
Reviewed-by: Andrew Gerrand <adg@golang.org>
src/html/template/examplefiles_test.go [new file with mode: 0644]

diff --git a/src/html/template/examplefiles_test.go b/src/html/template/examplefiles_test.go
new file mode 100644 (file)
index 0000000..60518ae
--- /dev/null
@@ -0,0 +1,226 @@
+// 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"
+       "io/ioutil"
+       "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 := ioutil.TempDir("", "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))
+}