Handlebars-Net / Handlebars.Net

A real .NET Handlebars engine
MIT License
1.21k stars 213 forks source link

[Question] Compiling a template always returns a string, also when the source is different #487

Open StefH opened 2 years ago

StefH commented 2 years ago

This is probably 'by design', but when calling this c# code:

var template2 = Handlebars.Compile(@"{{x}}");

var data2 = new { x = 1 };

var result2 = template2(data2);

The result is "1" (as a string), but actually it should be 1 (as a integer).

Is there any way to change this? Or is always a string returned?

See example: https://dotnetfiddle.net/b3WCwx

oformaniuk commented 2 years ago

Hello @StefH This is by design. Handlebars is a templating engineering and is designed to work with strings.

StefH commented 2 years ago

OK. I see.

In Scriban, this is supported with Evaluate method.

See https://dotnetfiddle.net/sZGbaX

Maybe this is something which can also be implemented in Handlebars.Net ?

oformaniuk commented 2 years ago

@StefH I do not see a strong need for this. At least as part of main package. In case you need such behavior why not just try to parse it yourself?

StefH commented 2 years ago

I did not read all the code yet.

Can you point me in the right direction where to look?

rexm commented 2 years ago

Type inference is a tough problem once we start to tackle all the corner cases. It’s almost always more appropriate to use a purpose-built library to do that type of conversion… or in most cases, you have to know what type you expect to do something useful with it, so int.TryParse directly (although at this point going through a library like Handlebars is a pretty circuitous route to get to a number, so now I’m curious what your use case is!)

StefH commented 2 years ago

Hello @rexm, for some details on my use-case, see https://github.com/WireMock-Net/WireMock.Net/issues/709

oformaniuk commented 2 years ago

@StefH , based on the example, it looks like you want to use Handlebars as object accessor. This functionality can be build around existing API (e.g. using Extension method). Let's assume the following:

public class EvaluateHelper : IHelperDescriptor<HelperOptions>
{
    private PathInfo Name { get; } = "__evaluate";
    public readonly AsyncLocal<object> Result = new();

    public object Invoke(in HelperOptionsoptions, in Context context, in Arguments arguments)
    {
        return Result.Value = arguments[0];
    }

    public void Invoke(in EncodedTextWriter output, in HelperOptions options, in Context context, in Arguments arguments){
        Result.Value = arguments[0];
    }
}

public static object Evaluate(string template, object data)
{
    var helper = new EvaluateHelper();
    Handlebars.RegisterHelper(helper);
    var t2 = template.TrimStart('{').TrimEnd('}');
    Handlebars.Compile("{{ __evaluate " + t2 + "}}")(data);
    return helper.Result;
}

The code above definitely needs some more optimizations and considerations, but it illustrates the idea well: intercept write operation and extract value as object.