Handlebars-Net/Handlebars.Net

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

StefH opened this issue · 7 comments

StefH commented

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

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

StefH commented

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 ?

@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

I did not read all the code yet.

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

rexm commented

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

Hello @rexm, for some details on my use-case, see WireMock-Net/WireMock.Net#709

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