StubbleOrg / Stubble

Trimmed down {{mustache}} templates in .NET
Other
399 stars 58 forks source link

Truthiness on a Dictionary<string, object> #135

Closed Baccanno closed 1 year ago

Baccanno commented 1 year ago

I recently swapped from Nustach to Stubble, it was quite a breath. Howerver a peculiar usecase seems to be suddenly non functional (stubble 1.10.8)

{{#Rec}}
  {{#Dy}}^Lang.Cards.{{Data.AddData.Card.CardCode}}{{/Dy}}
    !{{Data.AddData.Card.CardCode}} NO LANG
  {{#Dy}}/Lang.Cards.{{Data.AddData.Card.CardCode}}{{/Dy}}
  {{#Dy}}Lang.Cards.{{Data.AddData.Card.CardCode}}.CardName{{/Dy}}
{{/Rec}}

The Dy section does "{{{" + render(value) + "}}}" The Rec section does render(render(value))

Basically at the first Rec pass it was doing something like {{^Lang.Cards.MY_CODE}}! MY_CODE NO LANG{{/Lang.Cards.MY_CODE}}{{Lang.Cards.MY_CODE.CardName}}

and at the second one it was either ! MY_CODE NO LANG or My Card Name

However the current result I get is ! MY_CODE NO LANGMy Card Name which is strange, even if I inverse the truthiness the result stays the same.

I saw that truthiness was expected to be a bit touchy. Might it be that the truthiness is not "supported" and so it is ignored whatsoever ?

https://github.com/StubbleOrg/Stubble/blob/master/docs/extensibility.md#truthiness-evaluation

The type of Lang.Cards is public Dictionary<string, CardLang> Cards = new Dictionary<string, CardLang> {{ "-1", DefaultCardLang}}; and CardLang is a standard class. Shouldn't it fall in the IDictionary value getter and if value is null or non existant validate the check ? Should I add my own truthy check for CardLang ?

Baccanno commented 1 year ago

The same actually occurs with empty values. For instance an empty anonymous object {} will not trigger {^...} rules. new { TargetedMelee = false, TargetedRange = false} will work. In my sens truthiness checks for non existing/empty/null and "False" values

Romanx commented 1 year ago

Hi there,

I don't suppose you'd be able to give a small reproduction case showing this that we can use to debug?

Thanks

Baccanno commented 1 year ago

Mmh, well unfortunatelly not right now. I'd have to isolate.

Romanx commented 1 year ago

Okay i had a closer read of your issue, as you noted truthy checks are complicated bar the base types.

So the simple answer

Last note is that anonymous objects {} are just objects so if they're not null then they're truthy unless you put a custom check on them.

I'm not sure there's actually an error here, my advice is to avoid object since the lack of typing makes it harder for our reflection based code to infer types better. Usually a concrete type or base type of the things going into a collection or property would be better.

I'm going to close this now but hopefully this helped, if you find something you think is wrong and could isolate a repro please feel free to open a new issue!

Thanks 👍

Baccanno commented 1 year ago

Thanks, this is indeed what I wanted to know, I was looking at the source code but had some hard time figuring out what was really happening.

If the type can be cast to IEnumerable then we check if the enumerable has any items. So in your example a dictionary<TKey, TValue> does implement IEnumerable so will be truthy if it has any key value pairs.

My result is then quite strange isn't it ? As Lang.Cards is a dictionary {{^Lang.Cards.MY_CODE}}! MY_CODE NO LANG{{/Lang.Cards.MY_CODE}}{{Lang.Cards.MY_CODE.CardName}} should only validate one of the parts ?

I'll try to come back with a further debugging sessions.