diff --git a/Stencil/Node.swift b/Stencil/Node.swift index 04caaa5..f9c3f50 100644 --- a/Stencil/Node.swift +++ b/Stencil/Node.swift @@ -154,3 +154,66 @@ public class ForNode : Node { return (result, nil) } } + +public class IfNode : Node { + public let variable:Variable + public let trueNodes:[Node] + public let falseNodes:[Node] + + public class func parse(parser:TokenParser, token:Token) -> Node { + let variable = token.components()[1] + + let trueNodes = parser.parse(until(["endif", "else"])) + var falseNodes = [Node]() + + if let token = parser.nextToken() { + if token.contents == "else" { + falseNodes = parser.parse(until(["endif"])) + parser.nextToken() + } + } + + return IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes) + } + + public class func parse_ifnot(parser:TokenParser, token:Token) -> Node { + let variable = token.components()[1] + + let falseNodes = parser.parse(until(["endif", "else"])) + var trueNodes = [Node]() + + if let token = parser.nextToken() { + if token.contents == "else" { + trueNodes = parser.parse(until(["endif"])) + parser.nextToken() + } + } + + return IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes) + } + + public init(variable:String, trueNodes:[Node], falseNodes:[Node]) { + self.variable = Variable(variable) + self.trueNodes = trueNodes + self.falseNodes = falseNodes + } + + public func render(context: Context) -> (String?, Error?) { + let result: AnyObject? = variable.resolve(context) + var truthy = false + + if let result = result as? [AnyObject] { + if result.count > 0 { + truthy = true + } + } else if let result: AnyObject = result { + truthy = true + } + + context.push() + let (string, error) = renderNodes(truthy ? trueNodes : falseNodes, context) + context.pop() + + return (string, error) + } +} diff --git a/Stencil/Parser.swift b/Stencil/Parser.swift index d293337..e002b18 100644 --- a/Stencil/Parser.swift +++ b/Stencil/Parser.swift @@ -28,6 +28,8 @@ public class TokenParser { self.tokens = tokens tags["for"] = ForNode.parse tags["now"] = NowNode.parse + tags["if"] = IfNode.parse + tags["ifnot"] = IfNode.parse_ifnot } public func parse() -> [Node] { diff --git a/StencilTests/NodeTests.swift b/StencilTests/NodeTests.swift index 3109c2c..a4f5498 100644 --- a/StencilTests/NodeTests.swift +++ b/StencilTests/NodeTests.swift @@ -86,3 +86,71 @@ class ForNodeTests: NodeTests { XCTAssertEqual(result.0!, "123") } } + +class IfNodeTests: NodeTests { + + // MARK: Parsing + + func testParseIf() { + let tokens = [ + Token.Block(value: "if value"), + Token.Text(value: "true"), + Token.Block(value: "else"), + Token.Text(value: "false"), + Token.Block(value: "endif") + ] + + let parser = TokenParser(tokens: tokens) + let nodes = parser.parse() + let node = nodes.first! as IfNode + let trueNode = node.trueNodes.first! as TextNode + let falseNode = node.falseNodes.first! as TextNode + + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.variable.variable, "value") + XCTAssertEqual(node.trueNodes.count, 1) + XCTAssertEqual(trueNode.text, "true") + XCTAssertEqual(node.falseNodes.count, 1) + XCTAssertEqual(falseNode.text, "false") + } + + func testParseIfNot() { + let tokens = [ + Token.Block(value: "ifnot value"), + Token.Text(value: "false"), + Token.Block(value: "else"), + Token.Text(value: "true"), + Token.Block(value: "endif") + ] + + let parser = TokenParser(tokens: tokens) + let nodes = parser.parse() + let node = nodes.first! as IfNode + let trueNode = node.trueNodes.first! as TextNode + let falseNode = node.falseNodes.first! as TextNode + + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.variable.variable, "value") + XCTAssertEqual(node.trueNodes.count, 1) + XCTAssertEqual(trueNode.text, "true") + XCTAssertEqual(node.falseNodes.count, 1) + XCTAssertEqual(falseNode.text, "false") + } + + // MARK: Rendering + + func testIfNodeRenderTruth() { + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + let result = node.render(context) + + XCTAssertEqual(result.0!, "true") + } + + func testIfNodeRenderFalse() { + let node = IfNode(variable: "unknown", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + let result = node.render(context) + + XCTAssertEqual(result.0!, "false") + } + +}