Skip to content

Improved context in lambda#153

Open
devhl-labs wants to merge 3 commits intoStubbleOrg:masterfrom
devhl-labs:devhl/150-improved-context-in-lambda
Open

Improved context in lambda#153
devhl-labs wants to merge 3 commits intoStubbleOrg:masterfrom
devhl-labs:devhl/150-improved-context-in-lambda

Conversation

@devhl-labs
Copy link
Copy Markdown

@devhl-labs devhl-labs commented Mar 29, 2026

Fixes #151

Fix Stubble section lambda context for primitive-scoped sections

Problem

When a 3-argument section lambda (Func<dynamic, string, Func<string, string>, object>) is nested inside a boolean or other primitive-valued section, Stubble passes the primitive value (e.g. true) as the lambda's context argument instead of the actual data object. This causes the lambda to receive a useless value and fail to resolve any data paths.

Example template structure that triggers the bug:

{{#ringProgression}}          ← ringProgression = true
  {{#blockLambdas.groupBy}}   ← lambda receives `true` instead of the data dictionary
    ...
  {{/blockLambdas.groupBy}}
{{/ringProgression}}

The groupBy lambda needs the parent data dictionary to resolve array paths, but receives true and produces empty output.

Root Cause

In SectionTokenRenderer, the lambda dispatch code passed context.View directly to dynamic-context lambdas. When rendering inside a boolean section like {{#ringProgression}}, the context is pushed with the boolean value, so context.View is true.

Fix

Stubble.Core (SectionTokenRenderer.cs)

Added a FindNearestObjectView helper that walks up the context parent chain, skipping primitive views (bool, int, long, float, double, decimal, string), and returns the nearest ancestor view that is a real object (dictionary, hashtable, etc.). Both Write and WriteAsync use this instead of context.View when dispatching to dynamic-context lambdas.

private static dynamic FindNearestObjectView(Context context)
{
    var current = context;
    while (current != null)
    {
        var view = current.View;
        if (view != null && !(view is bool) && !(view is int) && !(view is long)
            && !(view is float) && !(view is double) && !(view is decimal)
            && !(view is string))
        {
            return view;
        }
        current = current.ParentContext;
    }
    return context.View;
}

When the lambda is inside an object section (e.g. a dictionary), FindNearestObjectView returns that dictionary directly — preserving existing scoped behavior.

@devhl-labs devhl-labs marked this pull request as ready for review March 29, 2026 23:19
@devhl-labs devhl-labs marked this pull request as draft March 30, 2026 17:57
@devhl-labs devhl-labs marked this pull request as ready for review March 31, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lambdas have no reference of the current context

1 participant