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: add missingkey=ignore #23488

Closed
sean- opened this issue Jan 19, 2018 · 7 comments
Closed

proposal: text/template: add missingkey=ignore #23488

sean- opened this issue Jan 19, 2018 · 7 comments

Comments

@sean-
Copy link

@sean- sean- commented Jan 19, 2018

I'd like to propose extending text/template to include a missingkey=ignore Option(). Sometimes it is desirable to re-parse the template to find the original value and handle or defer processing until all template variables have been provided. In order to do this, text/template needs to pass through the original, invalid or missing value. Here is what I was thinking:

        data := map[string]int{
                "x": 99,
        }
        tmpl, err := New("t1").Parse("{{.x}} {{.y}}")
        if err != nil {
                t.Fatal(err)
        }
        var b bytes.Buffer
        tmpl.Option("missingkey=ignore")
        b.Reset()
        err = tmpl.Execute(&b, data)
        if err != nil {
                t.Fatal("default:", err)
        }
        want = "99 {{ .y }}"
        got = b.String()
        if got != want {
                t.Errorf("got %q; expected %q", got, want)
        }

Note that in want the extra whitespace between the left and right delimiters that was injected. I'm unsure as to whether or not that is actually more correct vs not adding any additional whitespace.

Because text/template's lexer eats whitespace, this isn't perfect, but it does eventually reduce to the correct value. Specifically:

foo {{- .bar}} comes back as foobaz if the variable bar was assigned the value baz. If, however, bar was not part of the variable map, the result would be foo{{ .bar }}.

This limitation seemed acceptable to me. Thoughts?

@gopherbot
Copy link

@gopherbot gopherbot commented Jan 19, 2018

Change https://golang.org/cl/88596 mentions this issue: text/template: add missingkey=ignore to echo the original ast tree

@bradfitz bradfitz changed the title text/template: add missingkey=ignore proposal: text/template: add missingkey=ignore Jan 20, 2018
@gopherbot gopherbot added this to the Proposal milestone Jan 20, 2018
@gopherbot gopherbot added the Proposal label Jan 20, 2018
@rsc
Copy link
Contributor

@rsc rsc commented Jan 22, 2018

Once you have 99 {{ .y }}, then what are you going to do with it?

Sometimes it is desirable to re-parse the template to find the original value and handle or defer processing until all template variables have been provided

If this means to parse 99 {{ .y }} as if it were a template, what if data were map[string]string and data["x"] = "{{"? Then the output is {{ {{ .y }}. Then what?

@sean-
Copy link
Author

@sean- sean- commented Jan 22, 2018

Once you have 99 {{ .y }}, then what are you going to do with it?

Re-process the output later during the final phase of evaluation. In a system that is making two passes over the templates, I don't want to fail silently or be lossy on the first pass, but also don't want to fail immediately, either.

I was considering shimming in text/template in place of a homegrown template system that allows for the injection of variables that rewrite an object model. Basically:

  1. Read in a text/template and Execute() the template with the data on hand.
  2. Unmarshal the result into a struct.
  3. Evaluate each member of the struct a second time in order to account for late-binding or other data that wasn't available during the Execute() from step 1.

The existing testing framework that I'm extending makes use of two passes over a template before evaluating the final result during the execution of the tests.

The point about data["x"] = "{{" is not lost on me and one of the reasons I don't like this approach. That said, if a user wants a self-rewriting template, I understand that usecase, despite it's possible peril.

In an ideal world there would be a callback or handler that would be called when .y failed to resolve and I could replace the missing token with something application-specific that is unambiguously represented in the AST.

Another more blunt approach is two have two sets of delimiters that are used for each phase of processing. This would result in users needing to rewrite templates, which is problematic and a chore, but not impossible.

Before going this last route, I thought I'd burn a few cycles and see what the interest was in echoing the AST node and re-running the result through the template engine.

@rsc
Copy link
Contributor

@rsc rsc commented Jan 29, 2018

Given that what you want to do with this new feature doesn't work (as you said) and is very hard to specify in any way that's useful, let's not do this.

@rsc rsc closed this Jan 29, 2018
@bogatuadrian
Copy link

@bogatuadrian bogatuadrian commented Nov 25, 2018

Would having an extra supported type besides struct and map that would resolve a field name help? A type that implements something like Get(fieldName string) (interface{}, error). Something similar has been suggested in #6288 (comment) many years ago.

I am happy to open a separate issue if this proposal has a chance of being added to text/template.

@boosh
Copy link

@boosh boosh commented Mar 29, 2019

I don't see why users supplying incompatible data is a good justification for not implementing this feature. The template {{ {{ .y }} would just fail validation on the next attempt to execute it. What's wrong with that?

@boosh
Copy link

@boosh boosh commented Apr 3, 2019

For anyone else looking for this I have implemented a much better solution at https://github.com/sugarkube/texttemplate/ - the change set linked to in this issue didn't support submaps and didn't return the entire tag pipeline (i.e. {{ .a | printf "hi=%s"}} was replaced by just {{ .a }}).

@golang golang locked and limited conversation to collaborators Apr 2, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
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.