jet icon indicating copy to clipboard operation
jet copied to clipboard

Option to not abort template rendering process

Open kabukky opened this issue 5 years ago • 9 comments

Having to check every variable on whether it exists or not before using it makes Jet templates kind of verbose. Also, front end developers are used to invalid expressions simply returning an empty string (e. g. when using Handlebars) instead of aborting the whole template rendering process.

I was wondering if it would be feasible to disable the default behavior (abort template rendering) when an invalid expression is encountered.

To demonstrate, I've created a PR here: #109

kabukky avatar Mar 05 '19 07:03 kabukky

+1

mamarx avatar Mar 05 '19 14:03 mamarx

I believe this is solved with https://github.com/CloudyKit/jet/pull/152

tooolbox avatar Jul 01 '20 17:07 tooolbox

Mhmm... not really I'd say. This issue asks to silently ignore undefined variables etc by default (or at least by a configurable flag). Try/catch doesn't do that.

sauerbraten avatar Jul 01 '20 17:07 sauerbraten

Gotcha. Well, it seemed like a way to avoid aborting the entire template process when you find an invalid expression. In either case, dunno that it's a great idea to ignore undefined variables; seems like it would make things more buggy.

🤷‍♂️

tooolbox avatar Jul 01 '20 18:07 tooolbox

For user defined templates this is a pretty important feature. You can't guarantee that user's input (either templates or variables) are always accurate. I think an option to use Resolve() vs resolve() and ignore errors makes a lot of sense!

tonyhb avatar Dec 22 '20 01:12 tonyhb

Jet isn't intended to be used with unchecked user input, at least it's definitely not something I would recommend.

The Resolve() vs resolve() proposition sounds interesting, even though the code would fall apart the first time you pass a buggy resolved value to a function or something...

sauerbraten avatar Dec 22 '20 09:12 sauerbraten

I hear you. RE. unchecked user input, was actually attempting to compile this with GOOS=js and GOARCH=wasm to run this in a separate webassembly environment. I'll investigate other things for now :)

tonyhb avatar Dec 22 '20 10:12 tonyhb

For the fun of it, I spent 30 minutes hacking together a feature that allows "custom resolvers". The diff is here:

https://github.com/CloudyKit/jet/compare/master...tonyhb:feature/custom-resolver

This lets us do whatever we want, including skipping errors, getting a list of all variables used in a template, etc.

It adds a single interface - Resovler:

type Resolver interface {
	Resolve(name string) (reflect.Value, error)
}

This allows us to define custom functions overriding default functionality in Resolve. You can always get the default resolver functionality within Runtime by calling runtime.DefaultResolver().

Usage:

type CustomResolver struct {
  default jet.Resolver
}

func (cr *CustomResolver) Resolve(name string) (reflect.Value, error) {
        fmt.Printf("resolving %s\n", name)
        // Here, we can ignore the error from our default resolver, choose to do no resolving at all, etc.
        return cr.default(name)
}

var buf bytes.Buffer
tpl, err := jet.NewSet(jet.NewInMemLoader()).Parse("some-tpl.tpl". source)
runtime := tpl.Runtime()
runtime.WithResolver(&customResolver{default: runtime.DefaultResolver()})
tpl.ExecuteWith(runtime, buf, nil, nil)

I don't like how I have to expose the Runtime() func within templates to get the default resolver at all to be honest, but yeah, like I said - it's a hacky POC to show that interfaces here might help.

tonyhb avatar Dec 22 '20 11:12 tonyhb

Opened #186 to discuss the Resolver direction specifically, so that we don't pollute this issue with off-topic discussions :)

tonyhb avatar Dec 22 '20 11:12 tonyhb