diff --git a/Sources/HummingbirdMustache/Template+Render.swift b/Sources/HummingbirdMustache/Template+Render.swift index 4220dbd..02779ae 100644 --- a/Sources/HummingbirdMustache/Template+Render.swift +++ b/Sources/HummingbirdMustache/Template+Render.swift @@ -138,12 +138,17 @@ extension HBMustacheTemplate { // work out which object to access. "." means the current object, if the variable name is "" // and we have a transform to run on the variable then we need the context object, otherwise - // the name is split by "." and we use mirror to get the correct child object + // the name is split by "." and we use mirror to get the correct child object. If we cannot find + // the root object we look up the context stack until we can find one with a matching name. The + // stack climbing can be disabled by prefixing the variable name with a "." let child: Any? if name == "." { child = context.stack.last! } else if name == "", transform != nil { child = context.sequenceContext + } else if name.first == "." { + let nameSplit = name.split(separator: ".").map { String($0) } + child = _getChild(named: nameSplit[...], from: context.stack.last!) } else { let nameSplit = name.split(separator: ".").map { String($0) } child = _getChildInStack(named: nameSplit[...], from: context.stack) diff --git a/Tests/HummingbirdMustacheTests/TemplateRendererTests.swift b/Tests/HummingbirdMustacheTests/TemplateRendererTests.swift index 6cb1551..26f8a3c 100644 --- a/Tests/HummingbirdMustacheTests/TemplateRendererTests.swift +++ b/Tests/HummingbirdMustacheTests/TemplateRendererTests.swift @@ -99,6 +99,16 @@ final class TemplateRendererTests: XCTestCase { XCTAssertEqual(template2.render("<>"), "<>") } + func testStopClimbingStack() throws { + let template1 = try HBMustacheTemplate(string: "{{#test}}{{name}}{{/test}}") + let template2 = try HBMustacheTemplate(string: "{{#test}}{{.name}}{{/test}}") + let object: [String: Any] = ["test": [:], "name": "John"] + let object2: [String: Any] = ["test": ["name": "Jane"], "name": "John"] + XCTAssertEqual(template1.render(object), "John") + XCTAssertEqual(template2.render(object), "") + XCTAssertEqual(template2.render(object2), "Jane") + } + /// variables func testMustacheManualExample1() throws { let template = try HBMustacheTemplate(string: """