From 804bc658b2d65a57985001b2d19bc285f1223498 Mon Sep 17 00:00:00 2001 From: Marius Rackwitz Date: Sun, 26 Oct 2014 13:12:30 +0000 Subject: [PATCH 1/5] Changes for Xcode 6.1 GM --- Stencil/Lexer.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Stencil/Lexer.swift b/Stencil/Lexer.swift index 71147bb..9d10cd8 100644 --- a/Stencil/Lexer.swift +++ b/Stencil/Lexer.swift @@ -10,7 +10,7 @@ import Foundation public struct Lexer { public let templateString:String - let regex = NSRegularExpression(pattern: "(\\{\\{.*?\\}\\}|\\{%.*?%\\}|\\{#.*?#\\})", options: nil, error: nil) + let regex = NSRegularExpression(pattern: "(\\{\\{.*?\\}\\}|\\{%.*?%\\}|\\{#.*?#\\})", options: nil, error: nil)! public init(templateString:String) { self.templateString = templateString @@ -41,7 +41,8 @@ public struct Lexer { let range = NSMakeRange(0, countElements(templateString)) var lastIndex = 0 let nsTemplateString = templateString as NSString - regex.enumerateMatchesInString(templateString, options: nil, range: range) { (result, flags, b) in + 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)) From 1bd2b21a1384c346989cb7ba6dd11357b58bf24e Mon Sep 17 00:00:00 2001 From: Marius Rackwitz Date: Sun, 26 Oct 2014 13:12:49 +0000 Subject: [PATCH 2/5] Make use of failable initializers --- Stencil/Template.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Stencil/Template.swift b/Stencil/Template.swift index 8719290..25122de 100644 --- a/Stencil/Template.swift +++ b/Stencil/Template.swift @@ -11,11 +11,11 @@ import Foundation public class Template { let parser:TokenParser - public convenience init(named:String) { + public convenience init?(named:String) { self.init(named:named, inBundle:nil) } - public convenience init(named:String, inBundle bundle:NSBundle?) { + public convenience init?(named:String, inBundle bundle:NSBundle?) { var url:NSURL? if let bundle = bundle { @@ -27,10 +27,15 @@ public class Template { self.init(URL:url!) } - public convenience init(URL:NSURL) { + public convenience init?(URL:NSURL) { var error:NSError? - let templateString = NSString.stringWithContentsOfURL(URL, encoding: NSUTF8StringEncoding, error: &error) - self.init(templateString:templateString) + let maybeTemplateString = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: &error) + if let templateString = maybeTemplateString { + self.init(templateString:templateString) + } else { + self.init(templateString:"") + return nil + } } public init(templateString:String) { From 7b89c3229526ca552d1cb312a7f6c06ac44d0f5f Mon Sep 17 00:00:00 2001 From: Marius Rackwitz Date: Sun, 26 Oct 2014 13:13:06 +0000 Subject: [PATCH 3/5] Reduced code-duplication --- Stencil/Tokenizer.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Stencil/Tokenizer.swift b/Stencil/Tokenizer.swift index d78b764..53a1268 100644 --- a/Stencil/Tokenizer.swift +++ b/Stencil/Tokenizer.swift @@ -18,16 +18,20 @@ public enum Token : Equatable { 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) + } switch self { case .Block(let value): - return value.value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet) + return strip(value) case .Variable(let value): - return value.value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet) + return strip(value) case .Text(let value): - return value.value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet) + return strip(value) case .Comment(let value): - return value.value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet) + return strip(value) } } From 25b86dea934c5e2e4e2544fa406663d7c190f332 Mon Sep 17 00:00:00 2001 From: Marius Rackwitz Date: Sun, 26 Oct 2014 13:24:50 +0000 Subject: [PATCH 4/5] Use enum instead of pair as result type for Template --- Stencil.xcodeproj/project.pbxproj | 12 +++++++---- Stencil/Node.swift | 4 ---- Stencil/Result.swift | 33 +++++++++++++++++++++++++++++++ Stencil/Template.swift | 14 ++++++++----- StencilTests/StencilTests.swift | 2 +- StencilTests/TemplateTests.swift | 5 ++--- 6 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 Stencil/Result.swift diff --git a/Stencil.xcodeproj/project.pbxproj b/Stencil.xcodeproj/project.pbxproj index 2937d28..57bcf9f 100644 --- a/Stencil.xcodeproj/project.pbxproj +++ b/Stencil.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 71CE4C0A19FD29D000B9E0C5 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71CE4C0919FD29D000B9E0C5 /* Result.swift */; }; 7725B3CB19F92B4F002CF74B /* VariableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7725B3CA19F92B4F002CF74B /* VariableTests.swift */; }; 7725B3CD19F92B61002CF74B /* Variable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7725B3CC19F92B61002CF74B /* Variable.swift */; }; 7725B3CF19F94214002CF74B /* Tokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7725B3CE19F94214002CF74B /* Tokenizer.swift */; }; @@ -36,6 +37,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 71CE4C0919FD29D000B9E0C5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; 7725B3CA19F92B4F002CF74B /* VariableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VariableTests.swift; sourceTree = ""; }; 7725B3CC19F92B61002CF74B /* Variable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Variable.swift; sourceTree = ""; }; 7725B3CE19F94214002CF74B /* Tokenizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tokenizer.swift; sourceTree = ""; }; @@ -97,14 +99,15 @@ 77FAAE5419F91E480029DC5E /* Stencil */ = { isa = PBXGroup; children = ( - 77FAAE5719F91E480029DC5E /* Stencil.h */, 77FAAE6E19F920750029DC5E /* Context.swift */, - 7725B3CC19F92B61002CF74B /* Variable.swift */, - 7725B3CE19F94214002CF74B /* Tokenizer.swift */, + 77EB082A19FA8600001870F1 /* Lexer.swift */, 7725B3D419F9438F002CF74B /* Node.swift */, 7725B3D619F94A43002CF74B /* Parser.swift */, - 77EB082A19FA8600001870F1 /* Lexer.swift */, + 71CE4C0919FD29D000B9E0C5 /* Result.swift */, + 77FAAE5719F91E480029DC5E /* Stencil.h */, 77EB082419F96E88001870F1 /* Template.swift */, + 7725B3CE19F94214002CF74B /* Tokenizer.swift */, + 7725B3CC19F92B61002CF74B /* Variable.swift */, 77FAAE5519F91E480029DC5E /* Supporting Files */, ); path = Stencil; @@ -254,6 +257,7 @@ 7725B3D719F94A43002CF74B /* Parser.swift in Sources */, 77EB082519F96E88001870F1 /* Template.swift in Sources */, 7725B3CD19F92B61002CF74B /* Variable.swift in Sources */, + 71CE4C0A19FD29D000B9E0C5 /* Result.swift in Sources */, 7725B3D519F9438F002CF74B /* Node.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Stencil/Node.swift b/Stencil/Node.swift index 902ac59..31f6212 100644 --- a/Stencil/Node.swift +++ b/Stencil/Node.swift @@ -8,10 +8,6 @@ import Foundation -public protocol Error : Printable { - -} - struct NodeError : Error { let token:Token let message:String diff --git a/Stencil/Result.swift b/Stencil/Result.swift new file mode 100644 index 0000000..369387a --- /dev/null +++ b/Stencil/Result.swift @@ -0,0 +1,33 @@ +// +// Result.swift +// Stencil +// +// Created by Marius Rackwitz on 26/10/14. +// Copyright (c) 2014 Cocode. All rights reserved. +// + +import Foundation + +public protocol Error : Printable { + +} + +public func ==(lhs:Error, rhs:Error) -> Bool { + return lhs.description == rhs.description +} + +public enum Result : Equatable { + case Success(string: String) + case Error(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 + } +} diff --git a/Stencil/Template.swift b/Stencil/Template.swift index 25122de..0407c5b 100644 --- a/Stencil/Template.swift +++ b/Stencil/Template.swift @@ -44,15 +44,19 @@ public class Template { parser = TokenParser(tokens: tokens) } - public func render(context:Context) -> (string:String?, error:Error?) { + public func render(context:Context) -> Result { let (nodes, error) = parser.parse() if let error = error { - return (nil, error) + return .Error(error: error) } else if let nodes = nodes { - return renderNodes(nodes, context) + let result = renderNodes(nodes, context) + if let string = result.0 { + return .Success(string: string) + } else { + return .Error(error: result.1!) + } } - - return (nil, nil) + return .Success(string: "") } } diff --git a/StencilTests/StencilTests.swift b/StencilTests/StencilTests.swift index 7919726..1c339fe 100644 --- a/StencilTests/StencilTests.swift +++ b/StencilTests/StencilTests.swift @@ -34,6 +34,6 @@ class StencilTests: XCTestCase { " - Memory Management with ARC by Kyle Fuller.\n" + "\n" - XCTAssertEqual(result.string!, fixture) + XCTAssertEqual(result, Result.Success(string: fixture)) } } diff --git a/StencilTests/TemplateTests.swift b/StencilTests/TemplateTests.swift index d611a26..cc1adf1 100644 --- a/StencilTests/TemplateTests.swift +++ b/StencilTests/TemplateTests.swift @@ -15,9 +15,8 @@ class TemplateTests: XCTestCase { func testTemplate() { let context = Context(dictionary: [ "name": "Kyle" ]) let template = Template(templateString: "Hello World") - let (string, error) = template.render(context) - XCTAssertEqual(string!, "Hello World") - XCTAssertTrue(error == nil) + let result = template.render(context) + XCTAssertEqual(result, Result.Success(string: "Hello World")) } } From d6a6696c200335b82b20099e4a16a15d79600f57 Mon Sep 17 00:00:00 2001 From: Marius Rackwitz Date: Sun, 26 Oct 2014 13:37:41 +0000 Subject: [PATCH 5/5] [Readme] Update code example for enum API --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1c03161..cf88dde 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,12 @@ let context = Context(dictionary: [ let template = Template(named: "template.stencil") let result = template.render(context) -if let error = result.error { - println("There was an error rendering your template (\(error)).") +switch result { + case .Error(let error): + println("There was an error rendering your template (\(error)).") + case .Success(let string): + println("\(string)") } - -println("\(result.string)") ``` ## Philosophy