sebastienros/fluid

How to convert ArgumentsTag

Opened this issue · 5 comments

Hi,

I am trying to convert the following arguments tag to v2:

https://github.com/Squidex/squidex/blob/master/backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs#L27

I have had a look to Orchard (https://github.com/OrchardCMS/OrchardCore/blob/339d74b251b53393dd510f8ad3027aa17d78500d/src/OrchardCore/OrchardCore.DisplayManagement.Liquid/LiquidViewParser.cs) and after that I came up with something like:

parser.RegisterParserTag("reference", primary.And(primary),
            async (ValueTuple<Expression, Expression> arguments, TextWriter writer, TextEncoder encoder, TemplateContext context) =>
            {
                if (context.GetValue("event")?.ToObjectValue() is EnrichedEvent enrichedEvent)
                {
                    var contentId = await arguments.Item2.EvaluateAsync(context);
                    var content = await ResolveContentAsync(serviceProvider, enrichedEvent.AppId.Id, contentId);

                    if (content != null)
                    {
                        var name = (await arguments.Item1.EvaluateAsync(context)).ToStringValue();

                        context.SetValue(name, content);
                    }
                }

                return Fluid.Ast.Completion.Normal;
            });

The tag works like assign:

{% for id in event.data.references.iv %}
    {% reference 'ref' id %}
    Text: {{ ref.data.field1.iv }} {{ ref.data.field2.iv }} {{ ref.id }}
{% endfor %}

there is also a filter

{% for id in event.data.references.iv %}
    {% assign ref = id | reference %}
    Text: {{ ref.data.field1.iv }} {{ ref.data.field2.iv }} {{ ref.id }}
{% endfor %}

But in my tests I always get the following error from the parse method:

Object reference not set to an instance of an object.

Okay, got it. It is basically the same problem like this: #307 (comment)

I derived from FluidParser and exposed the parsers I needed:

public sealed class CustomFluidParser : FluidParser
{
    public Deferred<Expression> PrimaryParser => Primary;

    public Parser<char> CommaParser => Comma;
}

I guess I could just have used ArgumentsList, but I decided to use this syntax:

parser.PrimaryParser.And(ZeroOrOne(parser.CommaParser)).And(parser.PrimaryParser);

It makes the comma optional and allows these statements:

{% asset 'ref' id %}
{% asset 'ref', id %}

You can even use AndSkip on the comma so it doesn't show up as a consumable token.

Good to know. I tried that first, but without the ZeroOrOne, so:

Primary.AndSkip(comma).And(Primary).

Can the parsers not be made public? It is "weird" that you can add custom tags from outside but not access the parsers that you need to register them.

I agree most of them should be public. But maybe through a custom property such that the public API is not drowning in these. A solution could be to introduce a class that will return the protected instances. This way it's not a breaking change, and there is a public property that exposes all of them.