mirror of
https://github.com/golang/go
synced 2024-11-19 13:54:56 -07:00
exp/template: doc and API changes suggested by rsc.
- template invocation is by string constant only. - NewSet is gone. - no global Funcs - writer is now first arg to Execute R=rsc, r CC=golang-dev https://golang.org/cl/4700043
This commit is contained in:
parent
2e9388e321
commit
7aa1a1a64d
@ -33,13 +33,15 @@ data, defined in detail below.
|
|||||||
is copied to the output.
|
is copied to the output.
|
||||||
|
|
||||||
{{if pipeline}} T1 {{end}}
|
{{if pipeline}} T1 {{end}}
|
||||||
If the value of the pipeline is the "zero value" (see below) for
|
If the value of the pipeline is empty, no output is generated;
|
||||||
its type, no output is generated; otherwise, T1 is executed.
|
otherwise, T1 is executed. The empty values are false, 0, any
|
||||||
|
nil pointer or interface value, and any array, slice, map, or
|
||||||
|
string of length zero.
|
||||||
Dot is unaffected.
|
Dot is unaffected.
|
||||||
|
|
||||||
{{if pipeline}} T1 {{else}} T0 {{end}}
|
{{if pipeline}} T1 {{else}} T0 {{end}}
|
||||||
If the value of the pipeline is the zero value for its type, T0
|
If the value of the pipeline is empty, T0 is executed;
|
||||||
is executed; otherwise, T1 is executed. Dot is unaffected.
|
otherwise, T1 is executed. Dot is unaffected.
|
||||||
|
|
||||||
{{range pipeline}} T1 {{end}}
|
{{range pipeline}} T1 {{end}}
|
||||||
The value of the pipeline must be an array, slice, or map. If
|
The value of the pipeline must be an array, slice, or map. If
|
||||||
@ -53,29 +55,22 @@ data, defined in detail below.
|
|||||||
T0 is executed; otherwise, dot is set to the successive elements
|
T0 is executed; otherwise, dot is set to the successive elements
|
||||||
of the array, slice, or map and T1 is executed.
|
of the array, slice, or map and T1 is executed.
|
||||||
|
|
||||||
{{template argument}}
|
{{template "name"}}
|
||||||
If the value of the argument is a string, the template with that
|
The template with the specified name is executed with nil data.
|
||||||
name is executed with nil data. If the value of arg is of type
|
|
||||||
*Template, that template is executed.
|
|
||||||
|
|
||||||
{{template argument pipeline}}
|
{{template "name" pipeline}}
|
||||||
If the value of the argument is a string, the template with that
|
The template with the specified name is executed with dot set
|
||||||
name is executed with data set to the value of the pipeline. If
|
to the value of the pipeline.
|
||||||
the value of arg is of type *Template, that template is
|
|
||||||
executed.
|
|
||||||
|
|
||||||
{{with pipeline}} T1 {{end}}
|
{{with pipeline}} T1 {{end}}
|
||||||
If the value of the pipeline is the zero value for its type, no
|
If the value of the pipeline is empty, no output is generated;
|
||||||
output is generated; otherwise, dot is set to the value of the
|
otherwise, dot is set to the value of the pipeline and T1 is
|
||||||
pipeline and T1 is executed.
|
executed.
|
||||||
|
|
||||||
{{with pipeline}} T1 {{else}} T0 {{end}}
|
{{with pipeline}} T1 {{else}} T0 {{end}}
|
||||||
If the value of the pipeline is the zero value for its type, dot
|
If the value of the pipeline is empty, dot is unaffected and T0
|
||||||
is unaffected and T0 is executed; otherwise, dot is set to the
|
is executed; otherwise, dot is set to the value of the pipeline
|
||||||
value of the pipeline and T1 is executed.
|
and T1 is executed.
|
||||||
|
|
||||||
"Zero value" means the true zero value in Go terms. Also, for arrays, slices,
|
|
||||||
maps, and strings, any value v with len(v)==0 counts as a zero value.
|
|
||||||
|
|
||||||
Arguments
|
Arguments
|
||||||
|
|
||||||
@ -106,12 +101,12 @@ An argument is a simple value, denoted by one of the following.
|
|||||||
such as
|
such as
|
||||||
.Method
|
.Method
|
||||||
The result is the value of invoking the method with dot as the
|
The result is the value of invoking the method with dot as the
|
||||||
receiver, dot.Method(). Such methods must have one return value (of
|
receiver, dot.Method(). Such a method must have one return value (of
|
||||||
any type) or two return values, the second of which is an os.Error.
|
any type) or two return values, the second of which is an os.Error.
|
||||||
If it has two and the returned error is non-nil, execution terminates
|
If it has two and the returned error is non-nil, execution terminates
|
||||||
and that error is returned to the caller as the value of Execute.
|
and an error is returned to the caller as the value of Execute.
|
||||||
Method invocations may be chained, but only the last element of
|
Method invocations may be chained, but only the last element of
|
||||||
the chain may be a method; other others must be struct fields:
|
the chain may be a method; others must be struct fields:
|
||||||
.Field1.Field2.Method
|
.Field1.Field2.Method
|
||||||
Methods can also be evaluated on variables, including chaining:
|
Methods can also be evaluated on variables, including chaining:
|
||||||
$x.Field1.Method
|
$x.Field1.Method
|
||||||
@ -173,7 +168,7 @@ All produce the quoted word "output":
|
|||||||
A string constant.
|
A string constant.
|
||||||
{{`"output"`}}
|
{{`"output"`}}
|
||||||
A raw string constant.
|
A raw string constant.
|
||||||
{{printf "%q" output}}
|
{{printf "%q" "output"}}
|
||||||
A function call.
|
A function call.
|
||||||
{{"output" | printf "%q"}}
|
{{"output" | printf "%q"}}
|
||||||
A function call whose final argument comes from the previous
|
A function call whose final argument comes from the previous
|
||||||
@ -182,14 +177,12 @@ All produce the quoted word "output":
|
|||||||
A more elaborate call.
|
A more elaborate call.
|
||||||
{{"output" | printf "%s" | printf "%q"}}
|
{{"output" | printf "%s" | printf "%q"}}
|
||||||
A longer chain.
|
A longer chain.
|
||||||
{{$x := "output" | printf "%s" | printf "%q"}}
|
|
||||||
An unused variables captures the output.
|
|
||||||
{{with "output"}}{{printf "%q" .}}{{end}}
|
{{with "output"}}{{printf "%q" .}}{{end}}
|
||||||
A with action using dot.
|
A with action using dot.
|
||||||
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
|
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
|
||||||
A with action creates and uses a variable.
|
A with action that creates and uses a variable.
|
||||||
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
|
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
|
||||||
A with action uses the variable in another action.
|
A with action that uses the variable in another action.
|
||||||
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
|
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
|
||||||
The same, but pipelined.
|
The same, but pipelined.
|
||||||
|
|
||||||
@ -230,10 +223,10 @@ be true.
|
|||||||
|
|
||||||
Template sets
|
Template sets
|
||||||
|
|
||||||
All templates are named by a string specified when they are created. A template
|
Each template is named by a string specified when it is created. A template may
|
||||||
may use a template invocation to instantiate another template directly or by its
|
use a template invocation to instantiate another template directly or by its
|
||||||
name; see the explanation of the template action above. The name of a template
|
name; see the explanation of the template action above. The name is looked up
|
||||||
is looked up in the template set active during the invocation.
|
in the template set active during the invocation.
|
||||||
|
|
||||||
If no template invocation actions occur in the template, the issue of template
|
If no template invocation actions occur in the template, the issue of template
|
||||||
sets can be ignored. If it does contain invocations, though, a set must be
|
sets can be ignored. If it does contain invocations, though, a set must be
|
||||||
@ -241,10 +234,9 @@ defined in which to look up the names.
|
|||||||
|
|
||||||
There are two ways to construct template sets.
|
There are two ways to construct template sets.
|
||||||
|
|
||||||
The first is to use the Parse method of Set to create a set of named templates
|
The first is to use a Set's Parse method to create a set of named templates from
|
||||||
by reading a single string defining multiple templates. The syntax of the
|
a single input defining multiple templates. The syntax of the definitions is to
|
||||||
definitions is to surround each template declaration with a define and end
|
surround each template declaration with a define and end action.
|
||||||
action; those actions are discarded after parsing.
|
|
||||||
|
|
||||||
The define action names the template being created by providing a string
|
The define action names the template being created by providing a string
|
||||||
constant. Here is a simple example of input to Set.Parse:
|
constant. Here is a simple example of input to Set.Parse:
|
||||||
@ -256,14 +248,14 @@ constant. Here is a simple example of input to Set.Parse:
|
|||||||
This defines two templates, T1 and T2, and a third T3 that invokes the other two
|
This defines two templates, T1 and T2, and a third T3 that invokes the other two
|
||||||
when it is executed.
|
when it is executed.
|
||||||
|
|
||||||
The second way to build a template set is to use the Add method of Set to bind
|
The second way to build a template set is to use Set's Add method to add
|
||||||
a template to a set. A template may be bound to multiple sets.
|
a template to a set. A template may be bound to multiple sets.
|
||||||
|
|
||||||
Set.Parse may be called multiple times on different inputs to construct the set.
|
Set.Parse may be called multiple times on different inputs to construct the set.
|
||||||
Two sets may therefore be constructed with a common base set of templates plus,
|
Two sets may therefore be constructed with a common base set of templates plus,
|
||||||
through a second Parse call each, specializations for some elements.
|
through a second Parse call each, specializations for some elements.
|
||||||
|
|
||||||
When templates are executed via Template.Execute, no set is defined and so no
|
When a template is executed via Template.Execute, no set is defined and so no
|
||||||
template invocations are possible. The method Template.ExecuteInSet provides a
|
template invocations are possible. The method Template.ExecuteInSet provides a
|
||||||
way to specify a template set when executing a template directly.
|
way to specify a template set when executing a template directly.
|
||||||
|
|
||||||
|
@ -220,27 +220,12 @@ func (s *state) walkRange(dot reflect.Value, r *rangeNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) walkTemplate(dot reflect.Value, t *templateNode) {
|
func (s *state) walkTemplate(dot reflect.Value, t *templateNode) {
|
||||||
// Can't use evalArg because there are two types we expect.
|
|
||||||
arg := s.evalEmptyInterface(dot, t.name)
|
|
||||||
if !arg.IsValid() {
|
|
||||||
s.errorf("invalid value in template invocation; expected string or *Template")
|
|
||||||
}
|
|
||||||
var tmpl *Template
|
|
||||||
if arg.Type() == reflect.TypeOf((*Template)(nil)) {
|
|
||||||
tmpl = arg.Interface().(*Template)
|
|
||||||
if tmpl == nil {
|
|
||||||
s.errorf("nil template")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.validateType(arg, reflect.TypeOf(""))
|
|
||||||
name := arg.String()
|
|
||||||
if s.set == nil {
|
if s.set == nil {
|
||||||
s.errorf("no set defined in which to invoke template named %q", name)
|
s.errorf("no set defined in which to invoke template named %q", t.name)
|
||||||
}
|
}
|
||||||
tmpl = s.set.tmpl[name]
|
tmpl := s.set.tmpl[t.name]
|
||||||
if tmpl == nil {
|
if tmpl == nil {
|
||||||
s.errorf("template %q not in set", name)
|
s.errorf("template %q not in set", t.name)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
defer s.pop(s.mark())
|
defer s.pop(s.mark())
|
||||||
dot = s.evalPipeline(dot, t.pipe)
|
dot = s.evalPipeline(dot, t.pipe)
|
||||||
|
@ -448,13 +448,13 @@ func TestTree(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
set := NewSet()
|
set := new(Set)
|
||||||
err := set.Parse(treeTemplate)
|
err := set.Parse(treeTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("parse error:", err)
|
t.Fatal("parse error:", err)
|
||||||
}
|
}
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
err = set.Execute("tree", &b, tree)
|
err = set.Execute(&b, "tree", tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("exec error:", err)
|
t.Fatal("exec error:", err)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
// FuncMap is the type of the map defining the mapping from names to functions.
|
// FuncMap is the type of the map defining the mapping from names to functions.
|
||||||
// Each function must have either a single return value, or two return values of
|
// Each function must have either a single return value, or two return values of
|
||||||
// which the second has type os.Error. If the second argument evaluates to non-nil
|
// which the second has type os.Error. If the second argument evaluates to non-nil
|
||||||
// during execution, execution terminates and the error is returned by Execute.
|
// during execution, execution terminates and Execute returns an error.
|
||||||
type FuncMap map[string]interface{}
|
type FuncMap map[string]interface{}
|
||||||
|
|
||||||
var funcs = map[string]reflect.Value{
|
var funcs = map[string]reflect.Value{
|
||||||
@ -33,13 +33,6 @@ var funcs = map[string]reflect.Value{
|
|||||||
"println": reflect.ValueOf(fmt.Sprintln),
|
"println": reflect.ValueOf(fmt.Sprintln),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funcs adds to the global function map the elements of the
|
|
||||||
// argument map. It panics if a value in the map is not a function
|
|
||||||
// with appropriate return type.
|
|
||||||
func Funcs(funcMap FuncMap) {
|
|
||||||
addFuncs(funcs, funcMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
||||||
func addFuncs(values map[string]reflect.Value, funcMap FuncMap) {
|
func addFuncs(values map[string]reflect.Value, funcMap FuncMap) {
|
||||||
for name, fn := range funcMap {
|
for name, fn := range funcMap {
|
||||||
|
@ -42,17 +42,15 @@ func (t *Template) MustParseFile(filename string) *Template {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseFile is a helper function that creates a new Template and parses
|
// ParseFile creates a new Template and parses the template definition from
|
||||||
// the template definition from the named file.
|
// the named file. The template name is the base name of the file.
|
||||||
// The template name is the base name of the file.
|
|
||||||
func ParseFile(filename string) (*Template, os.Error) {
|
func ParseFile(filename string) (*Template, os.Error) {
|
||||||
t := New(filepath.Base(filename))
|
t := New(filepath.Base(filename))
|
||||||
return t, t.ParseFile(filename)
|
return t, t.ParseFile(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustParseFile is a helper function that creates a new Template and parses
|
// MustParseFile creates a new Template and parses the template definition
|
||||||
// the template definition from the named file.
|
// from the named file. The template name is the base name of the file.
|
||||||
// The template name is the base name of the file.
|
|
||||||
// It panics if the file cannot be read or the template cannot be parsed.
|
// It panics if the file cannot be read or the template cannot be parsed.
|
||||||
func MustParseFile(filename string) *Template {
|
func MustParseFile(filename string) *Template {
|
||||||
return New(filepath.Base(filename)).MustParseFile(filename)
|
return New(filepath.Base(filename)).MustParseFile(filename)
|
||||||
@ -85,16 +83,16 @@ func (s *Set) MustParseFile(filename string) *Set {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSetFile is a helper function that creates a new Set and parses
|
// ParseSetFile creates a new Set and parses the set definition from the
|
||||||
// the set definition from the named file.
|
// named file.
|
||||||
func ParseSetFile(filename string) (*Set, os.Error) {
|
func ParseSetFile(filename string) (*Set, os.Error) {
|
||||||
s := NewSet()
|
s := new(Set)
|
||||||
return s, s.ParseFile(filename)
|
return s, s.ParseFile(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustParseSetFile is a helper function that creates a new Set and parses
|
// MustParseSetFile creates a new Set and parses the set definition from the
|
||||||
// the set definition from the named file.
|
// named file.
|
||||||
// It panics if the file cannot be read or the set cannot be parsed.
|
// It panics if the file cannot be read or the set cannot be parsed.
|
||||||
func MustParseSetFile(filename string) *Set {
|
func MustParseSetFile(filename string) *Set {
|
||||||
return NewSet().MustParseFile(filename)
|
return new(Set).MustParseFile(filename)
|
||||||
}
|
}
|
||||||
|
@ -477,19 +477,19 @@ func (r *rangeNode) String() string {
|
|||||||
type templateNode struct {
|
type templateNode struct {
|
||||||
nodeType
|
nodeType
|
||||||
line int
|
line int
|
||||||
name node
|
name string
|
||||||
pipe *pipeNode
|
pipe *pipeNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTemplate(line int, name node, pipe *pipeNode) *templateNode {
|
func newTemplate(line int, name string, pipe *pipeNode) *templateNode {
|
||||||
return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipe: pipe}
|
return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipe: pipe}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *templateNode) String() string {
|
func (t *templateNode) String() string {
|
||||||
if t.pipe == nil {
|
if t.pipe == nil {
|
||||||
return fmt.Sprintf("{{template %s}}", t.name)
|
return fmt.Sprintf("{{template %q}}", t.name)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("{{template %s %s}}", t.name, t.pipe)
|
return fmt.Sprintf("{{template %q %s}}", t.name, t.pipe)
|
||||||
}
|
}
|
||||||
|
|
||||||
// withNode represents a {{with}} action and its commands.
|
// withNode represents a {{with}} action and its commands.
|
||||||
@ -523,9 +523,9 @@ func New(name string) *Template {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funcs adds to the template's function map the elements of the
|
// Funcs adds the elements of the argument map to the template's function
|
||||||
// argument map. It panics if a value in the map is not a function
|
// map. It panics if a value in the map is not a function with appropriate
|
||||||
// with appropriate return type.
|
// return type.
|
||||||
// The return value is the template, so calls can be chained.
|
// The return value is the template, so calls can be chained.
|
||||||
func (t *Template) Funcs(funcMap FuncMap) *Template {
|
func (t *Template) Funcs(funcMap FuncMap) *Template {
|
||||||
addFuncs(t.funcs, funcMap)
|
addFuncs(t.funcs, funcMap)
|
||||||
@ -800,25 +800,14 @@ func (t *Template) elseControl() node {
|
|||||||
// Template keyword is past. The name must be something that can evaluate
|
// Template keyword is past. The name must be something that can evaluate
|
||||||
// to a string.
|
// to a string.
|
||||||
func (t *Template) templateControl() node {
|
func (t *Template) templateControl() node {
|
||||||
var name node
|
var name string
|
||||||
switch token := t.next(); token.typ {
|
switch token := t.next(); token.typ {
|
||||||
case itemIdentifier:
|
|
||||||
if _, ok := findFunction(token.val, t, t.set); !ok {
|
|
||||||
t.errorf("function %q not defined", token.val)
|
|
||||||
}
|
|
||||||
name = newIdentifier(token.val)
|
|
||||||
case itemDot:
|
|
||||||
name = newDot()
|
|
||||||
case itemVariable:
|
|
||||||
name = t.useVar(token.val)
|
|
||||||
case itemField:
|
|
||||||
name = newField(token.val)
|
|
||||||
case itemString, itemRawString:
|
case itemString, itemRawString:
|
||||||
s, err := strconv.Unquote(token.val)
|
s, err := strconv.Unquote(token.val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
}
|
}
|
||||||
name = newString(s)
|
name = s
|
||||||
default:
|
default:
|
||||||
t.unexpected(token, "template invocation")
|
t.unexpected(token, "template invocation")
|
||||||
}
|
}
|
||||||
|
@ -204,15 +204,13 @@ var parseTests = []parseTest{
|
|||||||
{"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError,
|
{"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError,
|
||||||
`[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false N='a'])]}} [])]`},
|
`[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false N='a'])]}} [])]`},
|
||||||
{"template", "{{template `x`}}", noError,
|
{"template", "{{template `x`}}", noError,
|
||||||
"[{{template S=`x`}}]"},
|
`[{{template "x"}}]`},
|
||||||
{"template", "{{template `x` .Y}}", noError,
|
{"template with arg", "{{template `x` .Y}}", noError,
|
||||||
"[{{template S=`x` [(command: [F=[Y]])]}}]"},
|
`[{{template "x" [(command: [F=[Y]])]}}]`},
|
||||||
{"with", "{{with .X}}hello{{end}}", noError,
|
{"with", "{{with .X}}hello{{end}}", noError,
|
||||||
`[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
|
`[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
|
||||||
{"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
|
{"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
|
||||||
`[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
|
`[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
|
||||||
{"variable in template", "{{with $v := `hi`}}{{template $v}}{{end}}", noError,
|
|
||||||
"[({{with [$v] := [(command: [S=`hi`])]}} [{{template V=[$v]}}])]"},
|
|
||||||
// Errors.
|
// Errors.
|
||||||
{"unclosed action", "hello{{range", hasError, ""},
|
{"unclosed action", "hello{{range", hasError, ""},
|
||||||
{"unmatched end", "{{end}}", hasError, ""},
|
{"unmatched end", "{{end}}", hasError, ""},
|
||||||
@ -223,6 +221,8 @@ var parseTests = []parseTest{
|
|||||||
{"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
|
{"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
|
||||||
{"variable undefined in template", "{{template $v}}", hasError, ""},
|
{"variable undefined in template", "{{template $v}}", hasError, ""},
|
||||||
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
|
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
|
||||||
|
{"template with field ref", "{{template .X}}", hasError, ""},
|
||||||
|
{"template with var", "{{template $v}}", hasError, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
|
@ -14,33 +14,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Set holds a set of related templates that can refer to one another by name.
|
// Set holds a set of related templates that can refer to one another by name.
|
||||||
|
// The zero value represents an empty set.
|
||||||
// A template may be a member of multiple sets.
|
// A template may be a member of multiple sets.
|
||||||
type Set struct {
|
type Set struct {
|
||||||
tmpl map[string]*Template
|
tmpl map[string]*Template
|
||||||
funcs map[string]reflect.Value
|
funcs map[string]reflect.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSet allocates a new, empty template set.
|
func (s *Set) init() {
|
||||||
func NewSet() *Set {
|
if s.tmpl == nil {
|
||||||
return &Set{
|
s.tmpl = make(map[string]*Template)
|
||||||
tmpl: make(map[string]*Template),
|
s.funcs = make(map[string]reflect.Value)
|
||||||
funcs: make(map[string]reflect.Value),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funcs adds to the set's function map the elements of the
|
// Funcs adds the elements of the argument map to the set's function map. It
|
||||||
// argument map. It panics if a value in the map is not a function
|
// panics if a value in the map is not a function with appropriate return
|
||||||
// with appropriate return type.
|
// type.
|
||||||
// The return value is the set, so calls can be chained.
|
// The return value is the set, so calls can be chained.
|
||||||
func (s *Set) Funcs(funcMap FuncMap) *Set {
|
func (s *Set) Funcs(funcMap FuncMap) *Set {
|
||||||
|
s.init()
|
||||||
addFuncs(s.funcs, funcMap)
|
addFuncs(s.funcs, funcMap)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds the argument templates to the set. It panics if the call
|
// Add adds the argument templates to the set. It panics if two templates
|
||||||
// attempts to reuse a name defined in the set.
|
// with the same name are added.
|
||||||
// The return value is the set, so calls can be chained.
|
// The return value is the set, so calls can be chained.
|
||||||
func (s *Set) Add(templates ...*Template) *Set {
|
func (s *Set) Add(templates ...*Template) *Set {
|
||||||
|
s.init()
|
||||||
for _, t := range templates {
|
for _, t := range templates {
|
||||||
if _, ok := s.tmpl[t.name]; ok {
|
if _, ok := s.tmpl[t.name]; ok {
|
||||||
panic(fmt.Errorf("template: %q already defined in set", t.name))
|
panic(fmt.Errorf("template: %q already defined in set", t.name))
|
||||||
@ -54,6 +56,7 @@ func (s *Set) Add(templates ...*Template) *Set {
|
|||||||
// It panics if the call attempts to reuse a name defined in the set.
|
// It panics if the call attempts to reuse a name defined in the set.
|
||||||
// The return value is the set, so calls can be chained.
|
// The return value is the set, so calls can be chained.
|
||||||
func (s *Set) AddSet(set *Set) *Set {
|
func (s *Set) AddSet(set *Set) *Set {
|
||||||
|
s.init()
|
||||||
for _, t := range set.tmpl {
|
for _, t := range set.tmpl {
|
||||||
if _, ok := s.tmpl[t.name]; ok {
|
if _, ok := s.tmpl[t.name]; ok {
|
||||||
panic(fmt.Errorf("template: %q already defined in set", t.name))
|
panic(fmt.Errorf("template: %q already defined in set", t.name))
|
||||||
@ -68,6 +71,7 @@ func (s *Set) AddSet(set *Set) *Set {
|
|||||||
// template is replaced.
|
// template is replaced.
|
||||||
// The return value is the set, so calls can be chained.
|
// The return value is the set, so calls can be chained.
|
||||||
func (s *Set) Union(set *Set) *Set {
|
func (s *Set) Union(set *Set) *Set {
|
||||||
|
s.init()
|
||||||
for _, t := range set.tmpl {
|
for _, t := range set.tmpl {
|
||||||
s.tmpl[t.name] = t
|
s.tmpl[t.name] = t
|
||||||
}
|
}
|
||||||
@ -80,10 +84,9 @@ func (s *Set) Template(name string) *Template {
|
|||||||
return s.tmpl[name]
|
return s.tmpl[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute looks for the named template in the set and then applies that
|
// Execute applies the named template to the specified data object, writing
|
||||||
// template to the specified data object, writing the output to wr. Nested
|
// the output to wr. Nested template invocations will be resolved from the set.
|
||||||
// template invocations will be resolved from the set.
|
func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error {
|
||||||
func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error {
|
|
||||||
tmpl := s.tmpl[name]
|
tmpl := s.tmpl[name]
|
||||||
if tmpl == nil {
|
if tmpl == nil {
|
||||||
return fmt.Errorf("template: no template %q in set", name)
|
return fmt.Errorf("template: no template %q in set", name)
|
||||||
@ -110,6 +113,7 @@ func (s *Set) recover(errp *os.Error) {
|
|||||||
// to the set. If a template is redefined, the element in the set is
|
// to the set. If a template is redefined, the element in the set is
|
||||||
// overwritten with the new definition.
|
// overwritten with the new definition.
|
||||||
func (s *Set) Parse(text string) (err os.Error) {
|
func (s *Set) Parse(text string) (err os.Error) {
|
||||||
|
s.init()
|
||||||
defer s.recover(&err)
|
defer s.recover(&err)
|
||||||
lex := lex("set", text)
|
lex := lex("set", text)
|
||||||
const context = "define clause"
|
const context = "define clause"
|
||||||
|
@ -38,7 +38,7 @@ var setParseTests = []setParseTest{
|
|||||||
|
|
||||||
func TestSetParse(t *testing.T) {
|
func TestSetParse(t *testing.T) {
|
||||||
for _, test := range setParseTests {
|
for _, test := range setParseTests {
|
||||||
set := NewSet()
|
set := new(Set)
|
||||||
err := set.Parse(test.input)
|
err := set.Parse(test.input)
|
||||||
switch {
|
switch {
|
||||||
case err == nil && !test.ok:
|
case err == nil && !test.ok:
|
||||||
@ -82,10 +82,6 @@ var setExecTests = []execTest{
|
|||||||
{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
|
{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
|
||||||
{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
|
{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
|
||||||
{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
|
{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
|
||||||
{"invoke template by field", `{{template .X}}`, "TEXT", tVal, true},
|
|
||||||
{"invoke template by template", `{{template .Tmpl}}`, "test template", tVal, true},
|
|
||||||
{"invoke template by variable", `{{with $t := "x"}}{{template $t}}{{end}}`, "TEXT", tVal, true},
|
|
||||||
{"invalid: invoke template by []int", `{{template .SI}}`, "", tVal, false},
|
|
||||||
|
|
||||||
// User-defined function: test argument evaluator.
|
// User-defined function: test argument evaluator.
|
||||||
{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
|
{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
|
||||||
@ -104,7 +100,7 @@ const setText2 = `
|
|||||||
|
|
||||||
func TestSetExecute(t *testing.T) {
|
func TestSetExecute(t *testing.T) {
|
||||||
// Declare a set with a couple of templates first.
|
// Declare a set with a couple of templates first.
|
||||||
set := NewSet()
|
set := new(Set)
|
||||||
err := set.Parse(setText1)
|
err := set.Parse(setText1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error parsing set: %s", err)
|
t.Fatalf("error parsing set: %s", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user