Handlebars-Net/Handlebars.Net

[Question] I'm seeing some strange behaviors with shared environments, anyone see this behavior before?

Opened this issue · 2 comments

I have a model, it looks similar to this:

[
    {
        "url": "//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg",
        "sequence": 1,
        "isPrimary": true,
        "isImage": true,
        "imageType": "Studio"
    },
    ....
]

As part of a larger template, I have this

{{#objectFilter @data.images "isPrimary=true OR isImage=false"}}
	<pre>{{toJson this}}</pre>

	{{#each this}}
		<pre>{{this}}</pre>
		<pre>{{toJson this}}</pre>

		this.isImage => {{this.isImage}},
		isImage => {{isImage}},		
		this.isPrimary => {{this.isPrimary}},
		isPrimary => {{isPrimary}},

		<pre>{{toJson this}}</pre>
	{{/each}}
{{/objectFilter}}

With HandlebarsDotNet.Handlebars.CreateSharedEnvironment(config);, I get a result of

<pre>[{"url":"//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg","sequence":1,"isPrimary":true,"isImage":true,"imageType":"Studio"}]</pre>
<pre>[url, //domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg],[sequence, 1],[isPrimary, True],[isImage, True],[imageType, Studio]</pre>
<pre>{"url":"//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg","sequence":1,"isPrimary":true,"isImage":true,"imageType":"Studio"}</pre>

this.isImage => True,
isImage => True,
this.isPrimary => ,
isPrimary => True,

<pre>{"url":"//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg","sequence":1,"isPrimary":true,"isImage":true,"imageType":"Studio"}</pre>

Note the this.isPrimary => ,

Changing only HandlebarsDotNet.Handlebars.CreateSharedEnvironment(config); to HandlebarsDotNet.Handlebars.Create(config); results in an output of

<pre>[{"url":"//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg","sequence":1,"isPrimary":true,"isImage":true,"imageType":"Studio"}]</pre>
<pre>[url, //domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg],[sequence, 1],[isPrimary, True],[isImage, True],[imageType, Studio]</pre>
<pre>{"url":"//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg","sequence":1,"isPrimary":true,"isImage":true,"imageType":"Studio"}</pre>

this.isImage => True,
isImage => True,
this.isPrimary => True,
isPrimary => True,

<pre>{"url":"//domain.com/4fc914bb-7c8c-4801-a0a0-e2819413f703.jpg","sequence":1,"isPrimary":true,"isImage":true,"imageType":"Studio"}</pre>

Again, note this.isPrimary => True,

Can anyone think of why this. would evaluate differently when using a shared environment?
To add to the mystery, it's always happening to this one property, notice the isImage property was not impacted. Also, we do use custom helpers, but none change the context, attempt to do any late binding, or interact with the BlockHelperOptions. The objectFilter helper above uses Antlr to filter a collection of objects given an expression, so something like this:

		/// <summary>
		/// Filters a list of objects given some expression.
		/// </summary>
		/// <param name="input">List to apply the filter to.</param>
		/// <param name="expression">Expression to filter by.</param>
		/// <param name="ignoreCase">Whether or not to ignore case.</param>
		/// <returns>The filtered list; null if input is null.</returns>
		public static List<object> Filter(object input, string expression, bool ignoreCase) {
			if (input == null) {
				return null;
			}

			if (input is List<object> list) {
				var context = new FilterParser(
					new CommonTokenStream(
						new FilterLexer(
							CharStreams.fromString(expression)
						)
					)
				) {
					BuildParseTree = true
				}.parse();

				return list.Where(i => (bool) new FilterVisitor(i as Dictionary<string, object>, ignoreCase).Visit(context)).ToList();
			}

			return null;
		}

The toJson simply returns the serialized representation of the object

		/// <summary>
		/// Convert an object to a serialized json string.
		/// </summary>
		/// <param name="input">Object to serialize.</param>
		/// <returns>Serialized response.</returns>
		public static string ToJson(object input) {
			return input == null ? null : JsonConvert.SerializeObject(input);
		}

I added the output of this above and below to show that it does contain the property I'm attempting to access; so I can't imagine it's a case where we're running into some concurrency issue; additionally, I'd expect this to puke randomly if that were the case and not always on this specific property.

One more piece to add to the mystery, I cannot replicate this locally in an isolated environment (even when using the full complete model and template), so there's something more to it (which I'll continue to try to diagnose and identify). I'm just curious

  1. if anyone has seen this before
  2. if anyone has any clues as to why/how there could be a discrepancy between {{property}} and {{this.property}}

Thanks, I appreciate any insight.

It may be easier to help if you provide a test that reproduces the problem.

Right, as I said, I've still been unable to replicate it locally in an isolated environment. I will continue to poke around and figure out what might be causing this to happen.

I created this issue to simply get some feelers out as to whether or not anyone else has seen behavior like this. I'm trying/hoping that I'm simply doing something wrong, making an incorrect assumption, or at the very least hoping I could get some more anecdotal evidence to use in diagnosing the issue.