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

TypedObjectAccessor: can you make the TryGetValue() virtual? Or create a TryGetValue on TemplateContext #479

Closed
avataron opened this issue Jan 22, 2023 · 2 comments
Labels

Comments

@avataron
Copy link

Hello,

I'm trying to implement a sort of LazyValue implementation.

I've implemented changed both in
public class MyScriptObject : ScriptObject
and
public class MyTemplateContext : TemplateContext

... works, but as I advance in tests, there are edge cases coming, forcing me to revisit the source code to check what's going on.

For now, I'm stuck when an object is nested. Example, when the LazyValue is a property of a random object:

var model = {
   person = new {
      values = new {
          lazyValue1 = new LazyValue<object>(() => 1234), //LazyValue is a custom lazy handler, works similar to Lazy
      }
   }
}
//template: {{ person.values.lazyValue1 }}

In the case above, the ScriptObject, which give us the necessary control, can't manage this because the case is out of it's scope.
That is managed by the TemplateContext AND TypedObjectAcessor.

The ScriptObject has a handly method (TryGetValue) which allows us to make the magic work. But this only work in first level of imported objects.

But the TemplateContext has no such trick. As I investigated, you have to implement your own TypedObjectAcessor and handle your own TryGetValue().

Well, I could copy the entire code of TypedObjectAcessor, but what if you introduce important changes? I would lose these changes.

So, I'd like to ask you if you cold change one or both of the following:

  • ensure the TypeObjectAcessor is public, where we can override the TryGetValue() by just making it virtual
    or
  • a handly TryGetValue() in the TemplateContext, as exists in the ScriptObject

Is it possible?

@xoofx
Copy link
Member

xoofx commented Jan 23, 2023

I would think that it would be easier to use a custom parameter less function like this to wrap a lazy value (I could add this to library if necessary)

public class ScriptLazy<T> : IScriptCustomFunction
{
    private readonly Lazy<T> _lazy;

    public ScriptLazy(Func<T> valueFactory)
    {
        _lazy = new Lazy<T>(valueFactory);
    }

    public int RequiredParameterCount => 0;

    public int ParameterCount => 0;

    public ScriptVarParamKind VarParamKind => ScriptVarParamKind.None;

    public Type ReturnType => typeof(T);

    public ScriptParameterInfo GetParameterInfo(int index) => default;

    public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
    {
        return _lazy.Value;
    }

    public ValueTask<object> InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
    {
        return new ValueTask<object>(_lazy.Value);
    }
}

That can be used like this:

[Test]
public void TestLazy()
{
  var input = @"{{ value }}";
  var template = Template.Parse(input);
  var result = template.Render(new { value = new ScriptLazy<int>(() => 1)});
  Assert.AreEqual("1", result);
}

@xoofx xoofx added the question label Jan 23, 2023
@avataron
Copy link
Author

avataron commented Jan 23, 2023

That worked quite well, passed all cases I noticed here!

(I could add this to library if necessary)
If not adding to library, this could go to docs. The solution was really simple and useful.

xoofx added a commit that referenced this issue Feb 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants