From 393dc88a10534083353e4851d45759115d9dada0 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Sun, 27 Nov 2016 04:22:37 +0000 Subject: [PATCH] fix(extends): Support multiple extends Fixes #60 --- CHANGELOG.md | 3 +++ Sources/Inheritence.swift | 27 ++++++++++++++------ Tests/StencilTests/InheritenceSpec.swift | 6 +++++ Tests/StencilTests/fixtures/child-child.html | 2 ++ 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 Tests/StencilTests/fixtures/child-child.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 2afa164..769ab2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ index will now resolve to `nil` instead of causing a crash. [#72](https://github.com/kylef/Stencil/issues/72) +- Templates can now extend templates that extend other templates. + [#60](https://github.com/kylef/Stencil/issues/60) + ## 0.6.0 diff --git a/Sources/Inheritence.swift b/Sources/Inheritence.swift index fd3ef73..d2c1299 100644 --- a/Sources/Inheritence.swift +++ b/Sources/Inheritence.swift @@ -1,9 +1,9 @@ class BlockContext { class var contextKey: String { return "block_context" } - var blocks: [String:BlockNode] + var blocks: [String: BlockNode] - init(blocks: [String:BlockNode]) { + init(blocks: [String: BlockNode]) { self.blocks = blocks } @@ -42,10 +42,9 @@ class ExtendsNode : NodeType { throw TemplateSyntaxError("'extends' cannot appear more than once in the same template") } - let blockNodes = parsedNodes.filter { node in node is BlockNode } + let blockNodes = parsedNodes.flatMap { $0 as? BlockNode } - let nodes = blockNodes.reduce([String:BlockNode]()) { (accumulator, node:NodeType) -> [String:BlockNode] in - let node = (node as! BlockNode) + let nodes = blockNodes.reduce([String: BlockNode]()) { (accumulator, node) -> [String: BlockNode] in var dict = accumulator dict[node.name] = node return dict @@ -69,11 +68,23 @@ class ExtendsNode : NodeType { } guard let template = loader.loadTemplate(templateName) else { - let paths:String = loader.paths.map { $0.description }.joined(separator: ", ") + let paths: String = loader.paths.map { $0.description }.joined(separator: ", ") throw TemplateSyntaxError("'\(templateName)' template not found in \(paths)") } - let blockContext = BlockContext(blocks: blocks) + let blockContext: BlockContext + if let context = context[BlockContext.contextKey] as? BlockContext { + blockContext = context + + for (key, value) in blocks { + if !blockContext.blocks.keys.contains(key) { + blockContext.blocks[key] = value + } + } + } else { + blockContext = BlockContext(blocks: blocks) + } + return try context.push(dictionary: [BlockContext.contextKey: blockContext]) { return try template.render(context) } @@ -89,7 +100,7 @@ class BlockNode : NodeType { let bits = token.components() guard bits.count == 2 else { - throw TemplateSyntaxError("'block' tag takes one argument, the template file to be included") + throw TemplateSyntaxError("'block' tag takes one argument, the block name") } let blockName = bits[1] diff --git a/Tests/StencilTests/InheritenceSpec.swift b/Tests/StencilTests/InheritenceSpec.swift index accd15a..663161d 100644 --- a/Tests/StencilTests/InheritenceSpec.swift +++ b/Tests/StencilTests/InheritenceSpec.swift @@ -13,5 +13,11 @@ func testInheritence() { let template = loader.loadTemplate("child.html") try expect(try template?.render(context)) == "Header\nChild" } + + $0.it("can inherit from another template inheriting from another template") { + let context = Context(dictionary: ["loader": loader]) + let template = loader.loadTemplate("child-child.html") + try expect(try template?.render(context)) == "Child Child Header\nChild" + } } } diff --git a/Tests/StencilTests/fixtures/child-child.html b/Tests/StencilTests/fixtures/child-child.html new file mode 100644 index 0000000..5bb924e --- /dev/null +++ b/Tests/StencilTests/fixtures/child-child.html @@ -0,0 +1,2 @@ +{% extends "child.html" %} +{% block header %}Child Child Header{% endblock %}