Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: text/template: allow template and block outputs to be chained #33273

Open
sylr opened this issue Jul 25, 2019 · 11 comments
Open

proposal: text/template: allow template and block outputs to be chained #33273

sylr opened this issue Jul 25, 2019 · 11 comments
Labels
Projects
Milestone

Comments

@sylr
Copy link

@sylr sylr commented Jul 25, 2019

It would be nice to be able to post process the output of the template and block directives, e.g.:

{{ block "letter" . | GzipString | Base64EncodeString }}
Dear {{.Name}},
{{if .Attended}}It was a pleasure to see you at the wedding.
{{else}}It is a shame you couldn't make it to the wedding.{{end}}
{{with .Gift}}Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
{{ end }}
{{ define "letter" }}
Dear {{.Name}},
{{if .Attended}}It was a pleasure to see you at the wedding.
{{else}}It is a shame you couldn't make it to the wedding.{{end}}
{{with .Gift}}Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
{{ end }}
{{ template "letter" . | GzipString | Base64EncodeString }}

Regards.

@gopherbot gopherbot added this to the Proposal milestone Jul 25, 2019
@gopherbot gopherbot added the Proposal label Jul 25, 2019
@alersenkevich

This comment was marked as spam.

Copy link

@alersenkevich alersenkevich commented Jul 25, 2019

Bad. Disrespect

@sylr

This comment has been minimized.

Copy link
Author

@sylr sylr commented Jul 29, 2019

Motivation: Azure/aks-engine#1663

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Aug 27, 2019

/cc @robpike and @adg. I believe the Go 1.6 template blocks were meant to address this need, but maybe it is worth taking a second look to see how well it has done that.

@adg

This comment has been minimized.

Copy link
Contributor

@adg adg commented Sep 2, 2019

In the example provided in this issue, I don't see why the caller wouldn't do the gzipping and base64 encoding in plain Go. Is there some more context that I'm missing?

In the aks-engine PR, I'm not seeing the connection. I also wonder why the author is using text/template to generate JSON; it seems easier and safer to me to use encoding/json.

@sylr can you provide more context as to how these features would benefit you?

@ianlancetaylor ianlancetaylor changed the title proposal: Go 2: text/template allow template and block outputs to be chained proposal: Go 2: text/template: allow template and block outputs to be chained Sep 3, 2019
@sylr

This comment has been minimized.

Copy link
Author

@sylr sylr commented Sep 13, 2019

@adg aks-engine use text/template to generate a yaml string which is used as value in a json object. Only parts of the YAML are gzipped, e.g.:

https://github.com/Azure/aks-engine/blob/master/parts/k8s/cloud-init/nodecustomdata.yml#L6-L9

Some times the template content is not coming from functions but are defined in the templates:

https://github.com/Azure/aks-engine/blob/master/parts/k8s/cloud-init/nodecustomdata.yml#L245-L261

Allowing to chain template and block output would allow to do gzipping directly in the template.

@adg

This comment has been minimized.

Copy link
Contributor

@adg adg commented Sep 16, 2019

I can see how the feature would be useful, but the proposed syntax doesn't make sense. The syntax for a {{template}} block is:

{{template "name" pipeline}}

In the given example

{{ template "letter" . | GzipString | Base64EncodeString }}

the part that says

 . | GzipString | Base64EncodeString

would be considered the pipeline part, when really the dot and the Gzip/Base64 parts are separate. There'd need to be some other way to treat the template invocation as the value, for another pipeline. It's not clear how one might do that.

@sylr

This comment has been minimized.

Copy link
Author

@sylr sylr commented Sep 16, 2019

Yes it would need to change how parameters are handled.

It currently evaluates like this :

{{ template "name" (. | ...) }}

It would evaluate like this:

{{ (template "name" .) | (...) }}

@sylr

This comment has been minimized.

Copy link
Author

@sylr sylr commented Sep 16, 2019

As far as I see things it is the current syntax that does not make sense.

The 3rd parameter of the template statement is implicitly handled as a variadic argument which is confusing.

@adg

This comment has been minimized.

Copy link
Contributor

@adg adg commented Sep 17, 2019

The third argument to the template statement is not a variadic; it's a pipeline, which is just the text/template way of saying "expression". It's consistent with the rest of the grammar.

Any change we make here would need to be compatible with the existing uses of text/template, so changing the meaning of the grammar there is a non-starter.

@sylr

This comment has been minimized.

Copy link
Author

@sylr sylr commented Oct 7, 2019

Then what about a new type of block which takes an Argument rather than a Pipeline and which could be chained, e.g.:

{{ eval "letter" . |  ... }}
@adg

This comment has been minimized.

Copy link
Contributor

@adg adg commented Oct 7, 2019

To achieve that, you can define a function like 'eval' yourself. For example:

		"eval": func(name string, arg interface{}) (string, error) {
			var buf bytes.Buffer
			err := t.ExecuteTemplate(&buf, name, arg)
			return buf.String(), err
		},

The trick is to make that function enclose the template variable itself, for example:

package main

import (
	"bytes"
	"encoding/base64"
	"log"
	"os"
	"text/template"
)

const tmplText = `
{{ define "letter" }}
Dear {{.Name}},
{{if .Attended}}It was a pleasure to see you at the wedding.
{{else}}It is a shame you couldn't make it to the wedding.{{end}}
{{with .Gift}}Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
{{ end }}
{{(eval "letter" .) | base64 }}
`

func main() {
	t := template.New("")
	t.Funcs(template.FuncMap{
		"eval": func(name string, arg interface{}) (string, error) {
			var buf bytes.Buffer
			err := t.ExecuteTemplate(&buf, name, arg)
			return buf.String(), err
		},
		"base64": func(s string) string {
			return base64.StdEncoding.EncodeToString([]byte(s))
		},
	})
	_, err := t.Parse(tmplText)
	if err != nil {
		log.Fatal(err)
	}
	err = t.Execute(os.Stdout, map[string]interface{}{
		"Name":     "Joe",
		"Attended": true,
		"Gift":     "shoes",
	})
	if err != nil {
		log.Fatal(err)
	}
}
@rsc rsc changed the title proposal: Go 2: text/template: allow template and block outputs to be chained proposal: text/template: allow template and block outputs to be chained Dec 4, 2019
@rsc rsc added this to Incoming in Proposals Dec 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Proposals
Incoming
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.