Using pipeline value for Template name

1,542 views
Skip to first unread message

Andrew Scott

unread,
Jul 27, 2011, 5:24:55 AM7/27/11
to golan...@googlegroups.com
When using the exp/template package is there a way to use a value from a pipeline to define the template to be executed?

For example, if I had the following struct : 

type foo struct {
   Title string
   Tmpl string
}

And 2 different instances of the foo :

f1 := &foo{ 
   Title:"The Title for f1",
   Tmpl:"tmpl1",
}

f2 := &foo{ 
   Title:"The Title for f2",
   Tmpl:"tmpl2",
}

And then a Template set, say :

{{ define "main"}} == MAIN ==
The Title is : {{.Title}}
{{template <some pipeline to .Tmpl> .}}   {{/* rather than using "tmpl1" or "tmpl2" here I want it to execute the value that's in the Tmpl field */}}
== END MAIN =={{end}}

{{define "tmpl1"}}
=== This is the tmpl1 template ===
{{end}}

{{define "tmpl2"}}
=== This is the tmpl2 template ===
{{end}}

So f1 would render :
== MAIN ==
=== This is the tmpl1 template ===
== END MAIN ==

And f2 would render :
== MAIN ==
=== This is the tmpl2 template ===
== END MAIN ==

I've tried to use a variable but hasn't had much success, any help would be really great. 

Andy

  


Andrew Scott

unread,
Jul 27, 2011, 5:26:46 AM7/27/11
to golan...@googlegroups.com
Opps the templates would actually render : 

f1 :
== MAIN ==
The Title is : The Title for f1
=== This is the tmpl1 template ===
== END MAIN ==

f2 :
== MAIN ==
The Title is : The Title for f2
=== This is the tmpl2 template ===
== END MAIN ==

Not a very clear template example, sorry :).

roger peppe

unread,
Jul 27, 2011, 6:37:44 AM7/27/11
to golan...@googlegroups.com
On 27 July 2011 10:24, Andrew Scott <hokapo...@gmail.com> wrote:
> When using the exp/template package is there a way to use a value from a
> pipeline to define the template to be executed?

There was a CL that added this kind of functionality
(http://codereview.appspot.com/4671056/) but
it was reverted soon afterwards by 4700043.

I'm not entirely sure why. Perhaps it's a security
hole, or thwarts potential static analysis.

Andrew Scott

unread,
Jul 27, 2011, 7:28:06 AM7/27/11
to golan...@googlegroups.com
Hi rog, thanks.  I was sure I had seen an example / test that did something similar too. 

For now, I can get around it via some verbose nesting or ifs and variable tests. but it isn't something that I'm that happy doing. 

I have tried to pass it a pointer to template.Template for example : 

func (f *foo) InvokeTmpl(s string) (*template.Template){
   return f.set.Template(s)
}

{{$foo.InvokeTemplate .Tmpl}}

But this just prints the template's buffer. 

I've thought about executing the template in the func but I would have to pass the current io.Writer.  So I'd rather nest my if's for now.

Either way, I've tried a number of different methods for passes a value to the {{template some_name .}} and the only method that doesn't error when the Template is Parsed is when it's followed by a string. 



Andrew Scott

unread,
Jul 27, 2011, 7:41:35 AM7/27/11
to golan...@googlegroups.com
This is how I'm currently achieving what I need, with the type foo as I outlined above :

func(f *foo) TemplateType(s string) bool {
   return s == f.Tmpl
}

And then the template it self looks like this : 

{{if .TemplateType "tmpl1"}}{{template "tmpl1" .}}{{end}}
{{if .TemplateType "tmpl2"}}{{template "tmpl2" .}}{{end}}

It works, and I can add some additional tests to make sure that the template that's about to be executed is in the respective set.  


roger peppe

unread,
Jul 27, 2011, 8:15:19 AM7/27/11
to golan...@googlegroups.com
On 27 July 2011 12:28, Andrew Scott <hokapo...@gmail.com> wrote:
> Hi rog, thanks.  I was sure I had seen an example / test that did something
> similar too.
> For now, I can get around it via some verbose nesting or ifs and variable
> tests. but it isn't something that I'm that happy doing.
> I have tried to pass it a pointer to template.Template for example :
> func (f *foo) InvokeTmpl(s string) (*template.Template){
>    return f.set.Template(s)
> }

this approach could work fine. how about something like this, which
defines a new function to execute a dynamic template value.

package main

import (
"fmt"
"os"
"exp/template"
"bytes"
)

func RunTemplate(x interface{}, t *template.Template) (string, os.Error) {
var buf bytes.Buffer
err := t.Execute(&buf, x)
return buf.String(), err
}

func main() {
x := template.New("root").Funcs(template.FuncMap{"runtemplate":
RunTemplate}).
MustParse(`{{.Foo | runtemplate "first"}} {{.Bar |
runtemplate "second"}}`)
x1 := template.New("x1").MustParse(`x1{{.}}`)
x2 := template.New("x2").MustParse(`{{.}}x2`)

err := x.Execute(os.Stdout, struct{Bar, Foo *template.Template}{x2, x1})
if err != nil {
fmt.Printf("error on execute: %v\n", err)
}
}

Andrew Scott

unread,
Jul 27, 2011, 10:03:04 AM7/27/11
to golan...@googlegroups.com
Thanks rog that worked perfectly.

What I've got it effectively (nb: this is pseudo code)  :

type master struct {
  ...
  set *template.Set
}
func (m *master) loadTemplates(){
                                                                                                                   
    m.set := &template.Set{}                                                                                                                         
    m.Set.Funcs(                                                                                                                                     
        template.FuncMap{                                                                                                                            
            "runtemplate":func(name string, o interface{})(string, os.Error){                                                                        
                return m.runTemplate(name, o)                                                                                                     
            },
        },                                                                                                                                           
    )                                                                                                                                                
                                                                                                                                                     
    err := self.Set.ParseFile(conf.Root + "/master.tmpl")                                                                                               
    if err != nil {                                                                                                                                  
        fmt.Println(err.String())                                                                                                                    
        return                                                                                                                                       
    }                                                                                                                                                
                                                    

func(m *master) runTemplate(name string, o interface{}) (string os.Error){
    var buf bytes.Buffer                                                                                                                             
    err := m.Set.Execute(&buf, name, o)                                                                                                             
    return buf.String(), err         
}

And in the templates I have : 

{{runtemplate .TemplateName .}}  

Need to add some special handler logic for missing templates but it's perfect.

Many thanks.


j_al...@rocketmail.com

unread,
Jul 27, 2011, 10:05:16 AM7/27/11
to golan...@googlegroups.com
{{with $t <some pipleline to .Tmpl>}}{{template <some pipeline to .Tmpl> .}}{{ end }}

This requires CL 270e8bf51d35dd80f91be48efcd4f597fb639f85.

j_al...@rocketmail.com

unread,
Jul 27, 2011, 10:07:05 AM7/27/11
to golan...@googlegroups.com
Oops, I got that wrong.  The correct syntax is:

{{with $t <some pipleline to .Tmpl>}}{{template $t .}}{{ end }}

This requires CL 270e8bf51d35dd80f91be48efcd4f597fb639f85.

j_al...@rocketmail.com

unread,
Jul 27, 2011, 10:08:13 AM7/27/11
to golan...@googlegroups.com
Arghhhh

{{with $t := <some pipleline to .Tmpl>}}{{template $t .}}{{ end }}

Rob 'Commander' Pike

unread,
Jul 27, 2011, 10:11:40 AM7/27/11
to roger peppe, golan...@googlegroups.com

Correct. We want the template language to be statically analyzable so the context of a template's invocation is clear, checkable, and lockdownable. If an invocation point is totally dynamic, this can't be done. Similarly, if a template can belong to multiple sets, its context can differ between sets in a way that would require all sets to be analyzed simultaneously. Since both these constraints are easy to work around if you want to, at the cost of losing those static checks in a higher-level package, it seemed wise to control the situation in the base template implementation. A higher-level package, such as a hypothetical HTML-only wrapper, can guarantee no workarounds more easily if the constraints are clear.

I am still learning.

-rob

Reply all
Reply to author
Forward
0 new messages