diff --git a/Stencil/Node.swift b/Stencil/Node.swift index 9f29332..8345989 100644 --- a/Stencil/Node.swift +++ b/Stencil/Node.swift @@ -157,7 +157,7 @@ public class ForNode : NodeType { public func render(context: Context) throws -> String { let values = try variable.resolve(context) - if let values = values as? NSArray where values.count > 0 { + if let values = values as? [Any] where values.count > 0 { return try values.map { item in try context.push([loopVariable: item]) { try renderNodes(nodes, context) @@ -232,10 +232,10 @@ public class IfNode : NodeType { let result = try variable.resolve(context) var truthy = false - if let result = result as? NSArray { - if result.count > 0 { - truthy = true - } + if let result = result as? [Any] { + truthy = !result.isEmpty + } else if let result = result as? [String:Any] { + truthy = !result.isEmpty } else if result != nil { truthy = true } diff --git a/Stencil/Variable.swift b/Stencil/Variable.swift index 061059b..c41e60b 100644 --- a/Stencil/Variable.swift +++ b/Stencil/Variable.swift @@ -58,11 +58,9 @@ public struct Variable : Equatable, Resolvable { for bit in lookup() { if let context = current as? Context { current = context[bit] - } else if let dictionary = current as? [String: Any] { + } else if let dictionary = resolveDictionary(current) { current = dictionary[bit] - } else if let dictionary = current as? [String: AnyObject] { - current = dictionary[bit] - } else if let array = current as? [Any] { + } else if let array = resolveArray(current) { if let index = Int(bit) { current = array[index] } else if bit == "first" { @@ -72,27 +70,66 @@ public struct Variable : Equatable, Resolvable { } else if bit == "count" { current = array.count } - } else if let array = current as? NSArray { - if let index = Int(bit) { - current = array[index] - } else if bit == "first" { - current = array.firstObject - } else if bit == "last" { - current = array.lastObject - } else if bit == "count" { - current = array.count - } - } else if let object = current as? NSObject { + } else if let object = current as? NSObject { // NSKeyValueCoding current = object.valueForKey(bit) } else { return nil } } - return current + return normalize(current) } } public func ==(lhs: Variable, rhs: Variable) -> Bool { return lhs.variable == rhs.variable } + + +func resolveDictionary(current: Any?) -> [String: Any]? { + switch current { + case let dictionary as [String: Any]: + return dictionary + case let dictionary as [String: AnyObject]: + var result: [String: Any] = [:] + for (k, v) in dictionary { + result[k] = v as Any + } + return result + case let dictionary as NSDictionary: + var result: [String: Any] = [:] + for (k, v) in dictionary { + if let k = k as? String { + result[k] = v as Any + } + } + return result + default: + return nil + } +} + +func resolveArray(current: Any?) -> [Any]? { + switch current { + case let array as [Any]: + return array + case let array as [AnyObject]: + return array.map { $0 as Any } + case let array as NSArray: + return array.map { $0 as Any } + default: + return nil + } +} + +func normalize(current: Any?) -> Any? { + if let array = resolveArray(current) { + return array + } + + if let dictionary = resolveDictionary(current) { + return dictionary + } + + return current +} diff --git a/StencilSpecs/Nodes/ForNodeSpec.swift b/StencilSpecs/Nodes/ForNodeSpec.swift index 6dca534..eb1edaf 100644 --- a/StencilSpecs/Nodes/ForNodeSpec.swift +++ b/StencilSpecs/Nodes/ForNodeSpec.swift @@ -1,5 +1,6 @@ import Spectre import Stencil +import Foundation describe("ForNode") { @@ -20,4 +21,24 @@ describe("ForNode") { let node = ForNode(variable: "emptyItems", loopVariable: "item", nodes: nodes, emptyNodes: emptyNodes) try expect(try node.render(context)) == "empty" } + + $0.it("renders a context variable of type Array") { + let any_context = Context(dictionary: [ + "items": ([1, 2, 3] as [Any]) + ]) + + let nodes: [NodeType] = [VariableNode(variable: "item")] + let node = ForNode(variable: "items", loopVariable: "item", nodes: nodes, emptyNodes: []) + try expect(try node.render(any_context)) == "123" + } + + $0.it("renders a context variable of type NSArray") { + let nsarray_context = Context(dictionary: [ + "items": NSArray(array: [1, 2, 3]) + ]) + + let nodes: [NodeType] = [VariableNode(variable: "item")] + let node = ForNode(variable: "items", loopVariable: "item", nodes: nodes, emptyNodes: []) + try expect(try node.render(nsarray_context)) == "123" + } } diff --git a/StencilSpecs/Nodes/IfNodeSpec.swift b/StencilSpecs/Nodes/IfNodeSpec.swift index c46edc6..77f56e6 100644 --- a/StencilSpecs/Nodes/IfNodeSpec.swift +++ b/StencilSpecs/Nodes/IfNodeSpec.swift @@ -96,5 +96,18 @@ describe("IfNode") { let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) try expect(try node.render(arrayContext)) == "false" } + + $0.it("renders the false when dictionary expression is empty") { + let emptyItems = [String:AnyObject]() + let arrayContext = Context(dictionary: ["items": emptyItems]) + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + try expect(try node.render(arrayContext)) == "false" + } + + $0.it("renders the false when Array variable is empty") { + let arrayContext = Context(dictionary: ["items": ([] as [Any])]) + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + try expect(try node.render(arrayContext)) == "false" + } } }