CloudyKit / jet

Jet template engine
Apache License 2.0
1.25k stars 105 forks source link

Option to not abort template rendering process #110

Open kabukky opened 5 years ago

kabukky commented 5 years ago

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

mamarx commented 5 years ago

+1

tooolbox commented 4 years ago

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

sauerbraten commented 4 years ago

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.

tooolbox commented 4 years ago

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.

🤷‍♂️

tonyhb commented 3 years ago

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!

sauerbraten commented 3 years ago

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...

tonyhb commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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