From 53d5a4f8c3aab81d88328fac6bfb4f45608ca5d7 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Mon, 29 Jun 2015 16:40:15 -0700 Subject: [PATCH] [Project] Use 2 spaces for indentation --- Stencil.xcodeproj/project.pbxproj | 2 + Stencil/Context.swift | 74 +-- Stencil/Lexer.swift | 82 +-- Stencil/Node.swift | 466 +++++++++--------- Stencil/Parser.swift | 182 +++---- Stencil/Result.swift | 24 +- Stencil/Template.swift | 106 ++-- Stencil/TemplateLoader.swift | 48 +- Stencil/TemplateLoader/Include.swift | 58 +-- Stencil/Tokenizer.swift | 98 ++-- Stencil/Variable.swift | 74 +-- StencilTests/ContextTests.swift | 88 ++-- StencilTests/LexerTests.swift | 84 ++-- StencilTests/NodeTests.swift | 394 +++++++-------- StencilTests/ParserTests.swift | 66 +-- StencilTests/StencilTests.swift | 106 ++-- .../TemplateLoader/IncludeTests.swift | 96 ++-- StencilTests/TemplateLoaderTests.swift | 26 +- StencilTests/TemplateTests.swift | 12 +- StencilTests/VariableTests.swift | 106 ++-- 20 files changed, 1097 insertions(+), 1095 deletions(-) diff --git a/Stencil.xcodeproj/project.pbxproj b/Stencil.xcodeproj/project.pbxproj index c61a48f7..16ea1913 100644 --- a/Stencil.xcodeproj/project.pbxproj +++ b/Stencil.xcodeproj/project.pbxproj @@ -123,7 +123,9 @@ F0837D60120FB15CC00B9EBD /* Pods */, BE2E2DF8F488C4669126E920 /* Frameworks */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 77FAAE5319F91E480029DC5E /* Products */ = { isa = PBXGroup; diff --git a/Stencil/Context.swift b/Stencil/Context.swift index 202f99b3..f2276203 100644 --- a/Stencil/Context.swift +++ b/Stencil/Context.swift @@ -2,51 +2,51 @@ import Foundation /// A container for template variables. public class Context : Equatable { - var dictionaries:[Dictionary] - - public init(dictionary:Dictionary) { - dictionaries = [dictionary] - } - - public init() { - dictionaries = [] - } - - public subscript(key: String) -> AnyObject? { - /// Retrieves a variable's value, starting at the current context and going upwards - get { - for dictionary in reverse(dictionaries) { - if let value:AnyObject = dictionary[key] { - return value - } - } - - return nil + var dictionaries:[Dictionary] + + public init(dictionary:Dictionary) { + dictionaries = [dictionary] + } + + public init() { + dictionaries = [] + } + + public subscript(key: String) -> AnyObject? { + /// Retrieves a variable's value, starting at the current context and going upwards + get { + for dictionary in reverse(dictionaries) { + if let value:AnyObject = dictionary[key] { + return value } + } - /// Set a variable in the current context, deleting the variable if it's nil - set(value) { - if dictionaries.count > 0 { - var dictionary = dictionaries.removeLast() - dictionary[key] = value - dictionaries.append(dictionary) - } - } - } - - public func push() { - push(Dictionary()) + return nil } - public func push(dictionary:Dictionary) { + /// Set a variable in the current context, deleting the variable if it's nil + set(value) { + if dictionaries.count > 0 { + var dictionary = dictionaries.removeLast() + dictionary[key] = value dictionaries.append(dictionary) + } } + } - public func pop() { - dictionaries.removeLast() - } + public func push() { + push(Dictionary()) + } + + public func push(dictionary:Dictionary) { + dictionaries.append(dictionary) + } + + public func pop() { + dictionaries.removeLast() + } } public func ==(lhs:Context, rhs:Context) -> Bool { - return lhs.dictionaries == rhs.dictionaries + return lhs.dictionaries == rhs.dictionaries } diff --git a/Stencil/Lexer.swift b/Stencil/Lexer.swift index 0d5e8e01..6774628e 100644 --- a/Stencil/Lexer.swift +++ b/Stencil/Lexer.swift @@ -1,57 +1,57 @@ import Foundation public struct Lexer { - public let templateString:String - let regex = NSRegularExpression(pattern: "(\\{\\{.*?\\}\\}|\\{%.*?%\\}|\\{#.*?#\\})", options: nil, error: nil)! + public let templateString:String + let regex = NSRegularExpression(pattern: "(\\{\\{.*?\\}\\}|\\{%.*?%\\}|\\{#.*?#\\})", options: nil, error: nil)! - public init(templateString:String) { - self.templateString = templateString - } - - func createToken(string:String) -> Token { - func strip() -> String { - return string[string.startIndex.successor().successor().. Token { + func strip() -> String { + return string[string.startIndex.successor().successor().. [Token] { - // Unfortunately NSRegularExpression doesn't have a split. - // So here's a really terrible implementation + return Token.Text(value: string) + } - var tokens = [Token]() + /// Returns an array of tokens from a given template string. + public func tokenize() -> [Token] { + // Unfortunately NSRegularExpression doesn't have a split. + // So here's a really terrible implementation - let range = NSMakeRange(0, count(templateString)) - var lastIndex = 0 - let nsTemplateString = templateString as NSString - let options = NSMatchingOptions(0) - regex.enumerateMatchesInString(templateString, options: options, range: range) { (result, flags, b) in - if result.range.location != lastIndex { - let previousMatch = nsTemplateString.substringWithRange(NSMakeRange(lastIndex, result.range.location - lastIndex)) - tokens.append(self.createToken(previousMatch)) - } + var tokens = [Token]() - let match = nsTemplateString.substringWithRange(result.range) - tokens.append(self.createToken(match)) + let range = NSMakeRange(0, count(templateString)) + var lastIndex = 0 + let nsTemplateString = templateString as NSString + let options = NSMatchingOptions(0) + regex.enumerateMatchesInString(templateString, options: options, range: range) { (result, flags, b) in + if result.range.location != lastIndex { + let previousMatch = nsTemplateString.substringWithRange(NSMakeRange(lastIndex, result.range.location - lastIndex)) + tokens.append(self.createToken(previousMatch)) + } - lastIndex = result.range.location + result.range.length - } + let match = nsTemplateString.substringWithRange(result.range) + tokens.append(self.createToken(match)) - if lastIndex < count(templateString) { - let substring = (templateString as NSString).substringFromIndex(lastIndex) - tokens.append(Token.Text(value: substring)) - } + lastIndex = result.range.location + result.range.length + } - return tokens + if lastIndex < count(templateString) { + let substring = (templateString as NSString).substringFromIndex(lastIndex) + tokens.append(Token.Text(value: substring)) } + + return tokens + } } diff --git a/Stencil/Node.swift b/Stencil/Node.swift index 262e8aaa..84569555 100644 --- a/Stencil/Node.swift +++ b/Stencil/Node.swift @@ -1,304 +1,304 @@ import Foundation struct NodeError : Error { - let token:Token - let message:String + let token:Token + let message:String - init(token:Token, message:String) { - self.token = token - self.message = message - } + init(token:Token, message:String) { + self.token = token + self.message = message + } - var description:String { - return "\(token.components().first!): \(message)" - } + var description:String { + return "\(token.components().first!): \(message)" + } } public protocol Node { - /// Return the node rendered as a string, or returns a failure - func render(context:Context) -> Result + /// Return the node rendered as a string, or returns a failure + func render(context:Context) -> Result } extension Array { - func map(block:((Element) -> (U?, Error?))) -> ([U]?, Error?) { - var results = [U]() - - for item in self { - let (result, error) = block(item) - - if let error = error { - return (nil, error) - } else if (result != nil) { - // let result = result exposing a bug in the Swift compier :( - results.append(result!) - } - } - - return (results, nil) + func map(block:((Element) -> (U?, Error?))) -> ([U]?, Error?) { + var results = [U]() + + for item in self { + let (result, error) = block(item) + + if let error = error { + return (nil, error) + } else if (result != nil) { + // let result = result exposing a bug in the Swift compier :( + results.append(result!) + } } + + return (results, nil) + } } public func renderNodes(nodes:[Node], context:Context) -> Result { - var result = "" - - for item in nodes { - switch item.render(context) { - case .Success(let string): - result += string - case .Error(let error): - return .Error(error) - } + var result = "" + + for item in nodes { + switch item.render(context) { + case .Success(let string): + result += string + case .Error(let error): + return .Error(error) } + } - return .Success(result) + return .Success(result) } public class SimpleNode : Node { - let handler:(Context) -> (Result) + let handler:(Context) -> (Result) - public init(handler:((Context) -> (Result))) { - self.handler = handler - } + public init(handler:((Context) -> (Result))) { + self.handler = handler + } - public func render(context:Context) -> Result { - return handler(context) - } + public func render(context:Context) -> Result { + return handler(context) + } } public class TextNode : Node { - public let text:String + public let text:String - public init(text:String) { - self.text = text - } + public init(text:String) { + self.text = text + } - public func render(context:Context) -> Result { - return .Success(self.text) - } + public func render(context:Context) -> Result { + return .Success(self.text) + } } public class VariableNode : Node { - public let variable:Variable - - public init(variable:Variable) { - self.variable = variable - } + public let variable:Variable - public init(variable:String) { - self.variable = Variable(variable) - } + public init(variable:Variable) { + self.variable = variable + } - public func render(context:Context) -> Result { - let result:AnyObject? = variable.resolve(context) + public init(variable:String) { + self.variable = Variable(variable) + } - if let result = result as? String { - return .Success(result) - } else if let result = result as? NSObject { - return .Success(result.description) - } + public func render(context:Context) -> Result { + let result:AnyObject? = variable.resolve(context) - return .Success("") + if let result = result as? String { + return .Success(result) + } else if let result = result as? NSObject { + return .Success(result.description) } + + return .Success("") + } } public class NowNode : Node { - public let format:Variable - - public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { - var format:Variable? - - let components = token.components() - if components.count == 2 { - format = Variable(components[1]) - } + public let format:Variable - return .Success(node:NowNode(format:format)) - } + public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { + var format:Variable? - public init(format:Variable?) { - if let format = format { - self.format = format - } else { - self.format = Variable("\"yyyy-MM-dd 'at' HH:mm\"") - } + let components = token.components() + if components.count == 2 { + format = Variable(components[1]) } - public func render(context: Context) -> Result { - let date = NSDate() - let format: AnyObject? = self.format.resolve(context) - var formatter:NSDateFormatter? - - if let format = format as? NSDateFormatter { - formatter = format - } else if let format = format as? String { - formatter = NSDateFormatter() - formatter!.dateFormat = format - } else { - return .Success("") - } + return .Success(node:NowNode(format:format)) + } - return .Success(formatter!.stringFromDate(date)) + public init(format:Variable?) { + if let format = format { + self.format = format + } else { + self.format = Variable("\"yyyy-MM-dd 'at' HH:mm\"") + } + } + + public func render(context: Context) -> Result { + let date = NSDate() + let format: AnyObject? = self.format.resolve(context) + var formatter:NSDateFormatter? + + if let format = format as? NSDateFormatter { + formatter = format + } else if let format = format as? String { + formatter = NSDateFormatter() + formatter!.dateFormat = format + } else { + return .Success("") } + + return .Success(formatter!.stringFromDate(date)) + } } public class ForNode : Node { - let variable:Variable - let loopVariable:String - let nodes:[Node] - - public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { - let components = token.components() - - if count(components) == 4 && components[2] == "in" { - let loopVariable = components[1] - let variable = components[3] - - var forNodes:[Node]! - var emptyNodes = [Node]() - - switch parser.parse(until(["endfor", "empty"])) { - case .Success(let nodes): - forNodes = nodes - case .Error(let error): - return .Error(error: error) - } - - if let token = parser.nextToken() { - if token.contents == "empty" { - switch parser.parse(until(["endfor"])) { - case .Success(let nodes): - emptyNodes = nodes - case .Error(let error): - return .Error(error: error) - } - - parser.nextToken() - } - } else { - return .Error(error: NodeError(token: token, message: "`endfor` was not found.")) - } - - return .Success(node:ForNode(variable: variable, loopVariable: loopVariable, nodes: forNodes, emptyNodes:emptyNodes)) + let variable:Variable + let loopVariable:String + let nodes:[Node] + + public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { + let components = token.components() + + if count(components) == 4 && components[2] == "in" { + let loopVariable = components[1] + let variable = components[3] + + var forNodes:[Node]! + var emptyNodes = [Node]() + + switch parser.parse(until(["endfor", "empty"])) { + case .Success(let nodes): + forNodes = nodes + case .Error(let error): + return .Error(error: error) + } + + if let token = parser.nextToken() { + if token.contents == "empty" { + switch parser.parse(until(["endfor"])) { + case .Success(let nodes): + emptyNodes = nodes + case .Error(let error): + return .Error(error: error) + } + + parser.nextToken() } + } else { + return .Error(error: NodeError(token: token, message: "`endfor` was not found.")) + } - return .Error(error: NodeError(token: token, message: "Invalid syntax. Expected `for x in y`.")) + return .Success(node:ForNode(variable: variable, loopVariable: loopVariable, nodes: forNodes, emptyNodes:emptyNodes)) } - public init(variable:String, loopVariable:String, nodes:[Node], emptyNodes:[Node]) { - self.variable = Variable(variable) - self.loopVariable = loopVariable - self.nodes = nodes - } + return .Error(error: NodeError(token: token, message: "Invalid syntax. Expected `for x in y`.")) + } - public func render(context: Context) -> Result { - let values = variable.resolve(context) as? [AnyObject] - var output = "" - - if let values = values { - for item in values { - context.push() - context[loopVariable] = item - let result = renderNodes(nodes, context) - context.pop() - - switch result { - case .Success(let string): - output += string - case .Error(let error): - return .Error(error) - } - } - } + public init(variable:String, loopVariable:String, nodes:[Node], emptyNodes:[Node]) { + self.variable = Variable(variable) + self.loopVariable = loopVariable + self.nodes = nodes + } - return .Success(output) - } -} + public func render(context: Context) -> Result { + let values = variable.resolve(context) as? [AnyObject] + var output = "" -public class IfNode : Node { - public let variable:Variable - public let trueNodes:[Node] - public let falseNodes:[Node] - - public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { - let variable = token.components()[1] - var trueNodes = [Node]() - var falseNodes = [Node]() - - switch parser.parse(until(["endif", "else"])) { - case .Success(let nodes): - trueNodes = nodes - case .Error(let error): - return .Error(error: error) - } + if let values = values { + for item in values { + context.push() + context[loopVariable] = item + let result = renderNodes(nodes, context) + context.pop() - if let token = parser.nextToken() { - if token.contents == "else" { - switch parser.parse(until(["endif"])) { - case .Success(let nodes): - falseNodes = nodes - case .Error(let error): - return .Error(error: error) - } - parser.nextToken() - } - } else { - return .Error(error:NodeError(token: token, message: "`endif` was not found.")) + switch result { + case .Success(let string): + output += string + case .Error(let error): + return .Error(error) } - - return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)) + } } - public class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result { - let variable = token.components()[1] - var trueNodes = [Node]() - var falseNodes = [Node]() + return .Success(output) + } +} + +public class IfNode : Node { + public let variable:Variable + public let trueNodes:[Node] + public let falseNodes:[Node] + + public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { + let variable = token.components()[1] + var trueNodes = [Node]() + var falseNodes = [Node]() + + switch parser.parse(until(["endif", "else"])) { + case .Success(let nodes): + trueNodes = nodes + case .Error(let error): + return .Error(error: error) + } - switch parser.parse(until(["endif", "else"])) { + if let token = parser.nextToken() { + if token.contents == "else" { + switch parser.parse(until(["endif"])) { case .Success(let nodes): - falseNodes = nodes + falseNodes = nodes case .Error(let error): - return .Error(error: error) + return .Error(error: error) } + parser.nextToken() + } + } else { + return .Error(error:NodeError(token: token, message: "`endif` was not found.")) + } - if let token = parser.nextToken() { - if token.contents == "else" { - switch parser.parse(until(["endif"])) { - case .Success(let nodes): - trueNodes = nodes - case .Error(let error): - return .Error(error: error) - } - parser.nextToken() - } - } else { - return .Error(error:NodeError(token: token, message: "`endif` was not found.")) - } + return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)) + } - return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)) - } + public class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result { + let variable = token.components()[1] + var trueNodes = [Node]() + var falseNodes = [Node]() - public init(variable:String, trueNodes:[Node], falseNodes:[Node]) { - self.variable = Variable(variable) - self.trueNodes = trueNodes - self.falseNodes = falseNodes + switch parser.parse(until(["endif", "else"])) { + case .Success(let nodes): + falseNodes = nodes + case .Error(let error): + return .Error(error: error) } - public func render(context: Context) -> Result { - 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 + if let token = parser.nextToken() { + if token.contents == "else" { + switch parser.parse(until(["endif"])) { + case .Success(let nodes): + trueNodes = nodes + case .Error(let error): + return .Error(error: error) } + parser.nextToken() + } + } else { + return .Error(error:NodeError(token: token, message: "`endif` was not found.")) + } - context.push() - let output = renderNodes(truthy ? trueNodes : falseNodes, context) - context.pop() - - return output + return .Success(node: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) -> Result { + 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 output = renderNodes(truthy ? trueNodes : falseNodes, context) + context.pop() + + return output + } } diff --git a/Stencil/Parser.swift b/Stencil/Parser.swift index 536505dd..8eebeaab 100644 --- a/Stencil/Parser.swift +++ b/Stencil/Parser.swift @@ -1,109 +1,109 @@ import Foundation public func until(tags:[String])(parser:TokenParser, token:Token) -> Bool { - if let name = token.components().first { - for tag in tags { - if name == tag { - return true - } - } + if let name = token.components().first { + for tag in tags { + if name == tag { + return true + } } + } - return false + return false } /// A class for parsing an array of tokens and converts them into a collection of Node's public class TokenParser { - public typealias TagParser = (TokenParser, Token) -> Result - public typealias NodeList = [Node] - - public enum Result { - case Success(node: Node) - case Error(error: Stencil.Error) - } - - public enum Results { - case Success(nodes: NodeList) - case Error(error: Stencil.Error) - } - - private var tokens:[Token] - private var tags = Dictionary() - - public init(tokens:[Token]) { - self.tokens = tokens - registerTag("for", parser: ForNode.parse) - registerTag("if", parser: IfNode.parse) - registerTag("ifnot", parser: IfNode.parse_ifnot) - registerTag("now", parser: NowNode.parse) - registerTag("include", parser: IncludeNode.parse) - } - - /// Registers a new template tag - public func registerTag(name:String, parser:TagParser) { - tags[name] = parser - } - - /// Registers a simple template tag with a name and a handler - public func registerSimpleTag(name:String, handler:((Context) -> (Stencil.Result))) { - registerTag(name, parser: { (parser, token) -> TokenParser.Result in - return .Success(node:SimpleNode(handler: handler)) - }) - } - - /// Parse the given tokens into nodes - public func parse() -> Results { - return parse(nil) - } + public typealias TagParser = (TokenParser, Token) -> Result + public typealias NodeList = [Node] + + public enum Result { + case Success(node: Node) + case Error(error: Stencil.Error) + } + + public enum Results { + case Success(nodes: NodeList) + case Error(error: Stencil.Error) + } + + private var tokens:[Token] + private var tags = Dictionary() + + public init(tokens:[Token]) { + self.tokens = tokens + registerTag("for", parser: ForNode.parse) + registerTag("if", parser: IfNode.parse) + registerTag("ifnot", parser: IfNode.parse_ifnot) + registerTag("now", parser: NowNode.parse) + registerTag("include", parser: IncludeNode.parse) + } + + /// Registers a new template tag + public func registerTag(name:String, parser:TagParser) { + tags[name] = parser + } + + /// Registers a simple template tag with a name and a handler + public func registerSimpleTag(name:String, handler:((Context) -> (Stencil.Result))) { + registerTag(name, parser: { (parser, token) -> TokenParser.Result in + return .Success(node:SimpleNode(handler: handler)) + }) + } + + /// Parse the given tokens into nodes + public func parse() -> Results { + return parse(nil) + } + + public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> TokenParser.Results { + var nodes = NodeList() + + while tokens.count > 0 { + let token = nextToken()! + + switch token { + case .Text(let text): + nodes.append(TextNode(text: text)) + case .Variable(let variable): + nodes.append(VariableNode(variable: variable)) + case .Block(let value): + let tag = token.components().first + + if let parse_until = parse_until { + if parse_until(parser: self, token: token) { + prependToken(token) + return .Success(nodes:nodes) + } + } - public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> TokenParser.Results { - var nodes = NodeList() - - while tokens.count > 0 { - let token = nextToken()! - - switch token { - case .Text(let text): - nodes.append(TextNode(text: text)) - case .Variable(let variable): - nodes.append(VariableNode(variable: variable)) - case .Block(let value): - let tag = token.components().first - - if let parse_until = parse_until { - if parse_until(parser: self, token: token) { - prependToken(token) - return .Success(nodes:nodes) - } - } - - if let tag = tag { - if let parser = self.tags[tag] { - switch parser(self, token) { - case .Success(let node): - nodes.append(node) - case .Error(let error): - return .Error(error:error) - } - } - } - case .Comment(let value): - continue + if let tag = tag { + if let parser = self.tags[tag] { + switch parser(self, token) { + case .Success(let node): + nodes.append(node) + case .Error(let error): + return .Error(error:error) } + } } - - return .Success(nodes:nodes) + case .Comment(let value): + continue + } } - public func nextToken() -> Token? { - if tokens.count > 0 { - return tokens.removeAtIndex(0) - } + return .Success(nodes:nodes) + } - return nil + public func nextToken() -> Token? { + if tokens.count > 0 { + return tokens.removeAtIndex(0) } - public func prependToken(token:Token) { - tokens.insert(token, atIndex: 0) - } + return nil + } + + public func prependToken(token:Token) { + tokens.insert(token, atIndex: 0) + } } diff --git a/Stencil/Result.swift b/Stencil/Result.swift index c7083958..36466273 100644 --- a/Stencil/Result.swift +++ b/Stencil/Result.swift @@ -1,25 +1,25 @@ import Foundation public protocol Error : Printable { - + } public func ==(lhs:Error, rhs:Error) -> Bool { - return lhs.description == rhs.description + return lhs.description == rhs.description } public enum Result : Equatable { - case Success(String) - case Error(Stencil.Error) + case Success(String) + case Error(Stencil.Error) } public func ==(lhs:Result, rhs:Result) -> Bool { - switch (lhs, rhs) { - case (.Success(let lhsValue), .Success(let rhsValue)): - return lhsValue == rhsValue - case (.Error(let lhsValue), .Error(let rhsValue)): - return lhsValue == rhsValue - default: - return false - } + switch (lhs, rhs) { + case (.Success(let lhsValue), .Success(let rhsValue)): + return lhsValue == rhsValue + case (.Error(let lhsValue), .Error(let rhsValue)): + return lhsValue == rhsValue + default: + return false + } } diff --git a/Stencil/Template.swift b/Stencil/Template.swift index 9bf437a7..1be3480a 100644 --- a/Stencil/Template.swift +++ b/Stencil/Template.swift @@ -3,71 +3,71 @@ import PathKit /// A class representing a template public class Template { - public let parser:TokenParser + public let parser:TokenParser - /// Create a template with the given name inside the main bundle - public convenience init?(named:String) { - self.init(named:named, inBundle:nil) - } - - /// Create a template with the given name inside the given bundle - public convenience init?(named:String, inBundle bundle:NSBundle?) { - var url:NSURL? + /// Create a template with the given name inside the main bundle + public convenience init?(named:String) { + self.init(named:named, inBundle:nil) + } - if let bundle = bundle { - url = bundle.URLForResource(named, withExtension: nil) - } else { - url = NSBundle.mainBundle().URLForResource(named, withExtension: nil) - } + /// Create a template with the given name inside the given bundle + public convenience init?(named:String, inBundle bundle:NSBundle?) { + var url:NSURL? - self.init(URL:url!) + if let bundle = bundle { + url = bundle.URLForResource(named, withExtension: nil) + } else { + url = NSBundle.mainBundle().URLForResource(named, withExtension: nil) } - /// Create a template with a file found at the given URL - public convenience init?(URL:NSURL) { - var error:NSError? - let maybeTemplateString = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: &error) - if let templateString = maybeTemplateString { - self.init(templateString:templateString as String) - } else { - self.init(templateString:"") - return nil - } + self.init(URL:url!) + } + + /// Create a template with a file found at the given URL + public convenience init?(URL:NSURL) { + var error:NSError? + let maybeTemplateString = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: &error) + if let templateString = maybeTemplateString { + self.init(templateString:templateString as String) + } else { + self.init(templateString:"") + return nil } + } - /// Create a template with a file found at the given path - public convenience init?(path:Path) { - var error:NSError? + /// Create a template with a file found at the given path + public convenience init?(path:Path) { + var error:NSError? - if let string:String = path.read() { - self.init(templateString:string) - } else { - self.init(templateString:"") - return nil - } + if let string:String = path.read() { + self.init(templateString:string) + } else { + self.init(templateString:"") + return nil } + } - /// Create a template with a template string - public init(templateString:String) { - let lexer = Lexer(templateString: templateString) - let tokens = lexer.tokenize() - parser = TokenParser(tokens: tokens) - } + /// Create a template with a template string + public init(templateString:String) { + let lexer = Lexer(templateString: templateString) + let tokens = lexer.tokenize() + parser = TokenParser(tokens: tokens) + } - /// Render the given template in a context - public func render(context:Context) -> Result { - switch parser.parse() { - case .Success(let nodes): - return renderNodes(nodes, context) + /// Render the given template in a context + public func render(context:Context) -> Result { + switch parser.parse() { + case .Success(let nodes): + return renderNodes(nodes, context) - case .Error(let error): - return .Error(error) - } + case .Error(let error): + return .Error(error) } + } - /// Render the given template without a context - public func render() -> Result { - let context = Context() - return render(context) - } + /// Render the given template without a context + public func render() -> Result { + let context = Context() + return render(context) + } } diff --git a/Stencil/TemplateLoader.swift b/Stencil/TemplateLoader.swift index 978466ea..99c942f0 100644 --- a/Stencil/TemplateLoader.swift +++ b/Stencil/TemplateLoader.swift @@ -11,35 +11,35 @@ import PathKit // A class for loading a template from disk public class TemplateLoader { - public let paths:[Path] + public let paths:[Path] - public init(paths:[Path]) { - self.paths = paths - } + public init(paths:[Path]) { + self.paths = paths + } - public init(bundle:[NSBundle]) { - self.paths = bundle.map { - return Path($0.bundlePath) - } + public init(bundle:[NSBundle]) { + self.paths = bundle.map { + return Path($0.bundlePath) } + } - public func loadTemplate(templateName:String) -> Template? { - return loadTemplate([templateName]) - } + public func loadTemplate(templateName:String) -> Template? { + return loadTemplate([templateName]) + } - public func loadTemplate(templateNames:[String]) -> Template? { - for path in paths { - for templateName in templateNames { - let templatePath = path + Path(templateName) - - if templatePath.exists() { - if let template = Template(path: templatePath) { - return template - } - } - } - } + public func loadTemplate(templateNames:[String]) -> Template? { + for path in paths { + for templateName in templateNames { + let templatePath = path + Path(templateName) - return nil + if templatePath.exists() { + if let template = Template(path: templatePath) { + return template + } + } + } } + + return nil + } } diff --git a/Stencil/TemplateLoader/Include.swift b/Stencil/TemplateLoader/Include.swift index c730c0c5..bb653880 100644 --- a/Stencil/TemplateLoader/Include.swift +++ b/Stencil/TemplateLoader/Include.swift @@ -2,43 +2,43 @@ import Foundation import PathKit extension String : Error { - public var description:String { - return self - } + public var description:String { + return self + } } public class IncludeNode : Node { - public let templateName:String - - public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { - let bits = token.contents.componentsSeparatedByString("\"") + public let templateName:String - if bits.count != 3 { - return .Error(error:NodeError(token: token, message: "Tag takes one argument, the template file to be included")) - } + public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { + let bits = token.contents.componentsSeparatedByString("\"") - return .Success(node:IncludeNode(templateName: bits[1])) + if bits.count != 3 { + return .Error(error:NodeError(token: token, message: "Tag takes one argument, the template file to be included")) } - public init(templateName:String) { - self.templateName = templateName - } + return .Success(node:IncludeNode(templateName: bits[1])) + } - public func render(context: Context) -> Result { - if let loader = context["loader"] as? TemplateLoader { - if let template = loader.loadTemplate(templateName) { - return template.render(context) - } - - let paths:String = join(", ", loader.paths.map { path in - return path.description - }) - let error = "Template '\(templateName)' not found in \(paths)" - return .Error(error) - } - - let error = "Template loader not in context" - return .Error(error) + public init(templateName:String) { + self.templateName = templateName + } + + public func render(context: Context) -> Result { + if let loader = context["loader"] as? TemplateLoader { + if let template = loader.loadTemplate(templateName) { + return template.render(context) + } + + let paths:String = join(", ", loader.paths.map { path in + return path.description + }) + let error = "Template '\(templateName)' not found in \(paths)" + return .Error(error) } + + let error = "Template loader not in context" + return .Error(error) + } } diff --git a/Stencil/Tokenizer.swift b/Stencil/Tokenizer.swift index 3fa1022c..9545bc1b 100644 --- a/Stencil/Tokenizer.swift +++ b/Stencil/Tokenizer.swift @@ -2,64 +2,64 @@ import Foundation public enum Token : Equatable { - /// A token representing a piece of text. - case Text(value:String) + /// A token representing a piece of text. + case Text(value:String) - /// A token representing a variable. - case Variable(value:String) + /// A token representing a variable. + case Variable(value:String) - /// A token representing a comment. - case Comment(value:String) + /// A token representing a comment. + case Comment(value:String) - /// A token representing a template block. - case Block(value:String) + /// A token representing a template block. + case Block(value:String) - /// Returns the underlying value as an array seperated by spaces - func components() -> [String] { - // TODO: Make this smarter and treat quoted strings as a single component - let characterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet() - - func strip(value: String) -> [String] { - return value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet) - } + /// Returns the underlying value as an array seperated by spaces + func components() -> [String] { + // TODO: Make this smarter and treat quoted strings as a single component + let characterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet() - switch self { - case .Block(let value): - return strip(value) - case .Variable(let value): - return strip(value) - case .Text(let value): - return strip(value) - case .Comment(let value): - return strip(value) - } + func strip(value: String) -> [String] { + return value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet) } - var contents:String { - switch self { - case .Block(let value): - return value - case .Variable(let value): - return value - case .Text(let value): - return value - case .Comment(let value): - return value - } + switch self { + case .Block(let value): + return strip(value) + case .Variable(let value): + return strip(value) + case .Text(let value): + return strip(value) + case .Comment(let value): + return strip(value) } + } + + var contents:String { + switch self { + case .Block(let value): + return value + case .Variable(let value): + return value + case .Text(let value): + return value + case .Comment(let value): + return value + } + } } public func ==(lhs:Token, rhs:Token) -> Bool { - switch (lhs, rhs) { - case (.Text(let lhsValue), .Text(let rhsValue)): - return lhsValue == rhsValue - case (.Variable(let lhsValue), .Variable(let rhsValue)): - return lhsValue == rhsValue - case (.Block(let lhsValue), .Block(let rhsValue)): - return lhsValue == rhsValue - case (.Comment(let lhsValue), .Comment(let rhsValue)): - return lhsValue == rhsValue - default: - return false - } + switch (lhs, rhs) { + case (.Text(let lhsValue), .Text(let rhsValue)): + return lhsValue == rhsValue + case (.Variable(let lhsValue), .Variable(let rhsValue)): + return lhsValue == rhsValue + case (.Block(let lhsValue), .Block(let rhsValue)): + return lhsValue == rhsValue + case (.Comment(let lhsValue), .Comment(let rhsValue)): + return lhsValue == rhsValue + default: + return false + } } diff --git a/Stencil/Variable.swift b/Stencil/Variable.swift index 70e5d9d3..f1329b10 100644 --- a/Stencil/Variable.swift +++ b/Stencil/Variable.swift @@ -3,51 +3,51 @@ import Foundation /// A structure used to represent a template variable, and to resolve it in a given context. public struct Variable : Equatable { - public let variable:String + public let variable:String - /// Create a variable with a string representing the variable - public init(_ variable:String) { - self.variable = variable - } + /// Create a variable with a string representing the variable + public init(_ variable:String) { + self.variable = variable + } - private func lookup() -> [String] { - return variable.componentsSeparatedByString(".") - } + private func lookup() -> [String] { + return variable.componentsSeparatedByString(".") + } - /// Resolve the variable in the given context - public func resolve(context:Context) -> AnyObject? { - var current:AnyObject? = context + /// Resolve the variable in the given context + public func resolve(context:Context) -> AnyObject? { + var current:AnyObject? = context - if (variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\"")) { - return variable.substringWithRange(variable.startIndex.successor() ..< variable.endIndex.predecessor()) - } + if (variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\"")) { + return variable.substringWithRange(variable.startIndex.successor() ..< variable.endIndex.predecessor()) + } - for bit in lookup() { - if let context = current as? Context { - current = context[bit] - } else if let dictionary = current as? Dictionary { - current = dictionary[bit] - } else if let array = current as? [AnyObject] { - if let index = bit.toInt() { - current = array[index] - } else if bit == "first" { - current = array.first - } else if bit == "last" { - current = array.last - } else if bit == "count" { - current = count(array) - } - } else if let object = current as? NSObject { - current = object.valueForKey(bit) - } else { - return nil - } + for bit in lookup() { + if let context = current as? Context { + current = context[bit] + } else if let dictionary = current as? Dictionary { + current = dictionary[bit] + } else if let array = current as? [AnyObject] { + if let index = bit.toInt() { + current = array[index] + } else if bit == "first" { + current = array.first + } else if bit == "last" { + current = array.last + } else if bit == "count" { + current = count(array) } - - return current + } else if let object = current as? NSObject { + current = object.valueForKey(bit) + } else { + return nil + } } + + return current + } } public func ==(lhs:Variable, rhs:Variable) -> Bool { - return lhs.variable == rhs.variable + return lhs.variable == rhs.variable } diff --git a/StencilTests/ContextTests.swift b/StencilTests/ContextTests.swift index 1b132a4f..2204514f 100644 --- a/StencilTests/ContextTests.swift +++ b/StencilTests/ContextTests.swift @@ -3,63 +3,63 @@ import XCTest import Stencil class ContextTests: XCTestCase { - var context:Context! + var context:Context! - override func setUp() { - context = Context(dictionary: ["name": "Kyle"]) - } + override func setUp() { + context = Context(dictionary: ["name": "Kyle"]) + } - func testItAllowsYouToRetrieveAValue() { - let name = context["name"] as! String - XCTAssertEqual(name, "Kyle") - } + func testItAllowsYouToRetrieveAValue() { + let name = context["name"] as! String + XCTAssertEqual(name, "Kyle") + } - func testItAllowsYouToSetValue() { - context["name"] = "Katie" + func testItAllowsYouToSetValue() { + context["name"] = "Katie" - let name = context["name"] as! String - XCTAssertEqual(name, "Katie") - } + let name = context["name"] as! String + XCTAssertEqual(name, "Katie") + } - func testItAllowsYouToRemoveAValue() { - context["name"] = nil - XCTAssertNil(context["name"]) - } + func testItAllowsYouToRemoveAValue() { + context["name"] = nil + XCTAssertNil(context["name"]) + } - func testItAllowsYouToRetrieveAValueFromParent() { - context.push() + func testItAllowsYouToRetrieveAValueFromParent() { + context.push() - let name = context["name"] as! String - XCTAssertEqual(name, "Kyle") - } + let name = context["name"] as! String + XCTAssertEqual(name, "Kyle") + } - func testItAllowsYouToOverideAParentVariable() { - context.push() - context["name"] = "Katie" + func testItAllowsYouToOverideAParentVariable() { + context.push() + context["name"] = "Katie" - let name = context["name"] as! String - XCTAssertEqual(name, "Katie") - } + let name = context["name"] as! String + XCTAssertEqual(name, "Katie") + } - func testShowAllowYouToPopVariablesRestoringPreviousState() { - context.push() - context["name"] = "Katie" - context.pop() + func testShowAllowYouToPopVariablesRestoringPreviousState() { + context.push() + context["name"] = "Katie" + context.pop() - let name = context["name"] as! String - XCTAssertEqual(name, "Kyle") - } + let name = context["name"] as! String + XCTAssertEqual(name, "Kyle") + } - func testItAllowsYouToPushADictionaryToTheStack() { - context.push(["name": "Katie"]) + func testItAllowsYouToPushADictionaryToTheStack() { + context.push(["name": "Katie"]) - let name = context["name"] as! String - XCTAssertEqual(name, "Katie") - } + let name = context["name"] as! String + XCTAssertEqual(name, "Katie") + } - func testItAllowsYouToCompareTwoContextsForEquality() { - let otherContext = Context(dictionary: ["name": "Kyle"]) + func testItAllowsYouToCompareTwoContextsForEquality() { + let otherContext = Context(dictionary: ["name": "Kyle"]) - XCTAssertEqual(otherContext, context) - } + XCTAssertEqual(otherContext, context) + } } diff --git a/StencilTests/LexerTests.swift b/StencilTests/LexerTests.swift index 8409bf02..3298c284 100644 --- a/StencilTests/LexerTests.swift +++ b/StencilTests/LexerTests.swift @@ -4,47 +4,47 @@ import Stencil class LexerTests: XCTestCase { - func testTokenizeText() { - let lexer = Lexer(templateString:"Hello World") - let tokens = lexer.tokenize() - - XCTAssertEqual(tokens.count, 1) - XCTAssertEqual(tokens.first!, Token.Text(value: "Hello World")) - } - - func testTokenizeComment() { - let lexer = Lexer(templateString:"{# Comment #}") - let tokens = lexer.tokenize() - - XCTAssertEqual(tokens.count, 1) - XCTAssertEqual(tokens.first!, Token.Comment(value: "Comment")) - } - - func testTokenizeVariable() { - let lexer = Lexer(templateString:"{{ Variable }}") - let tokens = lexer.tokenize() - - XCTAssertEqual(tokens.count, 1) - XCTAssertEqual(tokens.first!, Token.Variable(value: "Variable")) - } - - func testTokenizeMixture() { - let lexer = Lexer(templateString:"My name is {{ name }}.") - let tokens = lexer.tokenize() - - XCTAssertEqual(tokens.count, 3) - XCTAssertEqual(tokens[0], Token.Text(value: "My name is ")) - XCTAssertEqual(tokens[1], Token.Variable(value: "name")) - XCTAssertEqual(tokens[2], Token.Text(value: ".")) - } - - func testTokenizeTwoVariables() { // Don't be greedy - let lexer = Lexer(templateString:"{{ thing }}{{ name }}") - let tokens = lexer.tokenize() - - XCTAssertEqual(tokens.count, 2) - XCTAssertEqual(tokens[0], Token.Variable(value: "thing")) - XCTAssertEqual(tokens[1], Token.Variable(value: "name")) - } + func testTokenizeText() { + let lexer = Lexer(templateString:"Hello World") + let tokens = lexer.tokenize() + + XCTAssertEqual(tokens.count, 1) + XCTAssertEqual(tokens.first!, Token.Text(value: "Hello World")) + } + + func testTokenizeComment() { + let lexer = Lexer(templateString:"{# Comment #}") + let tokens = lexer.tokenize() + + XCTAssertEqual(tokens.count, 1) + XCTAssertEqual(tokens.first!, Token.Comment(value: "Comment")) + } + + func testTokenizeVariable() { + let lexer = Lexer(templateString:"{{ Variable }}") + let tokens = lexer.tokenize() + + XCTAssertEqual(tokens.count, 1) + XCTAssertEqual(tokens.first!, Token.Variable(value: "Variable")) + } + + func testTokenizeMixture() { + let lexer = Lexer(templateString:"My name is {{ name }}.") + let tokens = lexer.tokenize() + + XCTAssertEqual(tokens.count, 3) + XCTAssertEqual(tokens[0], Token.Text(value: "My name is ")) + XCTAssertEqual(tokens[1], Token.Variable(value: "name")) + XCTAssertEqual(tokens[2], Token.Text(value: ".")) + } + + func testTokenizeTwoVariables() { // Don't be greedy + let lexer = Lexer(templateString:"{{ thing }}{{ name }}") + let tokens = lexer.tokenize() + + XCTAssertEqual(tokens.count, 2) + XCTAssertEqual(tokens[0], Token.Variable(value: "thing")) + XCTAssertEqual(tokens[1], Token.Variable(value: "name")) + } } diff --git a/StencilTests/NodeTests.swift b/StencilTests/NodeTests.swift index 56c4c0dc..28f186e0 100644 --- a/StencilTests/NodeTests.swift +++ b/StencilTests/NodeTests.swift @@ -3,248 +3,248 @@ import XCTest import Stencil class ErrorNodeError : Error { - var description: String { - return "Node Error" - } + var description: String { + return "Node Error" + } } class ErrorNode : Node { - func render(context: Context) -> Result { + func render(context: Context) -> Result { - return .Error(ErrorNodeError()) - } + return .Error(ErrorNodeError()) + } } class NodeTests: XCTestCase { - var context:Context! - - override func setUp() { - context = Context(dictionary: [ - "name": "Kyle", - "age": 27, - "items": [1,2,3], - ]) - } + var context:Context! + + override func setUp() { + context = Context(dictionary: [ + "name": "Kyle", + "age": 27, + "items": [1,2,3], + ]) + } } class TextNodeTests: NodeTests { - func testTextNodeResolvesText() { - let node = TextNode(text:"Hello World") - let result = node.render(context) - - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, "Hello World") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } + func testTextNodeResolvesText() { + let node = TextNode(text:"Hello World") + let result = node.render(context) + + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, "Hello World") + case .Error(let error): + XCTAssert(false, "Unexpected error") } + } } class VariableNodeTests: NodeTests { - func testVariableNodeResolvesVariable() { - let node = VariableNode(variable:Variable("name")) - let result = node.render(context) - - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, "Kyle") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } + func testVariableNodeResolvesVariable() { + let node = VariableNode(variable:Variable("name")) + let result = node.render(context) + + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, "Kyle") + case .Error(let error): + XCTAssert(false, "Unexpected error") } + } - func testVariableNodeResolvesNonStringVariable() { - let node = VariableNode(variable:Variable("age")) - let result = node.render(context) + func testVariableNodeResolvesNonStringVariable() { + let node = VariableNode(variable:Variable("age")) + let result = node.render(context) - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, "27") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, "27") + case .Error(let error): + XCTAssert(false, "Unexpected error") } + } } class RenderNodeTests: NodeTests { - func testRenderingNodes() { - let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name")] as [Node] - switch renderNodes(nodes, context) { - case .Success(let result): - XCTAssertEqual(result, "Hello Kyle") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } - } - - func testRenderingNodesWithFailure() { - let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name"), ErrorNode()] as [Node] - - switch renderNodes(nodes, context) { - case .Success(let result): - XCTAssert(false, "Unexpected success") - case .Error(let error): - XCTAssertEqual("\(error)", "Node Error") - } - } + func testRenderingNodes() { + let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name")] as [Node] + switch renderNodes(nodes, context) { + case .Success(let result): + XCTAssertEqual(result, "Hello Kyle") + case .Error(let error): + XCTAssert(false, "Unexpected error") + } + } + + func testRenderingNodesWithFailure() { + let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name"), ErrorNode()] as [Node] + + switch renderNodes(nodes, context) { + case .Success(let result): + XCTAssert(false, "Unexpected success") + case .Error(let error): + XCTAssertEqual("\(error)", "Node Error") + } + } } class ForNodeTests: NodeTests { - func testForNodeRender() { - let node = ForNode(variable: "items", loopVariable: "item", nodes: [VariableNode(variable: "item")], emptyNodes:[]) - let result = node.render(context) - - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, "123") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } + func testForNodeRender() { + let node = ForNode(variable: "items", loopVariable: "item", nodes: [VariableNode(variable: "item")], emptyNodes:[]) + let result = node.render(context) + + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, "123") + case .Error(let error): + XCTAssert(false, "Unexpected error") } + } } 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) - assertSuccess(parser.parse()) { nodes in - 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) - assertSuccess(parser.parse()) { nodes in - 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 testParseIfWithoutEndIfError() { - let tokens = [ - Token.Block(value: "if value"), - ] - - let parser = TokenParser(tokens: tokens) - assertFailure(parser.parse(), "if: `endif` was not found.") - } - - func testParseIfNotWithoutEndIfError() { - let tokens = [ - Token.Block(value: "ifnot value"), - ] - - let parser = TokenParser(tokens: tokens) - assertFailure(parser.parse(), "ifnot: `endif` was not found.") - } - - // MARK: Rendering - - func testIfNodeRenderTruth() { - let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) - let result = node.render(context) - - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, "true") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } - } - - func testIfNodeRenderFalse() { - let node = IfNode(variable: "unknown", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) - let result = node.render(context) - - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, "false") - case .Error(let error): - XCTAssert(false, "Unexpected error") - } - } + // 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) + assertSuccess(parser.parse()) { nodes in + 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) + assertSuccess(parser.parse()) { nodes in + 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 testParseIfWithoutEndIfError() { + let tokens = [ + Token.Block(value: "if value"), + ] + + let parser = TokenParser(tokens: tokens) + assertFailure(parser.parse(), "if: `endif` was not found.") + } + + func testParseIfNotWithoutEndIfError() { + let tokens = [ + Token.Block(value: "ifnot value"), + ] + + let parser = TokenParser(tokens: tokens) + assertFailure(parser.parse(), "ifnot: `endif` was not found.") + } + + // MARK: Rendering + + func testIfNodeRenderTruth() { + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + let result = node.render(context) + + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, "true") + case .Error(let error): + XCTAssert(false, "Unexpected error") + } + } + + func testIfNodeRenderFalse() { + let node = IfNode(variable: "unknown", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + let result = node.render(context) + + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, "false") + case .Error(let error): + XCTAssert(false, "Unexpected error") + } + } } class NowNodeTests: NodeTests { - // MARK: Parsing + // MARK: Parsing - func testParseDefaultNow() { - let tokens = [ Token.Block(value: "now") ] - let parser = TokenParser(tokens: tokens) + func testParseDefaultNow() { + let tokens = [ Token.Block(value: "now") ] + let parser = TokenParser(tokens: tokens) - assertSuccess(parser.parse()) { nodes in - let node = nodes.first as! NowNode - XCTAssertEqual(nodes.count, 1) - XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"") - } + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as! NowNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"") } + } - func testParseNowWithFormat() { - let tokens = [ Token.Block(value: "now \"HH:mm\"") ] - let parser = TokenParser(tokens: tokens) + func testParseNowWithFormat() { + let tokens = [ Token.Block(value: "now \"HH:mm\"") ] + let parser = TokenParser(tokens: tokens) - assertSuccess(parser.parse()) { nodes in - let node = nodes.first as! NowNode - XCTAssertEqual(nodes.count, 1) - XCTAssertEqual(node.format.variable, "\"HH:mm\"") - } + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as! NowNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.format.variable, "\"HH:mm\"") } + } - // MARK: Rendering + // MARK: Rendering - func testRenderNowNode() { - let node = NowNode(format: Variable("\"yyyy-MM-dd\"")) - let result = node.render(context) + func testRenderNowNode() { + let node = NowNode(format: Variable("\"yyyy-MM-dd\"")) + let result = node.render(context) - let formatter = NSDateFormatter() - formatter.dateFormat = "yyyy-MM-dd" - let date = formatter.stringFromDate(NSDate()) + let formatter = NSDateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + let date = formatter.stringFromDate(NSDate()) - switch node.render(context) { - case .Success(let string): - XCTAssertEqual(string, date) - case .Error(let error): - XCTAssert(false, "Unexpected error") - } + switch node.render(context) { + case .Success(let string): + XCTAssertEqual(string, date) + case .Error(let error): + XCTAssert(false, "Unexpected error") } + } } diff --git a/StencilTests/ParserTests.swift b/StencilTests/ParserTests.swift index c3383496..28b07fab 100644 --- a/StencilTests/ParserTests.swift +++ b/StencilTests/ParserTests.swift @@ -3,47 +3,47 @@ import XCTest import Stencil class TokenParserTests: XCTestCase { - func testParsingTextToken() { - let parser = TokenParser(tokens: [ - Token.Text(value: "Hello World") - ]) - - assertSuccess(parser.parse()) { nodes in - let node = nodes.first as! TextNode - XCTAssertEqual(nodes.count, 1) - XCTAssertEqual(node.text, "Hello World") - } + func testParsingTextToken() { + let parser = TokenParser(tokens: [ + Token.Text(value: "Hello World") + ]) + + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as! TextNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.text, "Hello World") } + } - func testParsingVariableToken() { - let parser = TokenParser(tokens: [ - Token.Variable(value: "name") - ]) + func testParsingVariableToken() { + let parser = TokenParser(tokens: [ + Token.Variable(value: "name") + ]) - assertSuccess(parser.parse()) { nodes in - let node = nodes.first as! VariableNode - XCTAssertEqual(nodes.count, 1) - XCTAssertEqual(node.variable, Variable("name")) - } + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as! VariableNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.variable, Variable("name")) } + } - func testParsingCommentToken() { - let parser = TokenParser(tokens: [ - Token.Comment(value: "Secret stuff!") - ]) + func testParsingCommentToken() { + let parser = TokenParser(tokens: [ + Token.Comment(value: "Secret stuff!") + ]) - assertSuccess(parser.parse()) { nodes in - XCTAssertEqual(nodes.count, 0) - } + assertSuccess(parser.parse()) { nodes in + XCTAssertEqual(nodes.count, 0) } + } - func testParsingTagToken() { - let parser = TokenParser(tokens: [ - Token.Block(value: "now"), - ]) + func testParsingTagToken() { + let parser = TokenParser(tokens: [ + Token.Block(value: "now"), + ]) - assertSuccess(parser.parse()) { nodes in - XCTAssertEqual(nodes.count, 1) - } + assertSuccess(parser.parse()) { nodes in + XCTAssertEqual(nodes.count, 1) } + } } diff --git a/StencilTests/StencilTests.swift b/StencilTests/StencilTests.swift index 7c8f003d..bf7f7d10 100644 --- a/StencilTests/StencilTests.swift +++ b/StencilTests/StencilTests.swift @@ -3,77 +3,77 @@ import XCTest import Stencil func assertSuccess(result:TokenParser.Results, block:(([Node]) -> ())) { - switch result { - case .Success(let nodes): - block(nodes) - case .Error(let error): - XCTAssert(false, "Unexpected error") - } + switch result { + case .Success(let nodes): + block(nodes) + case .Error(let error): + XCTAssert(false, "Unexpected error") + } } func assertFailure(result:TokenParser.Results, description:String) { - switch result { - case .Success(let nodes): - XCTAssert(false, "Unexpected error") - case .Error(let error): - XCTAssertEqual("\(error)", description) - } + switch result { + case .Success(let nodes): + XCTAssert(false, "Unexpected error") + case .Error(let error): + XCTAssertEqual("\(error)", description) + } } class CustomNode : Node { - func render(context:Context) -> Result { - return .Success("Hello World") - } + func render(context:Context) -> Result { + return .Success("Hello World") + } } class StencilTests: XCTestCase { - func testReadmeExample() { - let templateString = "There are {{ articles.count }} articles.\n" + - "\n" + - "{% for article in articles %}" + - " - {{ article.title }} by {{ article.author }}.\n" + - "{% endfor %}\n" + func testReadmeExample() { + let templateString = "There are {{ articles.count }} articles.\n" + + "\n" + + "{% for article in articles %}" + + " - {{ article.title }} by {{ article.author }}.\n" + + "{% endfor %}\n" - let context = Context(dictionary: [ - "articles": [ - [ "title": "Migrating from OCUnit to XCTest", "author": "Kyle Fuller" ], - [ "title": "Memory Management with ARC", "author": "Kyle Fuller" ], - ] - ]) + let context = Context(dictionary: [ + "articles": [ + [ "title": "Migrating from OCUnit to XCTest", "author": "Kyle Fuller" ], + [ "title": "Memory Management with ARC", "author": "Kyle Fuller" ], + ] + ]) - let template = Template(templateString:templateString) - let result = template.render(context) + let template = Template(templateString:templateString) + let result = template.render(context) - let fixture = "There are 2 articles.\n" + - "\n" + - " - Migrating from OCUnit to XCTest by Kyle Fuller.\n" + - " - Memory Management with ARC by Kyle Fuller.\n" + - "\n" + let fixture = "There are 2 articles.\n" + + "\n" + + " - Migrating from OCUnit to XCTest by Kyle Fuller.\n" + + " - Memory Management with ARC by Kyle Fuller.\n" + + "\n" - XCTAssertEqual(result, Result.Success(fixture)) - } - - func testCustomTag() { - let templateString = "{% custom %}" - let template = Template(templateString:templateString) + XCTAssertEqual(result, Result.Success(fixture)) + } - template.parser.registerTag("custom") { parser, token in - return .Success(node:CustomNode()) - } + func testCustomTag() { + let templateString = "{% custom %}" + let template = Template(templateString:templateString) - let result = template.render() - XCTAssertEqual(result, Result.Success("Hello World")) + template.parser.registerTag("custom") { parser, token in + return .Success(node:CustomNode()) } - func testSimpleCustomTag() { - let templateString = "{% custom %}" - let template = Template(templateString:templateString) + let result = template.render() + XCTAssertEqual(result, Result.Success("Hello World")) + } - template.parser.registerSimpleTag("custom") { context in - return .Success("Hello World") - } + func testSimpleCustomTag() { + let templateString = "{% custom %}" + let template = Template(templateString:templateString) - let result = template.render() - XCTAssertEqual(result, Result.Success("Hello World")) + template.parser.registerSimpleTag("custom") { context in + return .Success("Hello World") } + + let result = template.render() + XCTAssertEqual(result, Result.Success("Hello World")) + } } diff --git a/StencilTests/TemplateLoader/IncludeTests.swift b/StencilTests/TemplateLoader/IncludeTests.swift index 7336d4ce..735c7689 100644 --- a/StencilTests/TemplateLoader/IncludeTests.swift +++ b/StencilTests/TemplateLoader/IncludeTests.swift @@ -5,71 +5,71 @@ import PathKit class IncludeTests: NodeTests { - var loader:TemplateLoader! + var loader:TemplateLoader! - override func setUp() { - super.setUp() + override func setUp() { + super.setUp() - let path = (Path(__FILE__) + Path("../..")).absolute() - loader = TemplateLoader(paths: [path]) - } + let path = (Path(__FILE__) + Path("../..")).absolute() + loader = TemplateLoader(paths: [path]) + } - // MARK: Parsing + // MARK: Parsing - func testParseMissingTemplate() { - let tokens = [ Token.Block(value: "include") ] - let parser = TokenParser(tokens: tokens) + func testParseMissingTemplate() { + let tokens = [ Token.Block(value: "include") ] + let parser = TokenParser(tokens: tokens) - assertFailure(parser.parse(), "include: Tag takes one argument, the template file to be included") - } + assertFailure(parser.parse(), "include: Tag takes one argument, the template file to be included") + } - func testParse() { - let tokens = [ Token.Block(value: "include \"test.html\"") ] - let parser = TokenParser(tokens: tokens) + func testParse() { + let tokens = [ Token.Block(value: "include \"test.html\"") ] + let parser = TokenParser(tokens: tokens) - assertSuccess(parser.parse()) { nodes in - let node = nodes.first as! IncludeNode - XCTAssertEqual(nodes.count, 1) - XCTAssertEqual(node.templateName, "test.html") - } + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as! IncludeNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.templateName, "test.html") } + } - // MARK: Render + // MARK: Render - func testRenderWithoutLoader() { - let node = IncludeNode(templateName: "test.html") - let result = node.render(Context()) + func testRenderWithoutLoader() { + let node = IncludeNode(templateName: "test.html") + let result = node.render(Context()) - switch result { - case .Success(let string): - XCTAssert(false, "Unexpected error") - case .Error(let error): - XCTAssertEqual("\(error)", "Template loader not in context") - } + switch result { + case .Success(let string): + XCTAssert(false, "Unexpected error") + case .Error(let error): + XCTAssertEqual("\(error)", "Template loader not in context") } + } - func testRenderWithoutTemplateNamed() { - let node = IncludeNode(templateName: "unknown.html") - let result = node.render(Context(dictionary:["loader":loader])) + func testRenderWithoutTemplateNamed() { + let node = IncludeNode(templateName: "unknown.html") + let result = node.render(Context(dictionary:["loader":loader])) - switch result { - case .Success(let string): - XCTAssert(false, "Unexpected error") - case .Error(let error): - XCTAssertTrue("\(error)".hasPrefix("Template 'unknown.html' not found")) - } + switch result { + case .Success(let string): + XCTAssert(false, "Unexpected error") + case .Error(let error): + XCTAssertTrue("\(error)".hasPrefix("Template 'unknown.html' not found")) } + } - func testRender() { - let node = IncludeNode(templateName: "test.html") - let result = node.render(Context(dictionary:["loader":loader, "target": "World"])) + func testRender() { + let node = IncludeNode(templateName: "test.html") + let result = node.render(Context(dictionary:["loader":loader, "target": "World"])) - switch result { - case .Success(let string): - XCTAssertEqual(string, "Hello World!") - case .Error(let error): - XCTAssert(false, "Unexpected error: \(error)") - } + switch result { + case .Success(let string): + XCTAssertEqual(string, "Hello World!") + case .Error(let error): + XCTAssert(false, "Unexpected error: \(error)") } + } } diff --git a/StencilTests/TemplateLoaderTests.swift b/StencilTests/TemplateLoaderTests.swift index 7960f8c7..da43ce57 100644 --- a/StencilTests/TemplateLoaderTests.swift +++ b/StencilTests/TemplateLoaderTests.swift @@ -5,20 +5,20 @@ import PathKit class TemplateLoaderTests: XCTestCase { - func testLoadingUnknownTemplate() { - let loader = TemplateLoader(paths:[]) - XCTAssertNil(loader.loadTemplate("unknown.html")) - } + func testLoadingUnknownTemplate() { + let loader = TemplateLoader(paths:[]) + XCTAssertNil(loader.loadTemplate("unknown.html")) + } - func testLoadingUnknownTemplates() { - let loader = TemplateLoader(paths:[]) - XCTAssertNil(loader.loadTemplate(["unknown.html", "unknown2.html"])) - } + func testLoadingUnknownTemplates() { + let loader = TemplateLoader(paths:[]) + XCTAssertNil(loader.loadTemplate(["unknown.html", "unknown2.html"])) + } - func testLoadingTemplate() { - let path = (Path(__FILE__) + Path("..")).absolute() - let loader = TemplateLoader(paths: [path]) - XCTAssertTrue(loader.loadTemplate("test.html") != nil) - } + func testLoadingTemplate() { + let path = (Path(__FILE__) + Path("..")).absolute() + let loader = TemplateLoader(paths: [path]) + XCTAssertTrue(loader.loadTemplate("test.html") != nil) + } } diff --git a/StencilTests/TemplateTests.swift b/StencilTests/TemplateTests.swift index 06466876..8a833feb 100644 --- a/StencilTests/TemplateTests.swift +++ b/StencilTests/TemplateTests.swift @@ -4,11 +4,11 @@ import Stencil class TemplateTests: XCTestCase { - func testTemplate() { - let context = Context(dictionary: [ "name": "Kyle" ]) - let template = Template(templateString: "Hello World") - let result = template.render(context) - XCTAssertEqual(result, Result.Success("Hello World")) - } + func testTemplate() { + let context = Context(dictionary: [ "name": "Kyle" ]) + let template = Template(templateString: "Hello World") + let result = template.render(context) + XCTAssertEqual(result, Result.Success("Hello World")) + } } diff --git a/StencilTests/VariableTests.swift b/StencilTests/VariableTests.swift index c1a7630a..363556f5 100644 --- a/StencilTests/VariableTests.swift +++ b/StencilTests/VariableTests.swift @@ -3,60 +3,60 @@ import XCTest import Stencil @objc class Object : NSObject { - let title = "Hello World" + let title = "Hello World" } class VariableTests: XCTestCase { - var context:Context! - - override func setUp() { - context = Context(dictionary: [ - "name": "Kyle", - "contacts": [ "Katie", "Orta", ], - "profiles": [ "github": "kylef", ], - "object": Object(), - ]) - } - - func testResolvingStringLiteral() { - let variable = Variable("\"name\"") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "name") - } - - func testResolvingVariable() { - let variable = Variable("name") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "Kyle") - } - - func testResolvingItemFromDictionary() { - let variable = Variable("profiles.github") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "kylef") - } - - func testResolvingItemFromArrayWithIndex() { - let variable = Variable("contacts.0") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "Katie") - } - - func testResolvingFirstItemFromArray() { - let variable = Variable("contacts.first") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "Katie") - } - - func testResolvingLastItemFromArray() { - let variable = Variable("contacts.last") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "Orta") - } - - func testResolvingValueViaKVO() { - let variable = Variable("object.title") - let result = variable.resolve(context) as! String - XCTAssertEqual(result, "Hello World") - } + var context:Context! + + override func setUp() { + context = Context(dictionary: [ + "name": "Kyle", + "contacts": [ "Katie", "Orta", ], + "profiles": [ "github": "kylef", ], + "object": Object(), + ]) + } + + func testResolvingStringLiteral() { + let variable = Variable("\"name\"") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "name") + } + + func testResolvingVariable() { + let variable = Variable("name") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "Kyle") + } + + func testResolvingItemFromDictionary() { + let variable = Variable("profiles.github") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "kylef") + } + + func testResolvingItemFromArrayWithIndex() { + let variable = Variable("contacts.0") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "Katie") + } + + func testResolvingFirstItemFromArray() { + let variable = Variable("contacts.first") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "Katie") + } + + func testResolvingLastItemFromArray() { + let variable = Variable("contacts.last") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "Orta") + } + + func testResolvingValueViaKVO() { + let variable = Variable("object.title") + let result = variable.resolve(context) as! String + XCTAssertEqual(result, "Hello World") + } }