aboutsummaryrefslogtreecommitdiff
path: root/src/html/template
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2015-08-28 15:31:51 +1000
committerAndrew Gerrand <adg@golang.org>2015-09-28 06:01:30 +0000
commit12dfc3bee482f16263ce4673a0cce399127e2a0d (patch)
treeef8f60a50da69a5dfb3c8ab16082b385a59cfbec /src/html/template
parent09c6d13ac248eefd1d47e20457125ab5ac8b3246 (diff)
downloadgo-12dfc3bee482f16263ce4673a0cce399127e2a0d.tar.xz
text/template, html/template: add block keyword and permit template redefinition
This change adds a new "block" keyword that permits the definition of templates inline inside existing templates, and loosens the restriction on template redefinition. Templates may now be redefined, but in the html/template package they may only be redefined before the template is executed (and therefore escaped). The intention is that such inline templates can be redefined by subsequent template definitions, permitting a kind of template "inheritance" or "overlay". (See the example for details.) Fixes #3812 Change-Id: I733cb5332c1c201c235f759cc64333462e70dc27 Reviewed-on: https://go-review.googlesource.com/14005 Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/html/template')
-rw-r--r--src/html/template/clone_test.go12
-rw-r--r--src/html/template/example_test.go36
-rw-r--r--src/html/template/template.go4
3 files changed, 49 insertions, 3 deletions
diff --git a/src/html/template/clone_test.go b/src/html/template/clone_test.go
index c89d22a6f9..a0f1d6a048 100644
--- a/src/html/template/clone_test.go
+++ b/src/html/template/clone_test.go
@@ -78,9 +78,17 @@ func TestClone(t *testing.T) {
Must(t0.Parse(`{{define "lhs"}} ( {{end}}`))
Must(t0.Parse(`{{define "rhs"}} ) {{end}}`))
- // Clone t0 as t4. Redefining the "lhs" template should fail.
+ // Clone t0 as t4. Redefining the "lhs" template should not fail.
t4 := Must(t0.Clone())
- if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil {
+ if _, err := t4.Parse(`{{define "lhs"}} OK {{end}}`); err != nil {
+ t.Error(`redefine "lhs": got err %v want non-nil`, err)
+ }
+ // Cloning t1 should fail as it has been executed.
+ if _, err := t1.Clone(); err == nil {
+ t.Error("cloning t1: got nil err want non-nil")
+ }
+ // Redefining the "lhs" template in t1 should fail as it has been executed.
+ if _, err := t1.Parse(`{{define "lhs"}} OK {{end}}`); err == nil {
t.Error(`redefine "lhs": got nil err want non-nil`)
}
diff --git a/src/html/template/example_test.go b/src/html/template/example_test.go
index a75ceec480..a7c2905098 100644
--- a/src/html/template/example_test.go
+++ b/src/html/template/example_test.go
@@ -9,6 +9,7 @@ import (
"html/template"
"log"
"os"
+ "strings"
)
func Example() {
@@ -120,3 +121,38 @@ func Example_escape() {
// %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E
}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+func ExampleBlock() {
+ const (
+ master = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}`
+ overlay = `{{define "list"}} {{join . ", "}}{{end}} `
+ )
+ var (
+ funcs = template.FuncMap{"join": strings.Join}
+ guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"}
+ )
+ masterTmpl, err := template.New("master").Funcs(funcs).Parse(master)
+ if err != nil {
+ log.Fatal(err)
+ }
+ overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if err := masterTmpl.Execute(os.Stdout, guardians); err != nil {
+ log.Fatal(err)
+ }
+ if err := overlayTmpl.Execute(os.Stdout, guardians); err != nil {
+ log.Fatal(err)
+ }
+ // Output:
+ // Names:
+ // - Gamora
+ // - Groot
+ // - Nebula
+ // - Rocket
+ // - Star-Lord
+ // Names: Gamora, Groot, Nebula, Rocket, Star-Lord
+}
diff --git a/src/html/template/template.go b/src/html/template/template.go
index f9e6e43588..4c38f36e67 100644
--- a/src/html/template/template.go
+++ b/src/html/template/template.go
@@ -18,7 +18,7 @@ import (
// Template is a specialized Template from "text/template" that produces a safe
// HTML document fragment.
type Template struct {
- // Sticky error if escaping fails.
+ // Sticky error if escaping fails, or escapeOK if succeeded.
escapeErr error
// We could embed the text/template field, but it's safer not to because
// we need to keep our version of the name space and the underlying
@@ -170,6 +170,8 @@ func (t *Template) Parse(src string) (*Template, error) {
tmpl := t.set[name]
if tmpl == nil {
tmpl = t.new(name)
+ } else if tmpl.escapeErr != nil {
+ return nil, fmt.Errorf("html/template: cannot redefine %q after it has executed", name)
}
// Restore our record of this text/template to its unescaped original state.
tmpl.escapeErr = nil