diff options
| author | Andrew Gerrand <adg@golang.org> | 2015-08-28 15:31:51 +1000 |
|---|---|---|
| committer | Andrew Gerrand <adg@golang.org> | 2015-09-28 06:01:30 +0000 |
| commit | 12dfc3bee482f16263ce4673a0cce399127e2a0d (patch) | |
| tree | ef8f60a50da69a5dfb3c8ab16082b385a59cfbec /src/html | |
| parent | 09c6d13ac248eefd1d47e20457125ab5ac8b3246 (diff) | |
| download | go-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')
| -rw-r--r-- | src/html/template/clone_test.go | 12 | ||||
| -rw-r--r-- | src/html/template/example_test.go | 36 | ||||
| -rw-r--r-- | src/html/template/template.go | 4 |
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 |
