From 45e3e71ccf64691e7cd9a6e2c94ffe8257b6a684 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Tue, 20 Sep 2016 08:41:37 +0200
Subject: [PATCH 01/46] WIP: Swift 3 (#32)
---
Sources/Box.swift | 129 ++++-----
Sources/Common.swift | 26 +-
Sources/Configuration.swift | 6 +-
Sources/Context.swift | 76 +++---
Sources/CoreFunctions.swift | 52 ++--
Sources/EachFilter.swift | 10 +-
Sources/Expression.swift | 16 +-
Sources/ExpressionGenerator.swift | 26 +-
Sources/ExpressionInvocation.swift | 18 +-
Sources/ExpressionParser.swift | 254 +++++++++---------
Sources/HTMLEscapeHelper.swift | 10 +-
Sources/JavascriptEscapeHelper.swift | 12 +-
Sources/Localizer.swift | 34 +--
Sources/Logger.swift | 10 +-
Sources/MustacheBox.swift | 28 +-
Sources/NSFormatter.swift | 10 +-
Sources/RenderingEngine.swift | 62 ++---
Sources/SectionTag.swift | 4 +-
Sources/Tag.swift | 6 +-
Sources/Template.swift | 22 +-
Sources/TemplateAST.swift | 20 +-
Sources/TemplateASTNode.swift | 36 +--
Sources/TemplateCompiler.swift | 232 ++++++++--------
Sources/TemplateGenerator.swift | 44 +--
Sources/TemplateParser.swift | 190 ++++++-------
Sources/TemplateRepository.swift | 94 +++----
Sources/TemplateToken.swift | 32 +--
Sources/URLEscapeHelper.swift | 18 +-
Sources/VariableTag.swift | 6 +-
Sources/ZipFilter.swift | 8 +-
Tests/Public/BoxTests.swift | 16 +-
.../ConfigurationContentTypeTests.swift | 98 +++----
Tests/Public/ContextTests/ContextTests.swift | 2 +-
...ntextValueForMustacheExpressionTests.swift | 10 +-
.../MustacheBoxDocumentationTests.swift | 4 +-
.../MustacheRenderableGuideTests.swift | 10 +-
.../DocumentationTests/ReadMeTests.swift | 48 +++-
Tests/Public/FilterTests/FilterTests.swift | 64 ++---
.../FilterTests/VariadicFilterTests.swift | 4 +-
Tests/Public/FoundationCollectionTests.swift | 6 +-
Tests/Public/HookFunctionTests.swift | 40 +--
Tests/Public/LambdaTests.swift | 8 +-
Tests/Public/ObjcKeyAccessTests.swift | 4 +-
Tests/Public/RenderFunctionTests.swift | 76 +++---
.../ServicesTests/EachFilterTests.swift | 4 +-
.../Public/ServicesTests/LocalizerTests.swift | 20 +-
.../ServicesTests/NSFormatterTests.swift | 86 +++---
.../ServicesTests/StandardLibraryTests.swift | 2 +-
Tests/Public/SuitesTests/SuiteTestCase.swift | 62 ++---
Tests/Public/TagTests/TagTests.swift | 104 +++----
.../TemplateRepositoryBundleTests.swift | 12 +-
.../TemplateRepositoryDataSourceTests.swift | 26 +-
.../TemplateRepositoryDictionaryTests.swift | 8 +-
.../TemplateRepositoryPathTests.swift | 46 ++--
.../TemplateRepositoryURLTests.swift | 44 +--
.../TemplateRepositoryTests.swift | 16 +-
.../TemplateFromMethodsTests.swift | 126 ++++-----
Xcode/Mustache.xcodeproj/project.pbxproj | 6 +
58 files changed, 1223 insertions(+), 1220 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index f032ff4a..cacec431 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -180,10 +180,10 @@ extension Bool : MustacheBoxable {
boolValue: self,
render: { (info: RenderingInfo) in
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ bool }}
return Rendering("\(self ? 1 : 0)") // Behave like [NSNumber numberWithBool:]
- case .Section:
+ case .section:
if info.enumerationItem {
// {{# bools }}...{{/ bools }}
return try info.tag.render(info.context.extendedContext(Box(self)))
@@ -237,10 +237,10 @@ extension Int : MustacheBoxable {
boolValue: (self != 0),
render: { (info: RenderingInfo) in
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ int }}
return Rendering("\(self)")
- case .Section:
+ case .section:
if info.enumerationItem {
// {{# ints }}...{{/ ints }}
return try info.tag.render(info.context.extendedContext(Box(self)))
@@ -294,10 +294,10 @@ extension UInt : MustacheBoxable {
boolValue: (self != 0),
render: { (info: RenderingInfo) in
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ uint }}
return Rendering("\(self)")
- case .Section:
+ case .section:
if info.enumerationItem {
// {{# uints }}...{{/ uints }}
return try info.tag.render(info.context.extendedContext(Box(self)))
@@ -351,10 +351,10 @@ extension Double : MustacheBoxable {
boolValue: (self != 0.0),
render: { (info: RenderingInfo) in
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ double }}
return Rendering("\(self)")
- case .Section:
+ case .section:
if info.enumerationItem {
// {{# doubles }}...{{/ doubles }}
return try info.tag.render(info.context.extendedContext(Box(self)))
@@ -518,7 +518,7 @@ extension NSObject : MustacheBoxable {
// Enumerable
// Turn enumerable into a Swift array of MustacheBoxes that we know how to box
- let array = GeneratorSequence(NSFastGenerator(enumerable)).map(BoxAnyObject)
+ let array = IteratorSequence(NSFastEnumerationIterator(enumerable)).map(BoxAnyObject)
return array.mustacheBoxWithArrayValue(self, box: { $0 })
} else {
@@ -528,9 +528,9 @@ extension NSObject : MustacheBoxable {
return MustacheBox(
value: self,
keyedSubscript: { (key: String) in
- if GRMustacheKeyAccess.isSafeMustacheKey(key, forObject: self) {
+ if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
// Use valueForKey: for safe keys
- return BoxAnyObject(self.valueForKey(key))
+ return BoxAnyObject(self.value(forKey: key))
} else {
// Missing key
return Box()
@@ -618,28 +618,28 @@ extension NSNumber {
// This would make template rendering depend on the size of Int, and
// yield very weird platform-related issues. So keep it simple, stupid.
- let objCType = String.fromCString(self.objCType)!
+ let objCType = String(cString: self.objCType)
switch objCType {
case "c":
- return Box(Int(charValue))
+ return Box(Int(int8Value))
case "C":
- return Box(UInt(unsignedCharValue))
+ return Box(UInt(uint8Value))
case "s":
- return Box(Int(shortValue))
+ return Box(Int(int16Value))
case "S":
- return Box(UInt(unsignedShortValue))
+ return Box(UInt(uint16Value))
case "i":
- return Box(Int(intValue))
+ return Box(Int(int32Value))
case "I":
- return Box(UInt(unsignedIntValue))
+ return Box(UInt(uint32Value))
case "l":
- return Box(Int(longValue))
+ return Box(Int(intValue))
case "L":
- return Box(UInt(unsignedLongValue))
+ return Box(UInt(uintValue))
case "q":
- return Box(Int(longLongValue)) // May fail on 32-bits architectures, right?
+ return Box(Int(int64Value)) // May fail on 32-bits architectures, right?
case "Q":
- return Box(UInt(unsignedLongLongValue)) // May fail on 32-bits architectures, right?
+ return Box(UInt(uint64Value)) // May fail on 32-bits architectures, right?
case "f":
return Box(Double(floatValue))
case "d":
@@ -708,7 +708,7 @@ templates.
- returns: A MustacheBox that wraps *boxable*.
*/
-public func Box(boxable: MustacheBoxable?) -> MustacheBox {
+public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
return boxable?.mustacheBox ?? Box()
}
@@ -741,7 +741,7 @@ See the documentation of `NSObject.mustacheBox`.
- parameter object: An NSObject.
- returns: A MustacheBox that wraps *object*.
*/
-public func Box(object: NSObject?) -> MustacheBox {
+public func Box(_ object: NSObject?) -> MustacheBox {
return object?.mustacheBox ?? Box()
}
@@ -870,40 +870,11 @@ Otherwise, GRMustache logs a warning, and returns the empty box.
- parameter object: An object.
- returns: A MustacheBox that wraps *object*.
*/
-private func BoxAnyObject(object: AnyObject?) -> MustacheBox {
+private func BoxAnyObject(_ object: Any?) -> MustacheBox {
if let boxable = object as? MustacheBoxable {
return boxable.mustacheBox
- } else if let object: AnyObject = object {
-
- // IMPLEMENTATION NOTE
- //
- // In the example below, the Thing class can not be turned into any
- // relevant MustacheBox.
- //
- // Yet we can not prevent the user from trying to box it, because the
- // Thing class adopts the AnyObject protocol, just as all Swift classes.
- //
- // class Thing { }
- //
- // // Compilation error (OK): cannot find an overload for 'Box' that accepts an argument list of type '(Thing)'
- // Box(Thing())
- //
- // // Runtime warning (Not OK but unavoidable): value `Thing` is not NSObject and does not conform to MustacheBoxable: it is discarded.
- // BoxAnyObject(Thing())
- //
- // // Foundation collections can also contain unsupported classes:
- // let array = NSArray(object: Thing())
- //
- // // Runtime warning (Not OK but unavoidable): value `Thing` is not NSObject and does not conform to MustacheBoxable: it is discarded.
- // Box(array)
- //
- // // Compilation error (OK): cannot find an overload for 'Box' that accepts an argument list of type '(AnyObject)'
- // Box(array[0])
- //
- // // Runtime warning (Not OK but unavoidable): value `Thing` is not NSObject and does not conform to MustacheBoxable: it is discarded.
- // BoxAnyObject(array[0])
-
- NSLog("Mustache.BoxAnyObject(): value `\(object)` is does not conform to MustacheBoxable: it is discarded.")
+ } else if let object = object {
+ NSLog("Mustache.BoxAnyObject(): value `\(object)` does not conform to MustacheBoxable: it is discarded.")
return Box()
} else {
return Box()
@@ -930,7 +901,7 @@ private func BoxAnyObject(object: AnyObject?) -> MustacheBox {
// So we don't support boxing of sequences.
// Support for all collections
-extension CollectionType {
+extension Collection {
/**
Concatenates the rendering of the collection items.
@@ -956,7 +927,7 @@ extension CollectionType {
whatever the type of the collection items.
- returns: A Rendering
*/
- private func renderItems(info: RenderingInfo, @noescape box: (Generator.Element) -> MustacheBox) throws -> Rendering {
+ fileprivate func renderItems(_ info: RenderingInfo, box: (Iterator.Element) -> MustacheBox) throws -> Rendering {
// Prepare the rendering. We don't known the contentType yet: it depends on items
var buffer = ""
var contentType: ContentType? = nil
@@ -978,7 +949,7 @@ extension CollectionType {
info.enumerationItem = true
for item in self {
- let boxRendering = try box(item).render(info: info)
+ let boxRendering = try box(item).render(info)
if contentType == nil
{
// First item: now we know our contentType
@@ -994,7 +965,7 @@ extension CollectionType {
{
// Inconsistent content type: this is an error. How are we
// supposed to mix Text and HTML?
- throw MustacheError(kind: .RenderError, message: "Content type mismatch")
+ throw MustacheError(kind: .renderError, message: "Content type mismatch")
}
}
@@ -1032,7 +1003,7 @@ extension CollectionType {
}
// Support for Set
-extension CollectionType where Index.Distance == Int {
+extension Collection where IndexDistance == Int {
/**
This function returns a MustacheBox that wraps a set-like collection.
@@ -1047,7 +1018,7 @@ extension CollectionType where Index.Distance == Int {
whatever the type of the collection items.
- returns: A MustacheBox that wraps the collection.
*/
- private func mustacheBoxWithSetValue(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox {
+ fileprivate func mustacheBoxWithSetValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
return MustacheBox(
converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),
value: value,
@@ -1060,7 +1031,7 @@ extension CollectionType where Index.Distance == Int {
} else {
return Box()
}
- case "count": // C.Index.Distance == Int
+ case "count": // C.IndexDistance == Int
return Box(self.count)
default:
return Box()
@@ -1081,7 +1052,7 @@ extension CollectionType where Index.Distance == Int {
}
// Support for Array
-extension CollectionType where Index.Distance == Int, Index: BidirectionalIndexType {
+extension BidirectionalCollection where IndexDistance == Int {
/**
This function returns a MustacheBox that wraps an array-like collection.
@@ -1097,7 +1068,7 @@ extension CollectionType where Index.Distance == Int, Index: BidirectionalIndexT
whatever the type of the collection items.
- returns: A MustacheBox that wraps the collection.
*/
- private func mustacheBoxWithArrayValue(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox {
+ fileprivate func mustacheBoxWithArrayValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
return MustacheBox(
converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),
value: value,
@@ -1116,7 +1087,7 @@ extension CollectionType where Index.Distance == Int, Index: BidirectionalIndexT
} else {
return Box()
}
- case "count": // C.Index.Distance == Int
+ case "count": // C.IndexDistance == Int
return Box(self.count)
default:
return Box()
@@ -1199,7 +1170,7 @@ extension NSSet {
//
// So turn NSSet into a Swift Array of MustacheBoxes, and ask the array
// to return a set-like box:
- let array = GeneratorSequence(NSFastGenerator(self)).map(BoxAnyObject)
+ let array = IteratorSequence(NSFastEnumerationIterator(self)).map(BoxAnyObject)
return array.mustacheBoxWithSetValue(self, box: { $0 })
}
}
@@ -1247,7 +1218,7 @@ type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
- returns: A MustacheBox that wraps *array*.
*/
-public func Box(set: C?) -> MustacheBox {
+public func Box(_ set: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
if let set = set {
return set.mustacheBoxWithSetValue(set, box: { Box($0) })
} else {
@@ -1298,7 +1269,7 @@ type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
- returns: A MustacheBox that wraps *array*.
*/
-public func Box(array: C?) -> MustacheBox {
+public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
if let array = array {
return array.mustacheBoxWithArrayValue(array, box: { Box($0) })
} else {
@@ -1349,7 +1320,7 @@ type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
- returns: A MustacheBox that wraps *array*.
*/
-public func Box, T: MustacheBoxable, C.Index: BidirectionalIndexType, C.Index.Distance == Int>(array: C?) -> MustacheBox {
+public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element == Optional, T: MustacheBoxable, C.IndexDistance == Int {
if let array = array {
return array.mustacheBoxWithArrayValue(array, box: { Box($0) })
} else {
@@ -1410,11 +1381,11 @@ dictionary, whatever the actual type of the raw boxed value.
- returns: A MustacheBox that wraps *dictionary*.
*/
-public func Box(dictionary: [String: T]?) -> MustacheBox {
+public func Box(_ dictionary: [String: T]?) -> MustacheBox {
if let dictionary = dictionary {
return MustacheBox(
converter: MustacheBox.Converter(
- dictionaryValue: dictionary.reduce([String: MustacheBox](), combine: { (boxDictionary, item: (key: String, value: T)) in
+ dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: T)) in
var boxDictionary = boxDictionary
boxDictionary[item.key] = Box(item.value)
return boxDictionary
@@ -1476,11 +1447,11 @@ dictionary, whatever the actual type of the raw boxed value.
- returns: A MustacheBox that wraps *dictionary*.
*/
-public func Box(dictionary: [String: T?]?) -> MustacheBox {
+public func Box(_ dictionary: [String: T?]?) -> MustacheBox {
if let dictionary = dictionary {
return MustacheBox(
converter: MustacheBox.Converter(
- dictionaryValue: dictionary.reduce([String: MustacheBox](), combine: { (boxDictionary, item: (key: String, value: T?)) in
+ dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: T?)) in
var boxDictionary = boxDictionary
boxDictionary[item.key] = Box(item.value)
return boxDictionary
@@ -1557,7 +1528,7 @@ extension NSDictionary {
public override var mustacheBox: MustacheBox {
return MustacheBox(
converter: MustacheBox.Converter(
- dictionaryValue: GeneratorSequence(NSFastGenerator(self)).reduce([String: MustacheBox](), combine: { (boxDictionary, key) in
+ dictionaryValue: IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
var boxDictionary = boxDictionary
if let key = key as? String {
boxDictionary[key] = BoxAnyObject(self[key])
@@ -1597,7 +1568,7 @@ See also:
- FilterFunction
*/
-public func Box(filter: FilterFunction) -> MustacheBox {
+public func Box(_ filter: @escaping FilterFunction) -> MustacheBox {
return MustacheBox(filter: filter)
}
@@ -1618,7 +1589,7 @@ See also:
- RenderFunction
*/
-public func Box(render: RenderFunction) -> MustacheBox {
+public func Box(_ render: @escaping RenderFunction) -> MustacheBox {
return MustacheBox(render: render)
}
@@ -1650,7 +1621,7 @@ See also:
- WillRenderFunction
*/
-public func Box(willRender: WillRenderFunction) -> MustacheBox {
+public func Box(_ willRender: @escaping WillRenderFunction) -> MustacheBox {
return MustacheBox(willRender: willRender)
}
@@ -1683,7 +1654,7 @@ See also:
- DidRenderFunction
*/
-public func Box(didRender: DidRenderFunction) -> MustacheBox {
+public func Box(_ didRender: @escaping DidRenderFunction) -> MustacheBox {
return MustacheBox(didRender: didRender)
}
diff --git a/Sources/Common.swift b/Sources/Common.swift
index 66e8ae30..082509b2 100644
--- a/Sources/Common.swift
+++ b/Sources/Common.swift
@@ -46,8 +46,8 @@ See also:
- Rendering
*/
public enum ContentType {
- case Text
- case HTML
+ case text
+ case html
}
@@ -55,13 +55,13 @@ public enum ContentType {
// MARK: - Errors
/// The errors thrown by Mustache.swift
-public struct MustacheError: ErrorType {
+public struct MustacheError: Error {
/// MustacheError types
public enum Kind : Int {
- case TemplateNotFound
- case ParseError
- case RenderError
+ case templateNotFound
+ case parseError
+ case renderError
}
/// The error type
@@ -77,12 +77,12 @@ public struct MustacheError: ErrorType {
public let lineNumber: Int?
/// Eventual underlying error
- public let underlyingError: ErrorType?
+ public let underlyingError: Error?
// Not public
- public init(kind: Kind, message: String? = nil, templateID: TemplateID? = nil, lineNumber: Int? = nil, underlyingError: ErrorType? = nil) {
+ public init(kind: Kind, message: String? = nil, templateID: TemplateID? = nil, lineNumber: Int? = nil, underlyingError: Error? = nil) {
self.kind = kind
self.message = message
self.templateID = templateID
@@ -90,7 +90,7 @@ public struct MustacheError: ErrorType {
self.underlyingError = underlyingError
}
- func errorWith(message message: String? = nil, templateID: TemplateID? = nil, lineNumber: Int? = nil, underlyingError: ErrorType? = nil) -> MustacheError {
+ func errorWith(message: String? = nil, templateID: TemplateID? = nil, lineNumber: Int? = nil, underlyingError: Error? = nil) -> MustacheError {
return MustacheError(
kind: self.kind,
message: message ?? self.message,
@@ -122,15 +122,15 @@ extension MustacheError : CustomStringConvertible {
public var description: String {
var description: String
switch kind {
- case .TemplateNotFound:
+ case .templateNotFound:
description = ""
- case .ParseError:
+ case .parseError:
if let locationDescription = locationDescription {
description = "Parse error at \(locationDescription)"
} else {
description = "Parse error"
}
- case .RenderError:
+ case .renderError:
if let locationDescription = locationDescription {
description = "Rendering error at \(locationDescription)"
} else {
@@ -176,7 +176,7 @@ HTML-escapes a string by replacing `<`, `> `, `&`, `'` and `"` with HTML entitie
- parameter string: A string.
- returns: The HTML-escaped string.
*/
-public func escapeHTML(string: String) -> String {
+public func escapeHTML(_ string: String) -> String {
let escapeTable: [Character: String] = [
"<": "<",
">": ">",
diff --git a/Sources/Configuration.swift b/Sources/Configuration.swift
index fde4207f..51d42626 100644
--- a/Sources/Configuration.swift
+++ b/Sources/Configuration.swift
@@ -96,7 +96,7 @@ public struct Configuration {
*/
public init() {
- contentType = .HTML
+ contentType = .html
baseContext = Context()
tagDelimiterPair = ("{{", "}}")
}
@@ -237,7 +237,7 @@ public struct Configuration {
- baseContext
- registerInBaseContext
*/
- public mutating func extendBaseContext(box: MustacheBox) {
+ public mutating func extendBaseContext(_ box: MustacheBox) {
baseContext = baseContext.extendedContext(box)
}
@@ -284,7 +284,7 @@ public struct Configuration {
- baseContext
- extendBaseContext
*/
- public mutating func registerInBaseContext(key: String, _ box: MustacheBox) {
+ public mutating func registerInBaseContext(_ key: String, _ box: MustacheBox) {
baseContext = baseContext.contextWithRegisteredKey(key, box: box)
}
diff --git a/Sources/Context.swift b/Sources/Context.swift
index 1c5f7f0a..2ae68a49 100644
--- a/Sources/Context.swift
+++ b/Sources/Context.swift
@@ -65,7 +65,7 @@ final public class Context {
Builds an empty Context.
*/
public convenience init() {
- self.init(type: .Root)
+ self.init(type: .root)
}
/**
@@ -75,7 +75,7 @@ final public class Context {
- returns: A new context that contains *box*.
*/
public convenience init(_ box: MustacheBox) {
- self.init(type: .Box(box: box, parent: Context()))
+ self.init(type: .box(box: box, parent: Context()))
}
/**
@@ -88,7 +88,7 @@ final public class Context {
*/
public convenience init(registeredKey key: String, box: MustacheBox) {
let d = [key: box]
- self.init(type: .Root, registeredKeysContext: Context(Box(d)))
+ self.init(type: .root, registeredKeysContext: Context(Box(d)))
}
@@ -102,9 +102,9 @@ final public class Context {
- parameter box: A box.
- returns: A new context with *box* pushed at the top of the stack.
*/
- @warn_unused_result(message="Context.extendedContext returns a new Context.")
- public func extendedContext(box: MustacheBox) -> Context {
- return Context(type: .Box(box: box, parent: self), registeredKeysContext: registeredKeysContext)
+
+ public func extendedContext(_ box: MustacheBox) -> Context {
+ return Context(type: .box(box: box, parent: self), registeredKeysContext: registeredKeysContext)
}
/**
@@ -115,8 +115,8 @@ final public class Context {
- parameter box: A box.
- returns: A new context with *box* registered for *key*.
*/
- @warn_unused_result(message="Context.contextWithRegisteredKey returns a new Context.")
- public func contextWithRegisteredKey(key: String, box: MustacheBox) -> Context {
+
+ public func contextWithRegisteredKey(_ key: String, box: MustacheBox) -> Context {
let d = [key: box]
let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(d))
return Context(type: self.type, registeredKeysContext: registeredKeysContext)
@@ -132,11 +132,11 @@ final public class Context {
*/
public var topBox: MustacheBox {
switch type {
- case .Root:
+ case .root:
return Box()
- case .Box(box: let box, parent: _):
+ case .box(box: let box, parent: _):
return box
- case .PartialOverride(partialOverride: _, parent: let parent):
+ case .partialOverride(partialOverride: _, parent: let parent):
return parent.topBox
}
}
@@ -165,7 +165,7 @@ final public class Context {
- parameter key: A key.
- returns: The MustacheBox for *key*.
*/
- public func mustacheBoxForKey(key: String) -> MustacheBox {
+ public func mustacheBoxForKey(_ key: String) -> MustacheBox {
if let registeredKeysContext = registeredKeysContext {
let box = registeredKeysContext.mustacheBoxForKey(key)
if !box.isEmpty {
@@ -174,16 +174,16 @@ final public class Context {
}
switch type {
- case .Root:
+ case .root:
return Box()
- case .Box(box: let box, parent: let parent):
+ case .box(box: let box, parent: let parent):
let innerBox = box.mustacheBoxForKey(key)
if innerBox.isEmpty {
return parent.mustacheBoxForKey(key)
} else {
return innerBox
}
- case .PartialOverride(partialOverride: _, parent: let parent):
+ case .partialOverride(partialOverride: _, parent: let parent):
return parent.mustacheBoxForKey(key)
}
}
@@ -203,7 +203,7 @@ final public class Context {
- returns: The value of the expression.
*/
- public func mustacheBoxForExpression(string: String) throws -> MustacheBox {
+ public func mustacheBoxForExpression(_ string: String) throws -> MustacheBox {
let parser = ExpressionParser()
var empty = false
let expression = try parser.parse(string, empty: &empty)
@@ -215,63 +215,63 @@ final public class Context {
// =========================================================================
// MARK: - Not public
- private enum `Type` {
- case Root
- case Box(box: MustacheBox, parent: Context)
- case PartialOverride(partialOverride: TemplateASTNode.PartialOverride, parent: Context)
+ fileprivate enum `Type` {
+ case root
+ case box(box: MustacheBox, parent: Context)
+ case partialOverride(partialOverride: TemplateASTNode.PartialOverride, parent: Context)
}
- private var registeredKeysContext: Context?
- private let type: Type
+ fileprivate var registeredKeysContext: Context?
+ fileprivate let type: Type
var willRenderStack: [WillRenderFunction] {
switch type {
- case .Root:
+ case .root:
return []
- case .Box(box: let box, parent: let parent):
+ case .box(box: let box, parent: let parent):
if let willRender = box.willRender {
return [willRender] + parent.willRenderStack
} else {
return parent.willRenderStack
}
- case .PartialOverride(partialOverride: _, parent: let parent):
+ case .partialOverride(partialOverride: _, parent: let parent):
return parent.willRenderStack
}
}
var didRenderStack: [DidRenderFunction] {
switch type {
- case .Root:
+ case .root:
return []
- case .Box(box: let box, parent: let parent):
+ case .box(box: let box, parent: let parent):
if let didRender = box.didRender {
return parent.didRenderStack + [didRender]
} else {
return parent.didRenderStack
}
- case .PartialOverride(partialOverride: _, parent: let parent):
+ case .partialOverride(partialOverride: _, parent: let parent):
return parent.didRenderStack
}
}
var partialOverrideStack: [TemplateASTNode.PartialOverride] {
switch type {
- case .Root:
+ case .root:
return []
- case .Box(box: _, parent: let parent):
+ case .box(box: _, parent: let parent):
return parent.partialOverrideStack
- case .PartialOverride(partialOverride: let partialOverride, parent: let parent):
+ case .partialOverride(partialOverride: let partialOverride, parent: let parent):
return [partialOverride] + parent.partialOverrideStack
}
}
- private init(type: Type, registeredKeysContext: Context? = nil) {
+ fileprivate init(type: Type, registeredKeysContext: Context? = nil) {
self.type = type
self.registeredKeysContext = registeredKeysContext
}
- func extendedContext(partialOverride partialOverride: TemplateASTNode.PartialOverride) -> Context {
- return Context(type: .PartialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext)
+ func extendedContext(partialOverride: TemplateASTNode.PartialOverride) -> Context {
+ return Context(type: .partialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext)
}
}
@@ -279,12 +279,12 @@ extension Context: CustomDebugStringConvertible {
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
switch type {
- case .Root:
+ case .root:
return "Context.Root"
- case .Box(box: let box, parent: let parent):
+ case .box(box: let box, parent: let parent):
return "Context.Box(\(box)):\(parent.debugDescription)"
- case .PartialOverride(partialOverride: _, parent: let parent):
+ case .partialOverride(partialOverride: _, parent: let parent):
return "Context.PartialOverride:\(parent.debugDescription)"
}
}
-}
\ No newline at end of file
+}
diff --git a/Sources/CoreFunctions.swift b/Sources/CoreFunctions.swift
index fa324fe4..df819a4b 100644
--- a/Sources/CoreFunctions.swift
+++ b/Sources/CoreFunctions.swift
@@ -72,7 +72,7 @@ in the context stack in order to resolve a key, return the empty box `Box()`.
In order to express "missing value", and prevent the rendering engine from
digging deeper, return `Box(NSNull())`.
*/
-public typealias KeyedSubscriptFunction = (key: String) -> MustacheBox
+public typealias KeyedSubscriptFunction = (_ key: String) -> MustacheBox
// =============================================================================
@@ -118,7 +118,7 @@ types of filters:
See the documentation of the `Filter()` functions.
*/
-public typealias FilterFunction = (box: MustacheBox, partialApplication: Bool) throws -> MustacheBox
+public typealias FilterFunction = (_ box: MustacheBox, _ partialApplication: Bool) throws -> MustacheBox
// -----------------------------------------------------------------------------
@@ -145,11 +145,11 @@ MustacheError of type RenderError.
- parameter filter: A function `(MustacheBox) throws -> MustacheBox`.
- returns: A FilterFunction.
*/
-public func Filter(filter: (MustacheBox) throws -> MustacheBox) -> FilterFunction {
+public func Filter(_ filter: @escaping (MustacheBox) throws -> MustacheBox) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
// This is a single-argument filter: we do not wait for another one.
- throw MustacheError(kind: .RenderError, message: "Too many arguments")
+ throw MustacheError(kind: .renderError, message: "Too many arguments")
}
return try filter(box)
}
@@ -179,11 +179,11 @@ MustacheError of type RenderError.
- parameter filter: A function `(T?) throws -> MustacheBox`.
- returns: A FilterFunction.
*/
-public func Filter(filter: (T?) throws -> MustacheBox) -> FilterFunction {
+public func Filter(_ filter: @escaping (T?) throws -> MustacheBox) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
// This is a single-argument filter: we do not wait for another one.
- throw MustacheError(kind: .RenderError, message: "Too many arguments")
+ throw MustacheError(kind: .renderError, message: "Too many arguments")
}
return try filter(box.value as? T)
}
@@ -211,7 +211,7 @@ For example:
- parameter filter: A function `([MustacheBox]) throws -> MustacheBox`.
- returns: A FilterFunction.
*/
-public func VariadicFilter(filter: ([MustacheBox]) throws -> MustacheBox) -> FilterFunction {
+public func VariadicFilter(_ filter: @escaping ([MustacheBox]) throws -> MustacheBox) -> FilterFunction {
// f(a,b,c) is implemented as f(a)(b)(c).
//
@@ -220,7 +220,7 @@ public func VariadicFilter(filter: ([MustacheBox]) throws -> MustacheBox) -> Fil
//
// It is the partialApplication flag the tells when it's time to return the
// final result.
- func partialFilter(filter: ([MustacheBox]) throws -> MustacheBox, arguments: [MustacheBox]) -> FilterFunction {
+ func partialFilter(_ filter: @escaping ([MustacheBox]) throws -> MustacheBox, arguments: [MustacheBox]) -> FilterFunction {
return { (nextArgument: MustacheBox, partialApplication: Bool) in
let arguments = arguments + [nextArgument]
if partialApplication {
@@ -265,15 +265,15 @@ allows you to chain pre-rendering filters without mangling HTML entities.
- parameter filter: A function `(Rendering) throws -> Rendering`.
- returns: A FilterFunction.
*/
-public func Filter(filter: (Rendering) throws -> Rendering) -> FilterFunction {
+public func Filter(_ filter: @escaping (Rendering) throws -> Rendering) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
// This is a single-argument filter: we do not wait for another one.
- throw MustacheError(kind: .RenderError, message: "Too many arguments")
+ throw MustacheError(kind: .renderError, message: "Too many arguments")
}
// Box a RenderFunction
return Box { (info: RenderingInfo) in
- let rendering = try box.render(info: info)
+ let rendering = try box.render(info)
return try filter(rendering)
}
}
@@ -296,7 +296,7 @@ This example processes `T?` instead of `MustacheBox`, but the idea is the same.
- parameter filter: A function `(MustacheBox, RenderingInfo) throws -> Rendering`.
- returns: A FilterFunction.
*/
-public func Filter(filter: (MustacheBox, RenderingInfo) throws -> Rendering) -> FilterFunction {
+public func Filter(_ filter: @escaping (MustacheBox, RenderingInfo) throws -> Rendering) -> FilterFunction {
return Filter { (box: MustacheBox) in
// Box a RenderFunction
return Box { (info: RenderingInfo) in
@@ -343,7 +343,7 @@ the `RenderingInfo` and `Rendering` types.
- parameter filter: A function `(T?, RenderingInfo) throws -> Rendering`.
- returns: A FilterFunction.
*/
-public func Filter(filter: (T?, RenderingInfo) throws -> Rendering) -> FilterFunction {
+public func Filter(_ filter: @escaping (T?, RenderingInfo) throws -> Rendering) -> FilterFunction {
return Filter { (t: T?) in
// Box a RenderFunction
return Box { (info: RenderingInfo) in
@@ -455,7 +455,7 @@ The default rendering thus reads:
that describes the problem.
- returns: A Rendering.
*/
-public typealias RenderFunction = (info: RenderingInfo) throws -> Rendering
+public typealias RenderFunction = (_ info: RenderingInfo) throws -> Rendering
// -----------------------------------------------------------------------------
@@ -500,13 +500,13 @@ bolded section has already been parsed with its template. You may prefer the raw
- parameter lambda: A `String -> String` function.
- returns: A RenderFunction.
*/
-public func Lambda(lambda: String -> String) -> RenderFunction {
+public func Lambda(_ lambda: @escaping (String) -> String) -> RenderFunction {
return { (info: RenderingInfo) in
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ lambda }}
return Rendering("(Lambda)")
- case .Section:
+ case .section:
// {{# lambda }}...{{/ lambda }}
//
// https://github.com/mustache/spec/blob/83b0721610a4e11832e83df19c73ace3289972b9/specs/%7Elambdas.yml#L117
@@ -617,10 +617,10 @@ using a Template instead of a lambda (see the documentation of
- parameter lambda: A `() -> String` function.
- returns: A RenderFunction.
*/
-public func Lambda(lambda: () -> String) -> RenderFunction {
+public func Lambda(_ lambda: @escaping () -> String) -> RenderFunction {
return { (info: RenderingInfo) in
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ lambda }}
//
// https://github.com/mustache/spec/blob/83b0721610a4e11832e83df19c73ace3289972b9/specs/%7Elambdas.yml#L73
@@ -629,7 +629,7 @@ public func Lambda(lambda: () -> String) -> RenderFunction {
// Let's render a text template:
let templateRepository = TemplateRepository()
- templateRepository.configuration.contentType = .Text
+ templateRepository.configuration.contentType = .text
let templateString = lambda()
let template = try templateRepository.template(string: templateString)
@@ -685,7 +685,7 @@ public func Lambda(lambda: () -> String) -> RenderFunction {
//
// What should Lambda { "<{{>partial}}>" } render when partial
// contains "<>"? "<<>>" ????
- case .Section:
+ case .section:
// {{# lambda }}...{{/ lambda }}
//
// Behave as a true object, and render the section.
@@ -721,7 +721,7 @@ public struct Rendering {
- parameter contentType: A content type.
- returns: A Rendering.
*/
- public init(_ string: String, _ contentType: ContentType = .Text) {
+ public init(_ string: String, _ contentType: ContentType = .text) {
self.string = string
self.contentType = contentType
}
@@ -732,9 +732,9 @@ extension Rendering : CustomDebugStringConvertible {
public var debugDescription: String {
var contentTypeString: String
switch contentType {
- case .HTML:
+ case .html:
contentTypeString = "HTML"
- case .Text:
+ case .text:
contentTypeString = "Text"
}
@@ -811,7 +811,7 @@ section.
]
try! template.render(Box(data))
*/
-public typealias WillRenderFunction = (tag: Tag, box: MustacheBox) -> MustacheBox
+public typealias WillRenderFunction = (_ tag: Tag, _ box: MustacheBox) -> MustacheBox
// =============================================================================
@@ -863,6 +863,6 @@ See also:
- WillRenderFunction
*/
-public typealias DidRenderFunction = (tag: Tag, box: MustacheBox, string: String?) -> Void
+public typealias DidRenderFunction = (_ tag: Tag, _ box: MustacheBox, _ string: String?) -> Void
diff --git a/Sources/EachFilter.swift b/Sources/EachFilter.swift
index 13df8fe1..369ba1dd 100644
--- a/Sources/EachFilter.swift
+++ b/Sources/EachFilter.swift
@@ -43,7 +43,7 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
// (NSDictionary, [String: Int], [String: CustomObject], etc.
if let dictionary = box.dictionaryValue {
let count = dictionary.count
- let transformedBoxes = dictionary.enumerate().map { (index: Int, element: (key: String, box: MustacheBox)) -> MustacheBox in
+ let transformedBoxes = dictionary.enumerated().map { (index: Int, element: (key: String, box: MustacheBox)) -> MustacheBox in
let customRenderFunction: RenderFunction = { info in
// Push positional keys in the context stack and then perform
// a regular rendering.
@@ -57,7 +57,7 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
var info = info
info.context = info.context.extendedContext(Box(position))
- return try element.box.render(info: info)
+ return try element.box.render(info)
}
return Box(customRenderFunction)
}
@@ -77,7 +77,7 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
// the boxed collection: NSArray, NSSet, [String], [CustomObject], etc.
if let boxes = box.arrayValue {
let count = boxes.count
- let transformedBoxes = boxes.enumerate().map { (index: Int, box: MustacheBox) -> MustacheBox in
+ let transformedBoxes = boxes.enumerated().map { (index: Int, box: MustacheBox) -> MustacheBox in
let customRenderFunction: RenderFunction = { info in
// Push positional keys in the context stack and then perform
// a regular rendering.
@@ -90,7 +90,7 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
var info = info
info.context = info.context.extendedContext(Box(position))
- return try box.render(info: info)
+ return try box.render(info)
}
return Box(customRenderFunction)
}
@@ -98,5 +98,5 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
}
// Non-iterable value
- throw MustacheError(kind: .RenderError, message: "Non-enumerable argument in each filter: \(box.value)")
+ throw MustacheError(kind: .renderError, message: "Non-enumerable argument in each filter: \(box.value)")
}
diff --git a/Sources/Expression.swift b/Sources/Expression.swift
index 08262da7..da35658f 100644
--- a/Sources/Expression.swift
+++ b/Sources/Expression.swift
@@ -30,16 +30,16 @@ The type for expressions that appear in tags: `name`, `user.name`,
enum Expression {
// {{ . }}
- case ImplicitIterator
+ case implicitIterator
// {{ identifier }}
- case Identifier(identifier: String)
+ case identifier(identifier: String)
// {{ .identifier }}
- indirect case Scoped(baseExpression: Expression, identifier: String)
+ indirect case scoped(baseExpression: Expression, identifier: String)
// {{ () }}
- indirect case Filter(filterExpression: Expression, argumentExpression: Expression, partialApplication: Bool)
+ indirect case filter(filterExpression: Expression, argumentExpression: Expression, partialApplication: Bool)
}
/**
@@ -52,16 +52,16 @@ extension Expression: Equatable {
func ==(lhs: Expression, rhs: Expression) -> Bool {
switch (lhs, rhs) {
- case (.ImplicitIterator, .ImplicitIterator):
+ case (.implicitIterator, .implicitIterator):
return true
- case (.Identifier(let lIdentifier), .Identifier(let rIdentifier)):
+ case (.identifier(let lIdentifier), .identifier(let rIdentifier)):
return lIdentifier == rIdentifier
- case (.Scoped(let lBase, let lIdentifier), .Scoped(let rBase, let rIdentifier)):
+ case (.scoped(let lBase, let lIdentifier), .scoped(let rBase, let rIdentifier)):
return lBase == rBase && lIdentifier == rIdentifier
- case (.Filter(let lFilter, let lArgument, let lPartialApplication), .Filter(let rFilter, let rArgument, let rPartialApplication)):
+ case (.filter(let lFilter, let lArgument, let lPartialApplication), .filter(let rFilter, let rArgument, let rPartialApplication)):
return lFilter == rFilter && lArgument == rArgument && lPartialApplication == rPartialApplication
default:
diff --git a/Sources/ExpressionGenerator.swift b/Sources/ExpressionGenerator.swift
index fd1a7e6d..4f64b4b5 100644
--- a/Sources/ExpressionGenerator.swift
+++ b/Sources/ExpressionGenerator.swift
@@ -36,43 +36,43 @@ final class ExpressionGenerator {
self.configuration = configuration ?? DefaultConfiguration
}
- func stringFromExpression(expression: Expression) -> String {
+ func stringFromExpression(_ expression: Expression) -> String {
buffer = ""
renderExpression(expression)
return buffer
}
- func renderExpression(expression: Expression) {
+ func renderExpression(_ expression: Expression) {
switch expression {
- case .ImplicitIterator:
+ case .implicitIterator:
// {{ . }}
- buffer.appendContentsOf(".")
+ buffer.append(".")
- case .Identifier(let identifier):
+ case .identifier(let identifier):
// {{ identifier }}
- buffer.appendContentsOf(identifier)
+ buffer.append(identifier)
- case .Scoped(let baseExpression, let identifier):
+ case .scoped(let baseExpression, let identifier):
// {{ .identifier }}
renderExpression(baseExpression)
- buffer.appendContentsOf(".")
- buffer.appendContentsOf(identifier)
+ buffer.append(".")
+ buffer.append(identifier)
- case .Filter(let filterExpression, let argumentExpression, _):
+ case .filter(let filterExpression, let argumentExpression, _):
// {{ () }}
//
// Support for variadic filters is not implemented:
// `f(a,b)` is rendered `f(a)(b)`.
renderExpression(filterExpression)
- buffer.appendContentsOf("(")
+ buffer.append("(")
renderExpression(argumentExpression)
- buffer.appendContentsOf(")")
+ buffer.append(")")
}
}
- private var buffer: String = ""
+ fileprivate var buffer: String = ""
}
diff --git a/Sources/ExpressionInvocation.swift b/Sources/ExpressionInvocation.swift
index e46a89e3..eab914fb 100644
--- a/Sources/ExpressionInvocation.swift
+++ b/Sources/ExpressionInvocation.swift
@@ -26,42 +26,42 @@ import Foundation
struct ExpressionInvocation {
let expression: Expression
- func invokeWithContext(context: Context) throws -> MustacheBox {
+ func invokeWithContext(_ context: Context) throws -> MustacheBox {
return try evaluate(context: context, expression: expression)
}
- private func evaluate(context context: Context, expression: Expression) throws -> MustacheBox {
+ fileprivate func evaluate(context: Context, expression: Expression) throws -> MustacheBox {
switch expression {
- case .ImplicitIterator:
+ case .implicitIterator:
// {{ . }}
return context.topBox
- case .Identifier(let identifier):
+ case .identifier(let identifier):
// {{ identifier }}
return context.mustacheBoxForKey(identifier)
- case .Scoped(let baseExpression, let identifier):
+ case .scoped(let baseExpression, let identifier):
// {{ .identifier }}
return try evaluate(context: context, expression: baseExpression).mustacheBoxForKey(identifier)
- case .Filter(let filterExpression, let argumentExpression, let partialApplication):
+ case .filter(let filterExpression, let argumentExpression, let partialApplication):
// {{ () }}
let filterBox = try evaluate(context: context, expression: filterExpression)
guard let filter = filterBox.filter else {
if filterBox.isEmpty {
- throw MustacheError(kind: .RenderError, message: "Missing filter")
+ throw MustacheError(kind: .renderError, message: "Missing filter")
} else {
- throw MustacheError(kind: .RenderError, message: "Not a filter")
+ throw MustacheError(kind: .renderError, message: "Not a filter")
}
}
let argumentBox = try evaluate(context: context, expression: argumentExpression)
- return try filter(box: argumentBox, partialApplication: partialApplication)
+ return try filter(argumentBox, partialApplication)
}
}
}
diff --git a/Sources/ExpressionParser.swift b/Sources/ExpressionParser.swift
index 3bf5aac3..191f9c8d 100644
--- a/Sources/ExpressionParser.swift
+++ b/Sources/ExpressionParser.swift
@@ -25,34 +25,34 @@ import Foundation
final class ExpressionParser {
- func parse(string: String, inout empty outEmpty: Bool) throws -> Expression {
+ func parse(_ string: String, empty outEmpty: inout Bool) throws -> Expression {
enum State {
// error
- case Error(String)
+ case error(String)
// Any expression can start
- case WaitingForAnyExpression
+ case waitingForAnyExpression
// Expression has started with a dot
- case LeadingDot
+ case leadingDot
// Expression has started with an identifier
- case Identifier(identifierStart: String.Index)
+ case identifier(identifierStart: String.Index)
// Parsing a scoping identifier
- case ScopingIdentifier(identifierStart: String.Index, baseExpression: Expression)
+ case scopingIdentifier(identifierStart: String.Index, baseExpression: Expression)
// Waiting for a scoping identifier
- case WaitingForScopingIdentifier(baseExpression: Expression)
+ case waitingForScopingIdentifier(baseExpression: Expression)
// Parsed an expression
- case DoneExpression(expression: Expression)
+ case doneExpression(expression: Expression)
// Parsed white space after an expression
- case DoneExpressionPlusWhiteSpace(expression: Expression)
+ case doneExpressionPlusWhiteSpace(expression: Expression)
}
- var state: State = .WaitingForAnyExpression
+ var state: State = .waitingForAnyExpression
var filterExpressionStack: [Expression] = []
var i = string.startIndex
@@ -61,286 +61,286 @@ final class ExpressionParser {
let c = string[i]
switch state {
- case .Error:
+ case .error:
break stringLoop
- case .WaitingForAnyExpression:
+ case .waitingForAnyExpression:
switch c {
case " ", "\r", "\n", "\r\n", "\t":
break
case ".":
- state = .LeadingDot
+ state = .leadingDot
case "(", ")", ",", "{", "}", "&", "$", "#", "^", "/", "<", ">":
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
default:
- state = .Identifier(identifierStart: i)
+ state = .identifier(identifierStart: i)
}
- case .LeadingDot:
+ case .leadingDot:
switch c {
case " ", "\r", "\n", "\r\n", "\t":
- state = .DoneExpressionPlusWhiteSpace(expression: Expression.ImplicitIterator)
+ state = .doneExpressionPlusWhiteSpace(expression: Expression.implicitIterator)
case ".":
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
case "(":
- filterExpressionStack.append(Expression.ImplicitIterator)
- state = .WaitingForAnyExpression
+ filterExpressionStack.append(Expression.implicitIterator)
+ state = .waitingForAnyExpression
case ")":
if let filterExpression = filterExpressionStack.last {
filterExpressionStack.removeLast()
- let expression = Expression.Filter(filterExpression: filterExpression, argumentExpression: Expression.ImplicitIterator, partialApplication: false)
- state = .DoneExpression(expression: expression)
+ let expression = Expression.filter(filterExpression: filterExpression, argumentExpression: Expression.implicitIterator, partialApplication: false)
+ state = .doneExpression(expression: expression)
} else {
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
case ",":
if let filterExpression = filterExpressionStack.last {
filterExpressionStack.removeLast()
- filterExpressionStack.append(Expression.Filter(filterExpression: filterExpression, argumentExpression: Expression.ImplicitIterator, partialApplication: true))
- state = .WaitingForAnyExpression
+ filterExpressionStack.append(Expression.filter(filterExpression: filterExpression, argumentExpression: Expression.implicitIterator, partialApplication: true))
+ state = .waitingForAnyExpression
} else {
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
case "{", "}", "&", "$", "#", "^", "/", "<", ">":
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
default:
- state = .ScopingIdentifier(identifierStart: i, baseExpression: Expression.ImplicitIterator)
+ state = .scopingIdentifier(identifierStart: i, baseExpression: Expression.implicitIterator)
}
- case .Identifier(identifierStart: let identifierStart):
+ case .identifier(identifierStart: let identifierStart):
switch c {
case " ", "\r", "\n", "\r\n", "\t":
- let identifier = string.substringWithRange(identifierStart..":
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
default:
- state = .ScopingIdentifier(identifierStart: i, baseExpression: baseExpression)
+ state = .scopingIdentifier(identifierStart: i, baseExpression: baseExpression)
}
- case .DoneExpression(let doneExpression):
+ case .doneExpression(let doneExpression):
switch c {
case " ", "\r", "\n", "\r\n", "\t":
- state = .DoneExpressionPlusWhiteSpace(expression: doneExpression)
+ state = .doneExpressionPlusWhiteSpace(expression: doneExpression)
case ".":
- state = .WaitingForScopingIdentifier(baseExpression: doneExpression)
+ state = .waitingForScopingIdentifier(baseExpression: doneExpression)
case "(":
filterExpressionStack.append(doneExpression)
- state = .WaitingForAnyExpression
+ state = .waitingForAnyExpression
case ")":
if let filterExpression = filterExpressionStack.last {
filterExpressionStack.removeLast()
- let expression = Expression.Filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: false)
- state = .DoneExpression(expression: expression)
+ let expression = Expression.filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: false)
+ state = .doneExpression(expression: expression)
} else {
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
case ",":
if let filterExpression = filterExpressionStack.last {
filterExpressionStack.removeLast()
- filterExpressionStack.append(Expression.Filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: true))
- state = .WaitingForAnyExpression
+ filterExpressionStack.append(Expression.filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: true))
+ state = .waitingForAnyExpression
} else {
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
default:
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
- case .DoneExpressionPlusWhiteSpace(let doneExpression):
+ case .doneExpressionPlusWhiteSpace(let doneExpression):
switch c {
case " ", "\r", "\n", "\r\n", "\t":
break
case ".":
// Prevent "a .b"
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
case "(":
// Accept "a (b)"
filterExpressionStack.append(doneExpression)
- state = .WaitingForAnyExpression
+ state = .waitingForAnyExpression
case ")":
// Accept "a(b )"
if let filterExpression = filterExpressionStack.last {
filterExpressionStack.removeLast()
- let expression = Expression.Filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: false)
- state = .DoneExpression(expression: expression)
+ let expression = Expression.filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: false)
+ state = .doneExpression(expression: expression)
} else {
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
case ",":
// Accept "a(b ,c)"
if let filterExpression = filterExpressionStack.last {
filterExpressionStack.removeLast()
- filterExpressionStack.append(Expression.Filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: true))
- state = .WaitingForAnyExpression
+ filterExpressionStack.append(Expression.filter(filterExpression: filterExpression, argumentExpression: doneExpression, partialApplication: true))
+ state = .waitingForAnyExpression
} else {
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
default:
- state = .Error("Unexpected character `\(c)` at index \(string.startIndex.distanceTo(i))")
+ state = .error("Unexpected character `\(c)` at index \(string.characters.distance(from: string.startIndex, to: i))")
}
}
- i = i.successor()
+ i = string.index(after: i)
}
// Parsing done
enum FinalState {
- case Error(String)
- case Empty
- case Valid(expression: Expression)
+ case error(String)
+ case empty
+ case valid(expression: Expression)
}
let finalState: FinalState
switch state {
- case .WaitingForAnyExpression:
+ case .waitingForAnyExpression:
if filterExpressionStack.isEmpty {
- finalState = .Empty
+ finalState = .empty
} else {
- finalState = .Error("Missing `)` character at index \(string.startIndex.distanceTo(string.endIndex))")
+ finalState = .error("Missing `)` character at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
}
- case .LeadingDot:
+ case .leadingDot:
if filterExpressionStack.isEmpty {
- finalState = .Valid(expression: Expression.ImplicitIterator)
+ finalState = .valid(expression: Expression.implicitIterator)
} else {
- finalState = .Error("Missing `)` character at index \(string.startIndex.distanceTo(string.endIndex))")
+ finalState = .error("Missing `)` character at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
}
- case .Identifier(identifierStart: let identifierStart):
+ case .identifier(identifierStart: let identifierStart):
if filterExpressionStack.isEmpty {
- let identifier = string.substringFromIndex(identifierStart)
- finalState = .Valid(expression: Expression.Identifier(identifier: identifier))
+ let identifier = string.substring(from: identifierStart)
+ finalState = .valid(expression: Expression.identifier(identifier: identifier))
} else {
- finalState = .Error("Missing `)` character at index \(string.startIndex.distanceTo(string.endIndex))")
+ finalState = .error("Missing `)` character at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
}
- case .ScopingIdentifier(identifierStart: let identifierStart, baseExpression: let baseExpression):
+ case .scopingIdentifier(identifierStart: let identifierStart, baseExpression: let baseExpression):
if filterExpressionStack.isEmpty {
- let identifier = string.substringFromIndex(identifierStart)
- let scopedExpression = Expression.Scoped(baseExpression: baseExpression, identifier: identifier)
- finalState = .Valid(expression: scopedExpression)
+ let identifier = string.substring(from: identifierStart)
+ let scopedExpression = Expression.scoped(baseExpression: baseExpression, identifier: identifier)
+ finalState = .valid(expression: scopedExpression)
} else {
- finalState = .Error("Missing `)` character at index \(string.startIndex.distanceTo(string.endIndex))")
+ finalState = .error("Missing `)` character at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
}
- case .WaitingForScopingIdentifier:
- finalState = .Error("Missing identifier at index \(string.startIndex.distanceTo(string.endIndex))")
+ case .waitingForScopingIdentifier:
+ finalState = .error("Missing identifier at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
- case .DoneExpression(let doneExpression):
+ case .doneExpression(let doneExpression):
if filterExpressionStack.isEmpty {
- finalState = .Valid(expression: doneExpression)
+ finalState = .valid(expression: doneExpression)
} else {
- finalState = .Error("Missing `)` character at index \(string.startIndex.distanceTo(string.endIndex))")
+ finalState = .error("Missing `)` character at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
}
- case .DoneExpressionPlusWhiteSpace(let doneExpression):
+ case .doneExpressionPlusWhiteSpace(let doneExpression):
if filterExpressionStack.isEmpty {
- finalState = .Valid(expression: doneExpression)
+ finalState = .valid(expression: doneExpression)
} else {
- finalState = .Error("Missing `)` character at index \(string.startIndex.distanceTo(string.endIndex))")
+ finalState = .error("Missing `)` character at index \(string.characters.distance(from: string.startIndex, to: string.endIndex))")
}
- case .Error(let message):
- finalState = .Error(message)
+ case .error(let message):
+ finalState = .error(message)
}
// End
switch finalState {
- case .Empty:
+ case .empty:
outEmpty = true
- throw MustacheError(kind: .ParseError, message: "Missing expression")
+ throw MustacheError(kind: .parseError, message: "Missing expression")
- case .Error(let description):
+ case .error(let description):
outEmpty = false
- throw MustacheError(kind: .ParseError, message: "Invalid expression `\(string)`: \(description)")
+ throw MustacheError(kind: .parseError, message: "Invalid expression `\(string)`: \(description)")
- case .Valid(expression: let expression):
+ case .valid(expression: let expression):
return expression
}
}
diff --git a/Sources/HTMLEscapeHelper.swift b/Sources/HTMLEscapeHelper.swift
index 30a9e9e8..29f54605 100644
--- a/Sources/HTMLEscapeHelper.swift
+++ b/Sources/HTMLEscapeHelper.swift
@@ -40,7 +40,7 @@ final class HTMLEscapeHelper : MustacheBoxable {
}
// This function is used for evaluating `HTMLEscape(x)` expressions.
- private func filter(rendering: Rendering) throws -> Rendering {
+ fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(escapeHTML(rendering.string), rendering.contentType)
}
@@ -49,17 +49,17 @@ final class HTMLEscapeHelper : MustacheBoxable {
//
// It is activated as soon as the formatter enters the context stack, when
// used in a section {{# HTMLEscape }}...{{/ HTMLEscape }}.
- private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {
+ fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
switch tag.type {
- case .Variable:
+ case .variable:
// {{ value }}
// We don't know if the box contains a String, so let's escape its
// rendering.
return Box({ (info: RenderingInfo) -> Rendering in
- let rendering = try box.render(info: info)
+ let rendering = try box.render(info)
return try self.filter(rendering)
})
- case .Section:
+ case .section:
// {{# value }}...{{/ value }}
// {{^ value }}...{{/ value }}
// Leave sections untouched, so that loops and conditions are not
diff --git a/Sources/JavascriptEscapeHelper.swift b/Sources/JavascriptEscapeHelper.swift
index 8584fddc..cf854dfc 100644
--- a/Sources/JavascriptEscapeHelper.swift
+++ b/Sources/JavascriptEscapeHelper.swift
@@ -40,7 +40,7 @@ final class JavascriptEscapeHelper : MustacheBoxable {
}
// This function is used for evaluating `javascriptEscape(x)` expressions.
- private func filter(rendering: Rendering) throws -> Rendering {
+ fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(JavascriptEscapeHelper.escapeJavascript(rendering.string), rendering.contentType)
}
@@ -49,21 +49,21 @@ final class JavascriptEscapeHelper : MustacheBoxable {
//
// It is activated as soon as the formatter enters the context stack, when
// used in a section {{# javascriptEscape }}...{{/ javascriptEscape }}.
- private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {
+ fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
switch tag.type {
- case .Variable:
+ case .variable:
// We don't know if the box contains a String, so let's escape its
// rendering.
return Box({ (info: RenderingInfo) -> Rendering in
- let rendering = try box.render(info: info)
+ let rendering = try box.render(info)
return try self.filter(rendering)
})
- case .Section:
+ case .section:
return box
}
}
- private class func escapeJavascript(string: String) -> String {
+ fileprivate class func escapeJavascript(_ string: String) -> String {
// This table comes from https://github.com/django/django/commit/8c4a525871df19163d5bfdf5939eff33b544c2e2#django/template/defaultfilters.py
//
// Quoting Malcolm Tredinnick:
diff --git a/Sources/Localizer.swift b/Sources/Localizer.swift
index 37b26fb6..cfc2787f 100644
--- a/Sources/Localizer.swift
+++ b/Sources/Localizer.swift
@@ -55,7 +55,7 @@ extension StandardLibrary {
*/
public final class Localizer : MustacheBoxable {
/// The bundle
- public let bundle: NSBundle
+ public let bundle: Bundle
/// The table
public let table: String?
@@ -69,8 +69,8 @@ extension StandardLibrary {
nil, the default `Localizable.strings` is used.
- returns: A new Localizer.
*/
- public init(bundle: NSBundle?, table: String?) {
- self.bundle = bundle ?? NSBundle.mainBundle()
+ public init(bundle: Bundle?, table: String?) {
+ self.bundle = bundle ?? Bundle.main
self.table = table
}
@@ -108,15 +108,15 @@ extension StandardLibrary {
// =====================================================================
// MARK: - Not public
- private var formatArguments: [String]?
+ fileprivate var formatArguments: [String]?
// This function is used for evaluating `localize(x)` expressions.
- private func filter(rendering: Rendering) throws -> Rendering {
+ fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(localizedStringForKey(rendering.string), rendering.contentType)
}
// This functionis used to render a {{# localize }}Hello{{/ localize }} section.
- private func render(info: RenderingInfo) throws -> Rendering {
+ fileprivate func render(_ info: RenderingInfo) throws -> Rendering {
// Perform a first rendering of the section tag, that will turn
// variable tags into a custom placeholder.
@@ -157,7 +157,7 @@ extension StandardLibrary {
// Render again
- try! info.tag.render(context)
+ _ = try! info.tag.render(context)
let rendering: Rendering
@@ -186,7 +186,7 @@ extension StandardLibrary {
//
// The format string will then be "%%d %@", as needed.
- let localizableFormat = localizableFormatRendering.string.stringByReplacingOccurrencesOfString("%", withString: "%%").stringByReplacingOccurrencesOfString(Placeholder.string, withString: "%@")
+ let localizableFormat = localizableFormatRendering.string.replacingOccurrences(of: "%", with: "%%").replacingOccurrences(of: Placeholder.string, with: "%@")
// Now localize the format
let localizedFormat = localizedStringForKey(localizableFormat)
@@ -209,9 +209,9 @@ extension StandardLibrary {
return rendering
}
- private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {
+ fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
switch tag.type {
- case .Variable:
+ case .variable:
// {{ value }}
//
// We behave as stated in the documentation of render():
@@ -222,7 +222,7 @@ extension StandardLibrary {
return box
}
- case .Section:
+ case .section:
// {{# value }}
// {{^ value }}
//
@@ -232,9 +232,9 @@ extension StandardLibrary {
}
}
- private func didRender(tag: Tag, box: MustacheBox, string: String?) {
+ fileprivate func didRender(_ tag: Tag, box: MustacheBox, string: String?) {
switch tag.type {
- case .Variable:
+ case .variable:
// {{ value }}
//
// We behave as stated in the documentation of render():
@@ -245,18 +245,18 @@ extension StandardLibrary {
}
}
- case .Section:
+ case .section:
// {{# value }}
// {{^ value }}
break
}
}
- private func localizedStringForKey(key: String) -> String {
- return bundle.localizedStringForKey(key, value:"", table:table)
+ fileprivate func localizedStringForKey(_ key: String) -> String {
+ return bundle.localizedString(forKey: key, value:"", table:table)
}
- private func stringWithFormat(format format: String, argumentsArray args:[String]) -> String {
+ fileprivate func stringWithFormat(format: String, argumentsArray args:[String]) -> String {
switch args.count {
case 0:
return String(format: format)
diff --git a/Sources/Logger.swift b/Sources/Logger.swift
index 5b754330..b557f364 100644
--- a/Sources/Logger.swift
+++ b/Sources/Logger.swift
@@ -75,14 +75,14 @@ extension StandardLibrary {
public var mustacheBox: MustacheBox {
return MustacheBox(
willRender: { (tag, box) in
- if tag.type == .Section {
+ if tag.type == .section {
self.log("\(self.indentationPrefix)\(tag) will render \(box.valueDescription)")
self.indentationLevel += 1
}
return box
},
didRender: { (tag, box, string) in
- if tag.type == .Section {
+ if tag.type == .section {
self.indentationLevel -= 1
}
if let string = string {
@@ -93,10 +93,10 @@ extension StandardLibrary {
}
var indentationPrefix: String {
- return String(count: indentationLevel * 2, repeatedValue: " " as Character)
+ return String(repeating: " ", count: indentationLevel * 2)
}
- private let log: (String) -> Void
- private var indentationLevel: Int = 0
+ fileprivate let log: (String) -> Void
+ fileprivate var indentationLevel: Int = 0
}
}
diff --git a/Sources/MustacheBox.swift b/Sources/MustacheBox.swift
index 993b9572..cd14ece2 100644
--- a/Sources/MustacheBox.swift
+++ b/Sources/MustacheBox.swift
@@ -149,8 +149,8 @@ final public class MustacheBox : NSObject {
- parameter key: A key.
- returns: The MustacheBox for *key*.
*/
- public func mustacheBoxForKey(key: String) -> MustacheBox {
- return keyedSubscript?(key: key) ?? Box()
+ public func mustacheBoxForKey(_ key: String) -> MustacheBox {
+ return keyedSubscript?(key) ?? Box()
}
@@ -158,7 +158,7 @@ final public class MustacheBox : NSObject {
// MARK: - Other facets
/// See the documentation of `RenderFunction`.
- public private(set) var render: RenderFunction
+ public fileprivate(set) var render: RenderFunction
/// See the documentation of `FilterFunction`.
public let filter: FilterFunction?
@@ -481,16 +481,16 @@ final public class MustacheBox : NSObject {
// Default rendering depends on the tag type:
switch info.tag.type {
- case .Variable:
+ case .variable:
// {{ box }} and {{{ box }}}
if let value = self.value {
// Use the built-in Swift String Interpolation:
- return Rendering("\(value)", .Text)
+ return Rendering("\(value)", .text)
} else {
- return Rendering("", .Text)
+ return Rendering("", .text)
}
- case .Section:
+ case .section:
// {{# box }}...{{/ box }}
// Push the value on the top of the context stack:
@@ -503,7 +503,7 @@ final public class MustacheBox : NSObject {
}
}
- private let hasCustomRenderFunction: Bool
+ fileprivate let hasCustomRenderFunction: Bool
// Converter wraps all the conversion closures that help MustacheBox expose
// its raw value (typed Any) as useful types.
@@ -512,8 +512,8 @@ final public class MustacheBox : NSObject {
let dictionaryValue: (() -> [String: MustacheBox]?)
init(
- @autoclosure(escaping) arrayValue: () -> [MustacheBox]? = nil,
- @autoclosure(escaping) dictionaryValue: () -> [String: MustacheBox]? = nil)
+ arrayValue: @autoclosure @escaping () -> [MustacheBox]? = nil,
+ dictionaryValue: @autoclosure @escaping () -> [String: MustacheBox]? = nil)
{
self.arrayValue = arrayValue
self.dictionaryValue = dictionaryValue
@@ -529,7 +529,7 @@ extension MustacheBox {
case 0:
return "MustacheBox(Empty)"
default:
- let content = facets.joinWithSeparator(",")
+ let content = facets.joined(separator: ",")
return "MustacheBox(\(content))"
}
}
@@ -545,14 +545,14 @@ extension MustacheBox {
case 1:
return facets.first!
default:
- return "(" + facets.joinWithSeparator(",") + ")"
+ return "(" + facets.joined(separator: ",") + ")"
}
}
var facetsDescriptions: [String] {
var facets = [String]()
if let array = arrayValue {
- let items = array.map { $0.valueDescription }.joinWithSeparator(",")
+ let items = array.map { $0.valueDescription }.joined(separator: ",")
facets.append("[\(items)]")
} else if let dictionary = dictionaryValue {
if dictionary.isEmpty {
@@ -560,7 +560,7 @@ extension MustacheBox {
} else {
let items = dictionary.map { (key, box) in
return "\(key.debugDescription):\(box.valueDescription)"
- }.joinWithSeparator(",")
+ }.joined(separator: ",")
facets.append("[\(items)]")
}
} else if let value = value {
diff --git a/Sources/NSFormatter.swift b/Sources/NSFormatter.swift
index 298732dc..64eaaf1b 100644
--- a/Sources/NSFormatter.swift
+++ b/Sources/NSFormatter.swift
@@ -28,7 +28,7 @@ import Foundation
GRMustache lets you use `NSFormatter` to format your values.
*/
-extension NSFormatter {
+extension Formatter {
/**
`NSFormatter` adopts the `MustacheBoxable` protocol so that it can feed
@@ -102,7 +102,7 @@ extension NSFormatter {
// > right class, return a properly formatted and, if necessary,
// > localized string.
if let object = box.value as? NSObject {
- return Box(self.stringForObjectValue(object))
+ return Box(self.string(for: object))
} else {
// Not the correct class: return nil, i.e. empty Box.
return Box()
@@ -116,7 +116,7 @@ extension NSFormatter {
// stack, when used in a section `{{# formatter }}...{{/ formatter }}`.
willRender: { (tag: Tag, box: MustacheBox) in
switch tag.type {
- case .Variable:
+ case .variable:
// {{ value }}
// Format the value if we can.
//
@@ -129,13 +129,13 @@ extension NSFormatter {
// So nil result means that object is not of the correct class. Leave
// it untouched.
- if let object = box.value as? NSObject, let formatted = self.stringForObjectValue(object) {
+ if let object = box.value as? NSObject, let formatted = self.string(for: object) {
return Box(formatted)
} else {
return box
}
- case .Section:
+ case .section:
// {{# value }}...{{/ value }}
// {{^ value }}...{{/ value }}
// Leave sections untouched, so that loops and conditions are not
diff --git a/Sources/RenderingEngine.swift b/Sources/RenderingEngine.swift
index c6194064..294f9319 100644
--- a/Sources/RenderingEngine.swift
+++ b/Sources/RenderingEngine.swift
@@ -40,11 +40,11 @@ final class RenderingEngine {
// MARK: - Rendering
- private let templateAST: TemplateAST
- private let baseContext: Context
- private var buffer: String
+ fileprivate let templateAST: TemplateAST
+ fileprivate let baseContext: Context
+ fileprivate var buffer: String
- private func renderTemplateAST(templateAST: TemplateAST, inContext context: Context) throws {
+ fileprivate func renderTemplateAST(_ templateAST: TemplateAST, inContext context: Context) throws {
// We must take care of eventual content-type mismatch between the
// currently rendered AST (defined by init), and the argument.
//
@@ -74,37 +74,37 @@ final class RenderingEngine {
let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
let rendering = try renderingEngine.render()
switch (targetContentType, rendering.contentType) {
- case (.HTML, .Text):
- buffer.appendContentsOf(escapeHTML(rendering.string))
+ case (.html, .text):
+ buffer.append(escapeHTML(rendering.string))
default:
- buffer.appendContentsOf(rendering.string)
+ buffer.append(rendering.string)
}
}
}
- private func renderNode(node: TemplateASTNode, inContext context: Context) throws {
+ fileprivate func renderNode(_ node: TemplateASTNode, inContext context: Context) throws {
switch node {
- case .BlockNode(let block):
+ case .blockNode(let block):
// {{$ name }}...{{/ name }}
//
// Render the inner content of the resolved block.
let resolvedBlock = resolveBlock(block, inContext: context)
return try renderTemplateAST(resolvedBlock.innerTemplateAST, inContext: context)
- case .PartialOverrideNode(let partialOverride):
+ case .partialOverrideNode(let partialOverride):
// {{< name }}...{{/ name }}
//
// Extend the inheritance stack, and render the content of the parent partial
let context = context.extendedContext(partialOverride: partialOverride)
return try renderTemplateAST(partialOverride.parentPartial.templateAST, inContext: context)
- case .PartialNode(let partial):
+ case .partialNode(let partial):
// {{> name }}
//
// Render the content of the partial
return try renderTemplateAST(partial.templateAST, inContext: context)
- case .SectionNode(let section):
+ case .sectionNode(let section):
// {{# name }}...{{/ name }}
// {{^ name }}...{{/ name }}
//
@@ -112,11 +112,11 @@ final class RenderingEngine {
// a few specific flags:
return try renderTag(section.tag, escapesHTML: true, inverted: section.inverted, expression: section.expression, inContext: context)
- case .TextNode(let text):
+ case .textNode(let text):
// text is the trivial case:
- buffer.appendContentsOf(text)
+ buffer.append(text)
- case .VariableNode(let variable):
+ case .variableNode(let variable):
// {{ name }}
// {{{ name }}}
// {{& name }}
@@ -127,7 +127,7 @@ final class RenderingEngine {
}
}
- private func renderTag(tag: LocatedTag, escapesHTML: Bool, inverted: Bool, expression: Expression, inContext context: Context) throws {
+ fileprivate func renderTag(_ tag: LocatedTag, escapesHTML: Bool, inverted: Bool, expression: Expression, inContext context: Context) throws {
// 1. Evaluate expression
@@ -144,14 +144,14 @@ final class RenderingEngine {
}
throw error.errorWith(message: newMessage, templateID: tag.templateID, lineNumber: tag.lineNumber)
} catch {
- throw MustacheError(kind: .RenderError, message: "Could not evaluate \(tag)", templateID: tag.templateID, lineNumber: tag.lineNumber, underlyingError: error)
+ throw MustacheError(kind: .renderError, message: "Could not evaluate \(tag)", templateID: tag.templateID, lineNumber: tag.lineNumber, underlyingError: error)
}
// 2. Let willRender functions alter the box
for willRender in context.willRenderStack {
- box = willRender(tag: tag, box: box)
+ box = willRender(tag, box)
}
@@ -160,16 +160,16 @@ final class RenderingEngine {
let rendering: Rendering
do {
switch tag.type {
- case .Variable:
+ case .variable:
let info = RenderingInfo(tag: tag, context: context, enumerationItem: false)
- rendering = try box.render(info: info)
- case .Section:
+ rendering = try box.render(info)
+ case .section:
switch (inverted, box.boolValue) {
case (false, true):
// {{# true }}...{{/ true }}
// Only case where we trigger the RenderFunction of the Box
let info = RenderingInfo(tag: tag, context: context, enumerationItem: false)
- rendering = try box.render(info: info)
+ rendering = try box.render(info)
case (true, false):
// {{^ false }}...{{/ false }}
rendering = try tag.render(context)
@@ -181,7 +181,7 @@ final class RenderingEngine {
}
} catch {
for didRender in context.didRenderStack {
- didRender(tag: tag, box: box, string: nil)
+ didRender(tag, box, nil)
}
// TODO? Inject location in error
throw error
@@ -191,25 +191,25 @@ final class RenderingEngine {
let string: String
switch (templateAST.contentType!, rendering.contentType, escapesHTML) {
- case (.HTML, .Text, true):
+ case (.html, .text, true):
string = escapeHTML(rendering.string)
default:
string = rendering.string
}
- buffer.appendContentsOf(string)
+ buffer.append(string)
// 5. Let didRender functions do their job
for didRender in context.didRenderStack {
- didRender(tag: tag, box: box, string: string)
+ didRender(tag, box, string)
}
}
// MARK: - Template inheritance
- private func resolveBlock(block: TemplateASTNode.Block, inContext context: Context) -> TemplateASTNode.Block {
+ fileprivate func resolveBlock(_ block: TemplateASTNode.Block, inContext context: Context) -> TemplateASTNode.Block {
// As we iterate partial overrides, block becomes the deepest overriden
// block. context.partialOverrideStack has been built in
// renderNode(node:inContext:).
@@ -248,7 +248,7 @@ final class RenderingEngine {
// Looks for an override for the block argument in a TemplateAST.
// Returns the resolvedBlock, and a boolean that tells whether the block was
// actually overriden.
- private func resolveBlock(block: TemplateASTNode.Block, inChildTemplateAST childTemplateAST: TemplateAST) -> (TemplateASTNode.Block, Bool)
+ fileprivate func resolveBlock(_ block: TemplateASTNode.Block, inChildTemplateAST childTemplateAST: TemplateAST) -> (TemplateASTNode.Block, Bool)
{
// As we iterate template AST nodes, block becomes the last inherited
// block in the template AST.
@@ -257,13 +257,13 @@ final class RenderingEngine {
return childTemplateAST.nodes.reduce((block, false)) { (step, node) in
let (block, modified) = step
switch node {
- case .BlockNode(let resolvedBlock) where resolvedBlock.name == block.name:
+ case .blockNode(let resolvedBlock) where resolvedBlock.name == block.name:
// {{$ name }}...{{/ name }}
//
// A block is overriden by another block with the same name.
return (resolvedBlock, true)
- case .PartialOverrideNode(let partialOverride):
+ case .partialOverrideNode(let partialOverride):
// {{< partial }}...{{/ partial }}
//
// Partial overrides have two opprtunities to override the
@@ -294,7 +294,7 @@ final class RenderingEngine {
let (resolvedBlock2, modified2) = resolveBlock(resolvedBlock1, inChildTemplateAST: partialOverride.childTemplateAST)
return (resolvedBlock2, modified || modified1 || modified2)
- case .PartialNode(let partial):
+ case .partialNode(let partial):
// {{> partial }}
//
// Relevant test:
diff --git a/Sources/SectionTag.swift b/Sources/SectionTag.swift
index 820cc2ee..d3914d40 100644
--- a/Sources/SectionTag.swift
+++ b/Sources/SectionTag.swift
@@ -39,7 +39,7 @@ final class SectionTag: LocatedTag {
// Mark: - Tag protocol
- let type: TagType = .Section
+ let type: TagType = .section
let innerTemplateString: String
var tagDelimiterPair: TagDelimiterPair { return openingToken.tagDelimiterPair! }
@@ -47,7 +47,7 @@ final class SectionTag: LocatedTag {
return "\(openingToken.templateSubstring) at \(openingToken.locationDescription)"
}
- func render(context: Context) throws -> Rendering {
+ func render(_ context: Context) throws -> Rendering {
let renderingEngine = RenderingEngine(templateAST: innerTemplateAST, context: context)
return try renderingEngine.render()
}
diff --git a/Sources/Tag.swift b/Sources/Tag.swift
index a6d3371f..298e7f76 100644
--- a/Sources/Tag.swift
+++ b/Sources/Tag.swift
@@ -33,10 +33,10 @@ information.
public enum TagType {
/// The type of tags such as `{{name}}` and `{{{body}}}`.
- case Variable
+ case variable
/// The type of section tags such as `{{#user}}...{{/user}}`.
- case Section
+ case section
}
@@ -147,5 +147,5 @@ public protocol Tag: class, CustomStringConvertible {
- returns: The rendering of the tag.
*/
- func render(context: Context) throws -> Rendering
+ func render(_ context: Context) throws -> Rendering
}
diff --git a/Sources/Template.swift b/Sources/Template.swift
index 9fb1baf7..2e94412d 100644
--- a/Sources/Template.swift
+++ b/Sources/Template.swift
@@ -60,11 +60,11 @@ final public class Template {
partials, throws an error that describes the problem.
- returns: A new Template.
*/
- public convenience init(path: String, encoding: NSStringEncoding = NSUTF8StringEncoding) throws {
+ public convenience init(path: String, encoding: String.Encoding = String.Encoding.utf8) throws {
let nsPath = path as NSString
- let directoryPath = nsPath.stringByDeletingLastPathComponent
+ let directoryPath = nsPath.deletingLastPathComponent
let templateExtension = nsPath.pathExtension
- let templateName = (nsPath.lastPathComponent as NSString).stringByDeletingPathExtension
+ let templateName = (nsPath.lastPathComponent as NSString).deletingPathExtension
let repository = TemplateRepository(directoryPath: directoryPath, templateExtension: templateExtension, encoding: encoding)
let templateAST = try repository.templateAST(named: templateName)
self.init(repository: repository, templateAST: templateAST, baseContext: repository.configuration.baseContext)
@@ -85,10 +85,10 @@ final public class Template {
partials, throws an error that describes the problem.
- returns: A new Template.
*/
- public convenience init(URL: NSURL, encoding: NSStringEncoding = NSUTF8StringEncoding) throws {
- let baseURL = URL.URLByDeletingLastPathComponent!
+ public convenience init(URL: Foundation.URL, encoding: String.Encoding = String.Encoding.utf8) throws {
+ let baseURL = URL.deletingLastPathComponent()
let templateExtension = URL.pathExtension
- let templateName = (URL.lastPathComponent! as NSString).stringByDeletingPathExtension
+ let templateName = (URL.lastPathComponent as NSString).deletingPathExtension
let repository = TemplateRepository(baseURL: baseURL, templateExtension: templateExtension, encoding: encoding)
let templateAST = try repository.templateAST(named: templateName)
self.init(repository: repository, templateAST: templateAST, baseContext: repository.configuration.baseContext)
@@ -116,7 +116,7 @@ final public class Template {
describes the problem.
- returns: A new Template.
*/
- public convenience init(named name: String, bundle: NSBundle? = nil, templateExtension: String? = "mustache", encoding: NSStringEncoding = NSUTF8StringEncoding) throws {
+ public convenience init(named name: String, bundle: Bundle? = nil, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) throws {
let repository = TemplateRepository(bundle: bundle, templateExtension: templateExtension, encoding: encoding)
let templateAST = try repository.templateAST(named: name)
self.init(repository: repository, templateAST: templateAST, baseContext: repository.configuration.baseContext)
@@ -135,7 +135,7 @@ final public class Template {
that describes the problem.
- returns: The rendered string.
*/
- public func render(box: MustacheBox = Box()) throws -> String {
+ public func render(_ box: MustacheBox = Box()) throws -> String {
let rendering = try render(baseContext.extendedContext(box))
return rendering.string
}
@@ -158,7 +158,7 @@ final public class Template {
- RenderFunction
- Template.contentType
*/
- public func render(context: Context) throws -> Rendering {
+ public func render(_ context: Context) throws -> Rendering {
let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
return try renderingEngine.render()
}
@@ -213,7 +213,7 @@ final public class Template {
- registerInBaseContext
- Context.extendedContext
*/
- public func extendBaseContext(box: MustacheBox) {
+ public func extendBaseContext(_ box: MustacheBox) {
baseContext = baseContext.extendedContext(box)
}
@@ -237,7 +237,7 @@ final public class Template {
- extendBaseContext
- Context.contextWithRegisteredKey
*/
- public func registerInBaseContext(key: String, _ box: MustacheBox) {
+ public func registerInBaseContext(_ key: String, _ box: MustacheBox) {
baseContext = baseContext.contextWithRegisteredKey(key, box: box)
}
diff --git a/Sources/TemplateAST.swift b/Sources/TemplateAST.swift
index 02a206cc..7f6a4e37 100644
--- a/Sources/TemplateAST.swift
+++ b/Sources/TemplateAST.swift
@@ -36,12 +36,12 @@ final class TemplateAST {
//
// See TemplateRepository.templateAST(named:relativeToTemplateID:error:).
enum `Type` {
- case Undefined
- case Defined(nodes: [TemplateASTNode], contentType: ContentType)
+ case undefined
+ case defined(nodes: [TemplateASTNode], contentType: ContentType)
}
var type: Type
- private init(type: Type) {
+ fileprivate init(type: Type) {
self.type = type
}
@@ -50,14 +50,14 @@ final class TemplateAST {
Returns an undefined TemplateAST.
*/
convenience init() {
- self.init(type: Type.Undefined)
+ self.init(type: Type.undefined)
}
/**
Returns a defined TemplateAST.
*/
convenience init(nodes: [TemplateASTNode], contentType: ContentType) {
- self.init(type: Type.Defined(nodes: nodes, contentType: contentType))
+ self.init(type: Type.defined(nodes: nodes, contentType: contentType))
}
/**
@@ -65,9 +65,9 @@ final class TemplateAST {
*/
var nodes: [TemplateASTNode]! {
switch type {
- case .Undefined:
+ case .undefined:
return nil
- case .Defined(let nodes, _):
+ case .defined(let nodes, _):
return nodes
}
}
@@ -77,14 +77,14 @@ final class TemplateAST {
*/
var contentType: ContentType! {
switch type {
- case .Undefined:
+ case .undefined:
return nil
- case .Defined(_, let contentType):
+ case .defined(_, let contentType):
return contentType
}
}
- func updateFromTemplateAST(templateAST: TemplateAST) {
+ func updateFromTemplateAST(_ templateAST: TemplateAST) {
self.type = templateAST.type
}
}
diff --git a/Sources/TemplateASTNode.swift b/Sources/TemplateASTNode.swift
index 75aad218..71385546 100644
--- a/Sources/TemplateASTNode.swift
+++ b/Sources/TemplateASTNode.swift
@@ -24,12 +24,12 @@
import Foundation
enum TemplateASTNode {
- case BlockNode(Block) // {{$ name }}...{{/ name }}
- case PartialOverrideNode(PartialOverride) // {{< name }}...{{/ name }}
- case PartialNode(Partial) // {{> name }}
- case SectionNode(Section) // {{# name }}...{{/ name }}, {{^ name }}...{{/ name }}
- case TextNode(String) // text
- case VariableNode(Variable) // {{ name }}, {{{ name }}}, {{& name }}
+ case blockNode(Block) // {{$ name }}...{{/ name }}
+ case partialOverrideNode(PartialOverride) // {{< name }}...{{/ name }}
+ case partialNode(Partial) // {{> name }}
+ case sectionNode(Section) // {{# name }}...{{/ name }}, {{^ name }}...{{/ name }}
+ case textNode(String) // text
+ case variableNode(Variable) // {{ name }}, {{{ name }}}, {{& name }}
// Define structs instead of long tuples
@@ -66,29 +66,29 @@ enum TemplateASTNode {
// Factory methods
- static func block(innerTemplateAST innerTemplateAST: TemplateAST, name: String) -> TemplateASTNode {
- return .BlockNode(Block(innerTemplateAST: innerTemplateAST, name: name))
+ static func block(innerTemplateAST: TemplateAST, name: String) -> TemplateASTNode {
+ return .blockNode(Block(innerTemplateAST: innerTemplateAST, name: name))
}
- static func partialOverride(childTemplateAST childTemplateAST: TemplateAST, parentTemplateAST: TemplateAST, parentPartialName: String? = nil) -> TemplateASTNode {
- return .PartialOverrideNode(PartialOverride(childTemplateAST: childTemplateAST, parentPartial: Partial(templateAST: parentTemplateAST, name: parentPartialName)))
+ static func partialOverride(childTemplateAST: TemplateAST, parentTemplateAST: TemplateAST, parentPartialName: String? = nil) -> TemplateASTNode {
+ return .partialOverrideNode(PartialOverride(childTemplateAST: childTemplateAST, parentPartial: Partial(templateAST: parentTemplateAST, name: parentPartialName)))
}
- static func partial(templateAST templateAST: TemplateAST, name: String?) -> TemplateASTNode {
- return .PartialNode(Partial(templateAST: templateAST, name: name))
+ static func partial(templateAST: TemplateAST, name: String?) -> TemplateASTNode {
+ return .partialNode(Partial(templateAST: templateAST, name: name))
}
- static func section(templateAST templateAST: TemplateAST, expression: Expression, inverted: Bool, openingToken: TemplateToken, innerTemplateString: String) -> TemplateASTNode {
+ static func section(templateAST: TemplateAST, expression: Expression, inverted: Bool, openingToken: TemplateToken, innerTemplateString: String) -> TemplateASTNode {
let tag = SectionTag(innerTemplateAST: templateAST, openingToken: openingToken, innerTemplateString: innerTemplateString)
- return .SectionNode(Section(tag: tag, expression: expression, inverted: inverted))
+ return .sectionNode(Section(tag: tag, expression: expression, inverted: inverted))
}
- static func text(text text: String) -> TemplateASTNode {
- return .TextNode(text)
+ static func text(text: String) -> TemplateASTNode {
+ return .textNode(text)
}
- static func variable(expression expression: Expression, contentType: ContentType, escapesHTML: Bool, token: TemplateToken) -> TemplateASTNode {
+ static func variable(expression: Expression, contentType: ContentType, escapesHTML: Bool, token: TemplateToken) -> TemplateASTNode {
let tag = VariableTag(contentType: contentType, token: token)
- return .VariableNode(Variable(tag: tag, expression: expression, escapesHTML: escapesHTML))
+ return .variableNode(Variable(tag: tag, expression: expression, escapesHTML: escapesHTML))
}
}
diff --git a/Sources/TemplateCompiler.swift b/Sources/TemplateCompiler.swift
index 20a45c2b..8d4543db 100644
--- a/Sources/TemplateCompiler.swift
+++ b/Sources/TemplateCompiler.swift
@@ -23,32 +23,32 @@
import Foundation
final class TemplateCompiler: TemplateTokenConsumer {
- private var state: CompilerState
- private let repository: TemplateRepository
- private let templateID: TemplateID?
+ fileprivate var state: CompilerState
+ fileprivate let repository: TemplateRepository
+ fileprivate let templateID: TemplateID?
init(contentType: ContentType, repository: TemplateRepository, templateID: TemplateID?) {
- self.state = .Compiling(CompilationState(contentType: contentType))
+ self.state = .compiling(CompilationState(contentType: contentType))
self.repository = repository
self.templateID = templateID
}
func templateAST() throws -> TemplateAST {
switch(state) {
- case .Compiling(let compilationState):
+ case .compiling(let compilationState):
switch compilationState.currentScope.type {
- case .Root:
+ case .root:
return TemplateAST(nodes: compilationState.currentScope.templateASTNodes, contentType: compilationState.contentType)
- case .Section(openingToken: let openingToken, expression: _):
- throw MustacheError(kind: .ParseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
- case .InvertedSection(openingToken: let openingToken, expression: _):
- throw MustacheError(kind: .ParseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
- case .PartialOverride(openingToken: let openingToken, parentPartialName: _):
- throw MustacheError(kind: .ParseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
- case .Block(openingToken: let openingToken, blockName: _):
- throw MustacheError(kind: .ParseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
+ case .section(openingToken: let openingToken, expression: _):
+ throw MustacheError(kind: .parseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
+ case .invertedSection(openingToken: let openingToken, expression: _):
+ throw MustacheError(kind: .parseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
+ case .partialOverride(openingToken: let openingToken, parentPartialName: _):
+ throw MustacheError(kind: .parseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
+ case .block(openingToken: let openingToken, blockName: _):
+ throw MustacheError(kind: .parseError, message: "Unclosed Mustache tag", templateID: openingToken.templateID, lineNumber: openingToken.lineNumber)
}
- case .Error(let compilationError):
+ case .error(let compilationError):
throw compilationError
}
}
@@ -56,47 +56,47 @@ final class TemplateCompiler: TemplateTokenConsumer {
// MARK: - TemplateTokenConsumer
- func parser(parser: TemplateParser, didFailWithError error: ErrorType) {
- state = .Error(error)
+ func parser(_ parser: TemplateParser, didFailWithError error: Error) {
+ state = .error(error)
}
- func parser(parser: TemplateParser, shouldContinueAfterParsingToken token: TemplateToken) -> Bool {
+ func parser(_ parser: TemplateParser, shouldContinueAfterParsingToken token: TemplateToken) -> Bool {
switch(state) {
- case .Error:
+ case .error:
return false
- case .Compiling(let compilationState):
+ case .compiling(let compilationState):
do {
switch(token.type) {
- case .SetDelimiters:
+ case .setDelimiters:
// noop
break
- case .Comment:
+ case .comment:
// noop
break
- case .Pragma(content: let content):
- let pragma = content.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
- if (try! NSRegularExpression(pattern: "^CONTENT_TYPE\\s*:\\s*TEXT$", options: NSRegularExpressionOptions(rawValue: 0))).firstMatchInString(pragma, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, (pragma as NSString).length)) != nil {
+ case .pragma(content: let content):
+ let pragma = content.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
+ if (try! NSRegularExpression(pattern: "^CONTENT_TYPE\\s*:\\s*TEXT$", options: NSRegularExpression.Options(rawValue: 0))).firstMatch(in: pragma, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, (pragma as NSString).length)) != nil {
switch compilationState.compilerContentType {
- case .Unlocked:
- compilationState.compilerContentType = .Unlocked(.Text)
- case .Locked(_):
- throw MustacheError(kind: .ParseError, message:"CONTENT_TYPE:TEXT pragma tag must prepend any Mustache variable, section, or partial tag.", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .unlocked:
+ compilationState.compilerContentType = .unlocked(.text)
+ case .locked(_):
+ throw MustacheError(kind: .parseError, message:"CONTENT_TYPE:TEXT pragma tag must prepend any Mustache variable, section, or partial tag.", templateID: token.templateID, lineNumber: token.lineNumber)
}
- } else if (try! NSRegularExpression(pattern: "^CONTENT_TYPE\\s*:\\s*HTML$", options: NSRegularExpressionOptions(rawValue: 0))).firstMatchInString(pragma, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, (pragma as NSString).length)) != nil {
+ } else if (try! NSRegularExpression(pattern: "^CONTENT_TYPE\\s*:\\s*HTML$", options: NSRegularExpression.Options(rawValue: 0))).firstMatch(in: pragma, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, (pragma as NSString).length)) != nil {
switch compilationState.compilerContentType {
- case .Unlocked:
- compilationState.compilerContentType = .Unlocked(.HTML)
- case .Locked(_):
- throw MustacheError(kind: .ParseError, message:"CONTENT_TYPE:HTML pragma tag must prepend any Mustache variable, section, or partial tag.", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .unlocked:
+ compilationState.compilerContentType = .unlocked(.html)
+ case .locked(_):
+ throw MustacheError(kind: .parseError, message:"CONTENT_TYPE:HTML pragma tag must prepend any Mustache variable, section, or partial tag.", templateID: token.templateID, lineNumber: token.lineNumber)
}
}
- case .Text(text: let text):
+ case .text(text: let text):
switch compilationState.currentScope.type {
- case .PartialOverride:
+ case .partialOverride:
// Text inside a partial override tag is not rendered.
//
// We could throw an error, like we do for illegal tags
@@ -111,92 +111,92 @@ final class TemplateCompiler: TemplateTokenConsumer {
compilationState.currentScope.appendNode(TemplateASTNode.text(text: text))
}
- case .EscapedVariable(content: let content, tagDelimiterPair: _):
+ case .escapedVariable(content: let content, tagDelimiterPair: _):
switch compilationState.currentScope.type {
- case .PartialOverride:
- throw MustacheError(kind: .ParseError, message:"Illegal tag inside a partial override tag.", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .partialOverride:
+ throw MustacheError(kind: .parseError, message:"Illegal tag inside a partial override tag.", templateID: token.templateID, lineNumber: token.lineNumber)
default:
var empty = false
do {
let expression = try ExpressionParser().parse(content, empty: &empty)
compilationState.currentScope.appendNode(TemplateASTNode.variable(expression: expression, contentType: compilationState.contentType, escapesHTML: true, token: token))
- compilationState.compilerContentType = .Locked(compilationState.contentType)
+ compilationState.compilerContentType = .locked(compilationState.contentType)
} catch let error as MustacheError {
throw error.errorWith(templateID: token.templateID, lineNumber: token.lineNumber)
} catch {
- throw MustacheError(kind: .ParseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
+ throw MustacheError(kind: .parseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
}
}
- case .UnescapedVariable(content: let content, tagDelimiterPair: _):
+ case .unescapedVariable(content: let content, tagDelimiterPair: _):
switch compilationState.currentScope.type {
- case .PartialOverride:
- throw MustacheError(kind: .ParseError, message: "Illegal tag inside a partial override tag: \(token.templateSubstring)", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .partialOverride:
+ throw MustacheError(kind: .parseError, message: "Illegal tag inside a partial override tag: \(token.templateSubstring)", templateID: token.templateID, lineNumber: token.lineNumber)
default:
var empty = false
do {
let expression = try ExpressionParser().parse(content, empty: &empty)
compilationState.currentScope.appendNode(TemplateASTNode.variable(expression: expression, contentType: compilationState.contentType, escapesHTML: false, token: token))
- compilationState.compilerContentType = .Locked(compilationState.contentType)
+ compilationState.compilerContentType = .locked(compilationState.contentType)
} catch let error as MustacheError {
throw error.errorWith(templateID: token.templateID, lineNumber: token.lineNumber)
} catch {
- throw MustacheError(kind: .ParseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
+ throw MustacheError(kind: .parseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
}
}
- case .Section(content: let content, tagDelimiterPair: _):
+ case .section(content: let content, tagDelimiterPair: _):
switch compilationState.currentScope.type {
- case .PartialOverride:
- throw MustacheError(kind: .ParseError, message: "Illegal tag inside a partial override tag: \(token.templateSubstring)", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .partialOverride:
+ throw MustacheError(kind: .parseError, message: "Illegal tag inside a partial override tag: \(token.templateSubstring)", templateID: token.templateID, lineNumber: token.lineNumber)
default:
var empty = false
do {
let expression = try ExpressionParser().parse(content, empty: &empty)
- compilationState.pushScope(Scope(type: .Section(openingToken: token, expression: expression)))
- compilationState.compilerContentType = .Locked(compilationState.contentType)
+ compilationState.pushScope(Scope(type: .section(openingToken: token, expression: expression)))
+ compilationState.compilerContentType = .locked(compilationState.contentType)
} catch let error as MustacheError {
throw error.errorWith(templateID: token.templateID, lineNumber: token.lineNumber)
} catch {
- throw MustacheError(kind: .ParseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
+ throw MustacheError(kind: .parseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
}
}
- case .InvertedSection(content: let content, tagDelimiterPair: _):
+ case .invertedSection(content: let content, tagDelimiterPair: _):
switch compilationState.currentScope.type {
- case .PartialOverride:
- throw MustacheError(kind: .ParseError, message: "Illegal tag inside a partial override tag: \(token.templateSubstring)", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .partialOverride:
+ throw MustacheError(kind: .parseError, message: "Illegal tag inside a partial override tag: \(token.templateSubstring)", templateID: token.templateID, lineNumber: token.lineNumber)
default:
var empty = false
do {
let expression = try ExpressionParser().parse(content, empty: &empty)
- compilationState.pushScope(Scope(type: .InvertedSection(openingToken: token, expression: expression)))
- compilationState.compilerContentType = .Locked(compilationState.contentType)
+ compilationState.pushScope(Scope(type: .invertedSection(openingToken: token, expression: expression)))
+ compilationState.compilerContentType = .locked(compilationState.contentType)
} catch let error as MustacheError {
throw error.errorWith(templateID: token.templateID, lineNumber: token.lineNumber)
} catch {
- throw MustacheError(kind: .ParseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
+ throw MustacheError(kind: .parseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
}
}
- case .Block(content: let content):
+ case .block(content: let content):
var empty: Bool = false
let blockName = try blockNameFromString(content, inToken: token, empty: &empty)
- compilationState.pushScope(Scope(type: .Block(openingToken: token, blockName: blockName)))
- compilationState.compilerContentType = .Locked(compilationState.contentType)
+ compilationState.pushScope(Scope(type: .block(openingToken: token, blockName: blockName)))
+ compilationState.compilerContentType = .locked(compilationState.contentType)
- case .PartialOverride(content: let content):
+ case .partialOverride(content: let content):
var empty: Bool = false
let parentPartialName = try partialNameFromString(content, inToken: token, empty: &empty)
- compilationState.pushScope(Scope(type: .PartialOverride(openingToken: token, parentPartialName: parentPartialName)))
- compilationState.compilerContentType = .Locked(compilationState.contentType)
+ compilationState.pushScope(Scope(type: .partialOverride(openingToken: token, parentPartialName: parentPartialName)))
+ compilationState.compilerContentType = .locked(compilationState.contentType)
- case .Close(content: let content):
+ case .close(content: let content):
switch compilationState.currentScope.type {
- case .Root:
- throw MustacheError(kind: .ParseError, message: "Unmatched closing tag", templateID: token.templateID, lineNumber: token.lineNumber)
+ case .root:
+ throw MustacheError(kind: .parseError, message: "Unmatched closing tag", templateID: token.templateID, lineNumber: token.lineNumber)
- case .Section(openingToken: let openingToken, expression: let closedExpression):
+ case .section(openingToken: let openingToken, expression: let closedExpression):
var empty: Bool = false
var expression: Expression?
do {
@@ -206,10 +206,10 @@ final class TemplateCompiler: TemplateTokenConsumer {
throw error.errorWith(templateID: token.templateID, lineNumber: token.lineNumber)
}
} catch {
- throw MustacheError(kind: .ParseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
+ throw MustacheError(kind: .parseError, templateID: token.templateID, lineNumber: token.lineNumber, underlyingError: error)
}
if expression != nil && expression != closedExpression {
- throw MustacheError(kind: .ParseError, message: "Unmatched closing tag", templateID: token.templateID, lineNumber: token.lineNumber)
+ throw MustacheError(kind: .parseError, message: "Unmatched closing tag", templateID: token.templateID, lineNumber: token.lineNumber)
}
let templateASTNodes = compilationState.currentScope.templateASTNodes
@@ -220,13 +220,13 @@ final class TemplateCompiler: TemplateTokenConsumer {
// fatalError("Not implemented")
// }
let templateString = token.templateString
- let innerContentRange = openingToken.range.endIndex.. String {
- let whiteSpace = NSCharacterSet.whitespaceAndNewlineCharacterSet()
- let blockName = string.stringByTrimmingCharactersInSet(whiteSpace)
+ fileprivate func blockNameFromString(_ string: String, inToken token: TemplateToken, empty: inout Bool) throws -> String {
+ let whiteSpace = CharacterSet.whitespacesAndNewlines
+ let blockName = string.trimmingCharacters(in: whiteSpace)
if blockName.characters.count == 0 {
empty = true
- throw MustacheError(kind: .ParseError, message: "Missing block name", templateID: token.templateID, lineNumber: token.lineNumber)
- } else if (blockName.rangeOfCharacterFromSet(whiteSpace) != nil) {
+ throw MustacheError(kind: .parseError, message: "Missing block name", templateID: token.templateID, lineNumber: token.lineNumber)
+ } else if (blockName.rangeOfCharacter(from: whiteSpace) != nil) {
empty = false
- throw MustacheError(kind: .ParseError, message: "Invalid block name", templateID: token.templateID, lineNumber: token.lineNumber)
+ throw MustacheError(kind: .parseError, message: "Invalid block name", templateID: token.templateID, lineNumber: token.lineNumber)
}
return blockName
}
- private func partialNameFromString(string: String, inToken token: TemplateToken, inout empty: Bool) throws -> String {
- let whiteSpace = NSCharacterSet.whitespaceAndNewlineCharacterSet()
- let partialName = string.stringByTrimmingCharactersInSet(whiteSpace)
+ fileprivate func partialNameFromString(_ string: String, inToken token: TemplateToken, empty: inout Bool) throws -> String {
+ let whiteSpace = CharacterSet.whitespacesAndNewlines
+ let partialName = string.trimmingCharacters(in: whiteSpace)
if partialName.characters.count == 0 {
empty = true
- throw MustacheError(kind: .ParseError, message: "Missing template name", templateID: token.templateID, lineNumber: token.lineNumber)
- } else if (partialName.rangeOfCharacterFromSet(whiteSpace) != nil) {
+ throw MustacheError(kind: .parseError, message: "Missing template name", templateID: token.templateID, lineNumber: token.lineNumber)
+ } else if (partialName.rangeOfCharacter(from: whiteSpace) != nil) {
empty = false
- throw MustacheError(kind: .ParseError, message: "Invalid template name", templateID: token.templateID, lineNumber: token.lineNumber)
+ throw MustacheError(kind: .parseError, message: "Invalid template name", templateID: token.templateID, lineNumber: token.lineNumber)
}
return partialName
}
diff --git a/Sources/TemplateGenerator.swift b/Sources/TemplateGenerator.swift
index 9698a528..0f79c417 100644
--- a/Sources/TemplateGenerator.swift
+++ b/Sources/TemplateGenerator.swift
@@ -44,74 +44,74 @@ final class TemplateGenerator {
self.configuration = configuration ?? DefaultConfiguration
}
- func stringFromTemplateAST(templateAST: TemplateAST) -> String {
+ func stringFromTemplateAST(_ templateAST: TemplateAST) -> String {
buffer = ""
renderTemplateAST(templateAST)
return buffer
}
- private func renderTemplateAST(templateAST: TemplateAST) {
+ fileprivate func renderTemplateAST(_ templateAST: TemplateAST) {
for node in templateAST.nodes {
renderTemplateASTNode(node)
}
}
- func renderTemplateASTNode(node: TemplateASTNode) {
+ func renderTemplateASTNode(_ node: TemplateASTNode) {
switch node {
- case .BlockNode(let block):
+ case .blockNode(let block):
let tagStartDelimiter = configuration.tagDelimiterPair.0
let tagEndDelimiter = configuration.tagDelimiterPair.1
let name = block.name
- buffer.appendContentsOf("\(tagStartDelimiter)$\(name)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)$\(name)\(tagEndDelimiter)")
renderTemplateAST(block.innerTemplateAST)
- buffer.appendContentsOf("\(tagStartDelimiter)/\(name)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)/\(name)\(tagEndDelimiter)")
- case .PartialOverrideNode(let partialOverride):
+ case .partialOverrideNode(let partialOverride):
let tagStartDelimiter = configuration.tagDelimiterPair.0
let tagEndDelimiter = configuration.tagDelimiterPair.1
let name = partialOverride.parentPartial.name ?? ""
- buffer.appendContentsOf("\(tagStartDelimiter)<\(name)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)<\(name)\(tagEndDelimiter)")
renderTemplateAST(partialOverride.childTemplateAST)
- buffer.appendContentsOf("\(tagStartDelimiter)/\(name)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)/\(name)\(tagEndDelimiter)")
- case .PartialNode(let partial):
+ case .partialNode(let partial):
let tagStartDelimiter = configuration.tagDelimiterPair.0
let tagEndDelimiter = configuration.tagDelimiterPair.1
let name = partial.name ?? ""
- buffer.appendContentsOf("\(tagStartDelimiter)>\(name)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)>\(name)\(tagEndDelimiter)")
- case .SectionNode(let section):
+ case .sectionNode(let section):
// Change delimiters tags are ignored. Always use configuration tag
// delimiters.
let tagStartDelimiter = configuration.tagDelimiterPair.0
let tagEndDelimiter = configuration.tagDelimiterPair.1
let expression = ExpressionGenerator().stringFromExpression(section.expression)
if section.inverted {
- buffer.appendContentsOf("\(tagStartDelimiter)^\(expression)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)^\(expression)\(tagEndDelimiter)")
} else {
- buffer.appendContentsOf("\(tagStartDelimiter)#\(expression)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)#\(expression)\(tagEndDelimiter)")
}
renderTemplateAST(section.tag.innerTemplateAST)
- buffer.appendContentsOf("\(tagStartDelimiter)/\(expression)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)/\(expression)\(tagEndDelimiter)")
- case .TextNode(let text):
- buffer.appendContentsOf(text)
+ case .textNode(let text):
+ buffer.append(text)
- case .VariableNode(let variable):
+ case .variableNode(let variable):
// Change delimiters tags are ignored. Always use configuration tag
// delimiters.
let tagStartDelimiter = configuration.tagDelimiterPair.0
let tagEndDelimiter = configuration.tagDelimiterPair.1
let expression = ExpressionGenerator().stringFromExpression(variable.expression)
if variable.escapesHTML {
- buffer.appendContentsOf("\(tagStartDelimiter)\(expression)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)\(expression)\(tagEndDelimiter)")
} else if tagStartDelimiter == "{{" && tagEndDelimiter == "}}" {
- buffer.appendContentsOf("\(tagStartDelimiter){\(expression)}\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter){\(expression)}\(tagEndDelimiter)")
} else {
- buffer.appendContentsOf("\(tagStartDelimiter)&\(expression)\(tagEndDelimiter)")
+ buffer.append("\(tagStartDelimiter)&\(expression)\(tagEndDelimiter)")
}
}
}
- private var buffer: String = ""
+ fileprivate var buffer: String = ""
}
diff --git a/Sources/TemplateParser.swift b/Sources/TemplateParser.swift
index cae73991..8705bd59 100644
--- a/Sources/TemplateParser.swift
+++ b/Sources/TemplateParser.swift
@@ -24,32 +24,30 @@
import Foundation
protocol TemplateTokenConsumer {
- func parser(parser:TemplateParser, shouldContinueAfterParsingToken token:TemplateToken) -> Bool
- func parser(parser:TemplateParser, didFailWithError error:ErrorType)
+ func parser(_ parser:TemplateParser, shouldContinueAfterParsingToken token:TemplateToken) -> Bool
+ func parser(_ parser:TemplateParser, didFailWithError error:Error)
}
final class TemplateParser {
let tokenConsumer: TemplateTokenConsumer
- private let tagDelimiterPair: TagDelimiterPair
+ fileprivate let tagDelimiterPair: TagDelimiterPair
init(tokenConsumer: TemplateTokenConsumer, tagDelimiterPair: TagDelimiterPair) {
self.tokenConsumer = tokenConsumer
self.tagDelimiterPair = tagDelimiterPair
}
- func parse(templateString:String, templateID: TemplateID?) {
+ func parse(_ templateString:String, templateID: TemplateID?) {
var currentDelimiters = ParserTagDelimiters(tagDelimiterPair: tagDelimiterPair)
- let templateCharacters = templateString.characters
let atString = { (index: String.Index, string: String?) -> Bool in
guard let string = string else {
return false
}
- let endIndex = index.advancedBy(string.characters.count, limit: templateCharacters.endIndex)
- return templateCharacters[index..":
- let content = templateString.substringWithRange(tagInitialIndex.successor().. 0 }
+ let tagInitialIndex = templateString.index(startIndex, offsetBy: currentDelimiters.setDelimitersStartLength)
+ let content = templateString.substring(with: tagInitialIndex.. 0 }
if (newDelimiters.count != 2) {
- let error = MustacheError(kind: .ParseError, message: "Invalid set delimiters tag", templateID: templateID, lineNumber: startLineNumber)
+ let error = MustacheError(kind: .parseError, message: "Invalid set delimiters tag", templateID: templateID, lineNumber: startLineNumber)
tokenConsumer.parser(self, didFailWithError: error)
return;
}
let token = TemplateToken(
- type: .SetDelimiters,
+ type: .setDelimiters,
lineNumber: startLineNumber,
templateID: templateID,
templateString: templateString,
- range: startIndex.. TemplateID?
+ func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID?
/**
Returns the Mustache template string that matches the template ID.
@@ -81,7 +81,7 @@ public protocol TemplateRepositoryDataSource {
throws an error that describes the problem.
- returns: A Mustache template string.
*/
- func templateStringForTemplateID(templateID: TemplateID) throws -> String
+ func templateStringForTemplateID(_ templateID: TemplateID) throws -> String
}
/**
@@ -174,8 +174,8 @@ final public class TemplateRepository {
encoding is NSUTF8StringEncoding.
- returns: A new TemplateRepository.
*/
- convenience public init(directoryPath: String, templateExtension: String? = "mustache", encoding: NSStringEncoding = NSUTF8StringEncoding) {
- self.init(dataSource: URLDataSource(baseURL: NSURL.fileURLWithPath(directoryPath, isDirectory: true), templateExtension: templateExtension, encoding: encoding))
+ convenience public init(directoryPath: String, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) {
+ self.init(dataSource: URLDataSource(baseURL: URL(fileURLWithPath: directoryPath, isDirectory: true), templateExtension: templateExtension, encoding: encoding))
}
/**
@@ -213,7 +213,7 @@ final public class TemplateRepository {
encoding is NSUTF8StringEncoding.
- returns: A new TemplateRepository.
*/
- convenience public init(baseURL: NSURL, templateExtension: String? = "mustache", encoding: NSStringEncoding = NSUTF8StringEncoding) {
+ convenience public init(baseURL: URL, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) {
self.init(dataSource: URLDataSource(baseURL: baseURL, templateExtension: templateExtension, encoding: encoding))
}
@@ -234,8 +234,8 @@ final public class TemplateRepository {
encoding is NSUTF8StringEncoding.
- returns: A new TemplateRepository.
*/
- convenience public init(bundle: NSBundle?, templateExtension: String? = "mustache", encoding: NSStringEncoding = NSUTF8StringEncoding) {
- self.init(dataSource: BundleDataSource(bundle: bundle ?? NSBundle.mainBundle(), templateExtension: templateExtension, encoding: encoding))
+ convenience public init(bundle: Bundle?, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) {
+ self.init(dataSource: BundleDataSource(bundle: bundle ?? Bundle.main, templateExtension: templateExtension, encoding: encoding))
}
@@ -288,7 +288,7 @@ final public class TemplateRepository {
problem.
- returns: A Mustache Template.
*/
- public func template(string string: String) throws -> Template {
+ public func template(string: String) throws -> Template {
let templateAST = try self.templateAST(string: string)
return Template(repository: self, templateAST: templateAST, baseContext: lockedConfiguration.baseContext)
}
@@ -339,14 +339,14 @@ final public class TemplateRepository {
func templateAST(named name: String, relativeToTemplateID baseTemplateID: TemplateID? = nil) throws -> TemplateAST {
guard let dataSource = self.dataSource else {
- throw MustacheError(kind: .TemplateNotFound, message: "Missing dataSource", templateID: baseTemplateID)
+ throw MustacheError(kind: .templateNotFound, message: "Missing dataSource", templateID: baseTemplateID)
}
guard let templateID = dataSource.templateIDForName(name, relativeToTemplateID: baseTemplateID) else {
if let baseTemplateID = baseTemplateID {
- throw MustacheError(kind: .TemplateNotFound, message: "Template not found: \"\(name)\" from \(baseTemplateID)", templateID: baseTemplateID)
+ throw MustacheError(kind: .templateNotFound, message: "Template not found: \"\(name)\" from \(baseTemplateID)", templateID: baseTemplateID)
} else {
- throw MustacheError(kind: .TemplateNotFound, message: "Template not found: \"\(name)\"")
+ throw MustacheError(kind: .templateNotFound, message: "Template not found: \"\(name)\"")
}
}
@@ -369,12 +369,12 @@ final public class TemplateRepository {
return templateAST
} catch {
// Failure: remove the empty AST
- templateASTCache.removeValueForKey(templateID)
+ templateASTCache.removeValue(forKey: templateID)
throw error
}
}
- func templateAST(string string: String, templateID: TemplateID? = nil) throws -> TemplateAST {
+ func templateAST(string: String, templateID: TemplateID? = nil) throws -> TemplateAST {
// A Compiler
let compiler = TemplateCompiler(
contentType: lockedConfiguration.contentType,
@@ -394,8 +394,8 @@ final public class TemplateRepository {
}
- private var _lockedConfiguration: Configuration?
- private var lockedConfiguration: Configuration {
+ fileprivate var _lockedConfiguration: Configuration?
+ fileprivate var lockedConfiguration: Configuration {
// Changing mutable values within the repository's configuration no
// longer has any effect.
if _lockedConfiguration == nil {
@@ -404,28 +404,28 @@ final public class TemplateRepository {
return _lockedConfiguration!
}
- private var templateASTCache: [TemplateID: TemplateAST]
+ fileprivate var templateASTCache: [TemplateID: TemplateAST]
// -------------------------------------------------------------------------
// MARK: DictionaryDataSource
- private class DictionaryDataSource: TemplateRepositoryDataSource {
+ fileprivate class DictionaryDataSource: TemplateRepositoryDataSource {
let templates: [String: String]
init(templates: [String: String]) {
self.templates = templates
}
- func templateIDForName(name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
+ func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
return name
}
- func templateStringForTemplateID(templateID: TemplateID) throws -> String {
+ func templateStringForTemplateID(_ templateID: TemplateID) throws -> String {
if let string = templates[templateID] {
return string
} else {
- throw MustacheError(kind: .TemplateNotFound, templateID: templateID)
+ throw MustacheError(kind: .templateNotFound, templateID: templateID)
}
}
}
@@ -434,25 +434,25 @@ final public class TemplateRepository {
// -------------------------------------------------------------------------
// MARK: URLDataSource
- private class URLDataSource: TemplateRepositoryDataSource {
+ fileprivate class URLDataSource: TemplateRepositoryDataSource {
let baseURLAbsoluteString: String
- let baseURL: NSURL
+ let baseURL: URL
let templateExtension: String?
- let encoding: NSStringEncoding
+ let encoding: String.Encoding
- init(baseURL: NSURL, templateExtension: String?, encoding: NSStringEncoding) {
+ init(baseURL: URL, templateExtension: String?, encoding: String.Encoding) {
self.baseURL = baseURL
- self.baseURLAbsoluteString = baseURL.URLByStandardizingPath!.absoluteString!
+ self.baseURLAbsoluteString = baseURL.standardizedFileURL.absoluteString
self.templateExtension = templateExtension
self.encoding = encoding
}
- func templateIDForName(name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
+ func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
// Rebase template names starting with a /
let normalizedName: String
let normalizedBaseTemplateID: TemplateID?
if !name.isEmpty && name[name.startIndex] == "/" {
- normalizedName = name.substringFromIndex(name.startIndex.successor())
+ normalizedName = name.substring(from: name.characters.index(after: name.startIndex))
normalizedBaseTemplateID = nil
} else {
normalizedName = name
@@ -464,32 +464,32 @@ final public class TemplateRepository {
}
let templateFilename: String
- if let templateExtension = self.templateExtension where !templateExtension.isEmpty {
- templateFilename = (normalizedName as NSString).stringByAppendingPathExtension(templateExtension)!
+ if let templateExtension = self.templateExtension , !templateExtension.isEmpty {
+ templateFilename = (normalizedName as NSString).appendingPathExtension(templateExtension)!
} else {
templateFilename = normalizedName
}
- let templateBaseURL: NSURL
+ let templateBaseURL: URL
if let normalizedBaseTemplateID = normalizedBaseTemplateID {
- templateBaseURL = NSURL(string: normalizedBaseTemplateID)!
+ templateBaseURL = URL(string: normalizedBaseTemplateID)!
} else {
templateBaseURL = self.baseURL
}
- let templateURL = NSURL(string: templateFilename, relativeToURL: templateBaseURL)!.URLByStandardizingPath!
- let templateAbsoluteString = templateURL.absoluteString!
+ let templateURL = URL(string: templateFilename, relativeTo: templateBaseURL)!.standardizedFileURL
+ let templateAbsoluteString = templateURL.absoluteString
// Make sure partial relative paths can not escape repository root
- if templateAbsoluteString.rangeOfString(baseURLAbsoluteString)?.startIndex == templateAbsoluteString.startIndex {
+ if templateAbsoluteString.range(of: baseURLAbsoluteString)?.lowerBound == templateAbsoluteString.startIndex {
return templateAbsoluteString
} else {
return nil
}
}
- func templateStringForTemplateID(templateID: TemplateID) throws -> String {
- return try NSString(contentsOfURL: NSURL(string: templateID)!, encoding: encoding) as String
+ func templateStringForTemplateID(_ templateID: TemplateID) throws -> String {
+ return try NSString(contentsOf: URL(string: templateID)!, encoding: encoding.rawValue) as String
}
}
@@ -497,23 +497,23 @@ final public class TemplateRepository {
// -------------------------------------------------------------------------
// MARK: BundleDataSource
- private class BundleDataSource: TemplateRepositoryDataSource {
- let bundle: NSBundle
+ fileprivate class BundleDataSource: TemplateRepositoryDataSource {
+ let bundle: Bundle
let templateExtension: String?
- let encoding: NSStringEncoding
+ let encoding: String.Encoding
- init(bundle: NSBundle, templateExtension: String?, encoding: NSStringEncoding) {
+ init(bundle: Bundle, templateExtension: String?, encoding: String.Encoding) {
self.bundle = bundle
self.templateExtension = templateExtension
self.encoding = encoding
}
- func templateIDForName(name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
+ func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
// Rebase template names starting with a /
let normalizedName: String
let normalizedBaseTemplateID: TemplateID?
if !name.isEmpty && name[name.startIndex] == "/" {
- normalizedName = name.substringFromIndex(name.startIndex.successor())
+ normalizedName = name.substring(from: name.characters.index(after: name.startIndex))
normalizedBaseTemplateID = nil
} else {
normalizedName = name
@@ -525,15 +525,15 @@ final public class TemplateRepository {
}
if let normalizedBaseTemplateID = normalizedBaseTemplateID {
- let relativePath = (normalizedBaseTemplateID as NSString).stringByDeletingLastPathComponent.stringByReplacingOccurrencesOfString(bundle.resourcePath!, withString:"")
- return bundle.pathForResource(normalizedName, ofType: templateExtension, inDirectory: relativePath)
+ let relativePath = (normalizedBaseTemplateID as NSString).deletingLastPathComponent.replacingOccurrences(of: bundle.resourcePath!, with:"")
+ return bundle.path(forResource: normalizedName, ofType: templateExtension, inDirectory: relativePath)
} else {
- return bundle.pathForResource(normalizedName, ofType: templateExtension)
+ return bundle.path(forResource: normalizedName, ofType: templateExtension)
}
}
- func templateStringForTemplateID(templateID: TemplateID) throws -> String {
- return try NSString(contentsOfFile: templateID, encoding: encoding) as String
+ func templateStringForTemplateID(_ templateID: TemplateID) throws -> String {
+ return try NSString(contentsOfFile: templateID, encoding: encoding.rawValue) as String
}
}
}
diff --git a/Sources/TemplateToken.swift b/Sources/TemplateToken.swift
index ecc353ae..fee3e58d 100644
--- a/Sources/TemplateToken.swift
+++ b/Sources/TemplateToken.swift
@@ -24,40 +24,40 @@
struct TemplateToken {
enum `Type` {
/// text
- case Text(text: String)
+ case text(text: String)
/// {{ content }}
- case EscapedVariable(content: String, tagDelimiterPair: TagDelimiterPair)
+ case escapedVariable(content: String, tagDelimiterPair: TagDelimiterPair)
/// {{{ content }}}
- case UnescapedVariable(content: String, tagDelimiterPair: TagDelimiterPair)
+ case unescapedVariable(content: String, tagDelimiterPair: TagDelimiterPair)
/// {{! comment }}
- case Comment
+ case comment
/// {{# content }}
- case Section(content: String, tagDelimiterPair: TagDelimiterPair)
+ case section(content: String, tagDelimiterPair: TagDelimiterPair)
/// {{^ content }}
- case InvertedSection(content: String, tagDelimiterPair: TagDelimiterPair)
+ case invertedSection(content: String, tagDelimiterPair: TagDelimiterPair)
/// {{/ content }}
- case Close(content: String)
+ case close(content: String)
/// {{> content }}
- case Partial(content: String)
+ case partial(content: String)
/// {{= ... ... =}}
- case SetDelimiters
+ case setDelimiters
/// {{% content }}
- case Pragma(content: String)
+ case pragma(content: String)
/// {{< content }}
- case PartialOverride(content: String)
+ case partialOverride(content: String)
/// {{$ content }}
- case Block(content: String)
+ case block(content: String)
}
let type: Type
@@ -70,13 +70,13 @@ struct TemplateToken {
var tagDelimiterPair: TagDelimiterPair? {
switch type {
- case .EscapedVariable(content: _, tagDelimiterPair: let tagDelimiterPair):
+ case .escapedVariable(content: _, tagDelimiterPair: let tagDelimiterPair):
return tagDelimiterPair
- case .UnescapedVariable(content: _, tagDelimiterPair: let tagDelimiterPair):
+ case .unescapedVariable(content: _, tagDelimiterPair: let tagDelimiterPair):
return tagDelimiterPair
- case .Section(content: _, tagDelimiterPair: let tagDelimiterPair):
+ case .section(content: _, tagDelimiterPair: let tagDelimiterPair):
return tagDelimiterPair
- case .InvertedSection(content: _, tagDelimiterPair: let tagDelimiterPair):
+ case .invertedSection(content: _, tagDelimiterPair: let tagDelimiterPair):
return tagDelimiterPair
default:
return nil
diff --git a/Sources/URLEscapeHelper.swift b/Sources/URLEscapeHelper.swift
index 07d84739..11e0096c 100644
--- a/Sources/URLEscapeHelper.swift
+++ b/Sources/URLEscapeHelper.swift
@@ -40,7 +40,7 @@ final class URLEscapeHelper : MustacheBoxable {
}
// This function is used for evaluating `URLEscape(x)` expressions.
- private func filter(rendering: Rendering) throws -> Rendering {
+ fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(URLEscapeHelper.escapeURL(rendering.string), rendering.contentType)
}
@@ -49,24 +49,24 @@ final class URLEscapeHelper : MustacheBoxable {
//
// It is activated as soon as the formatter enters the context stack, when
// used in a section {{# URLEscape }}...{{/ URLEscape }}.
- private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {
+ fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
switch tag.type {
- case .Variable:
+ case .variable:
// We don't know if the box contains a String, so let's escape its
// rendering.
return Box({ (info: RenderingInfo) -> Rendering in
- let rendering = try box.render(info: info)
+ let rendering = try box.render(info)
return try self.filter(rendering)
})
- case .Section:
+ case .section:
return box
}
}
- private class func escapeURL(string: String) -> String {
- let s = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet
- s.removeCharactersInString("?&=")
- return string.stringByAddingPercentEncodingWithAllowedCharacters(s) ?? ""
+ fileprivate class func escapeURL(_ string: String) -> String {
+ let s = (CharacterSet.urlQueryAllowed as NSCharacterSet).mutableCopy() as! NSMutableCharacterSet
+ s.removeCharacters(in: "?&=")
+ return string.addingPercentEncoding(withAllowedCharacters: s as CharacterSet) ?? ""
}
}
diff --git a/Sources/VariableTag.swift b/Sources/VariableTag.swift
index 8e385014..dd910f3d 100644
--- a/Sources/VariableTag.swift
+++ b/Sources/VariableTag.swift
@@ -37,7 +37,7 @@ final class VariableTag: LocatedTag {
// Mark: - Tag protocol
- let type: TagType = .Variable
+ let type: TagType = .variable
let innerTemplateString: String = ""
var tagDelimiterPair: TagDelimiterPair { return token.tagDelimiterPair! }
@@ -46,7 +46,7 @@ final class VariableTag: LocatedTag {
}
// Variable have no inner content.
- func render(context: Context) throws -> Rendering {
+ func render(_ context: Context) throws -> Rendering {
return Rendering("", contentType)
}
@@ -54,4 +54,4 @@ final class VariableTag: LocatedTag {
var templateID: TemplateID? { return token.templateID }
var lineNumber: Int { return token.lineNumber }
-}
\ No newline at end of file
+}
diff --git a/Sources/ZipFilter.swift b/Sources/ZipFilter.swift
index 912a2387..16bb16df 100644
--- a/Sources/ZipFilter.swift
+++ b/Sources/ZipFilter.swift
@@ -30,17 +30,17 @@ let ZipFilter = VariadicFilter { (boxes) in
//
// Other kinds of arguments generate an error.
- var zippedGenerators: [AnyGenerator] = []
+ var zippedGenerators: [AnyIterator] = []
for box in boxes {
if box.isEmpty {
// Missing collection does not provide anything
} else if let array = box.arrayValue {
// Array
- zippedGenerators.append(AnyGenerator(array.generate()))
+ zippedGenerators.append(AnyIterator(array.makeIterator()))
} else {
// Error
- throw MustacheError(kind: .RenderError, message: "Non-enumerable argument in zip filter: `\(box.value)`")
+ throw MustacheError(kind: .renderError, message: "Non-enumerable argument in zip filter: `\(box.value)`")
}
}
@@ -88,4 +88,4 @@ let ZipFilter = VariadicFilter { (boxes) in
// Return a box of those boxed render functions
return Box(renderFunctions.map(Box))
-}
\ No newline at end of file
+}
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index 6e9c1b0d..9e1c3b2d 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -70,28 +70,28 @@ class BoxTests: XCTestCase {
let boxableStruct = BoxableStruct(name: "BoxableStruct")
let boxableClass = BoxableClass(name: "BoxableClass")
let optionalBoxableClass: BoxableClass? = BoxableClass(name: "BoxableClass")
- let NSObject = NSDate()
+ let object = Date()
let boxedBoxableStruct = boxableStruct.mustacheBox()
let boxedStruct = MustacheBox(value: Struct(name: "Struct"))
let boxedBoxableClass = boxableClass.mustacheBox()
let boxedOptionalBoxableClass = optionalBoxableClass!.mustacheBox()
let boxedClass = MustacheBox(value: Class(name: "Class"))
- let boxedNSObject = Box(NSObject)
+ let boxedObject = Box(object)
let extractedBoxableStruct = boxedBoxableStruct.value as! BoxableStruct
let extractedStruct = boxedStruct.value as! Struct
let extractedBoxableClass = boxedBoxableClass.value as! BoxableClass
let extractedOptionalBoxableClass = boxedOptionalBoxableClass.value as? BoxableClass
let extractedClass = boxedClass.value as! Class
- let extractedNSObject = boxedNSObject.value as! NSDate
+ let extractedObject = boxedObject.value as! Date
XCTAssertEqual(extractedBoxableStruct.name, "BoxableStruct")
XCTAssertEqual(extractedStruct.name, "Struct")
XCTAssertEqual(extractedBoxableClass.name, "BoxableClass")
XCTAssertEqual(extractedOptionalBoxableClass!.name, "BoxableClass")
XCTAssertEqual(extractedClass.name, "Class")
- XCTAssertEqual(extractedNSObject, NSObject)
+ XCTAssertEqual(extractedObject, object)
}
// TODO: why is this test commented out?
@@ -159,7 +159,7 @@ class BoxTests: XCTestCase {
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
- XCTAssertTrue(["012", "021", "102", "120", "201", "210"].indexOf(rendering) != nil)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
func testSetOfMustacheBoxable() {
@@ -167,7 +167,7 @@ class BoxTests: XCTestCase {
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
- XCTAssertTrue(["012", "021", "102", "120", "201", "210"].indexOf(rendering) != nil)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
func testDictionaryOfInt() {
@@ -282,14 +282,14 @@ class BoxTests: XCTestCase {
func testArrayValueForRange() {
let originalValue = 1...3
let box = Box(originalValue)
- let extractedValue = box.value as! Range
+ let extractedValue = box.value as! CountableRange
XCTAssertEqual(extractedValue, originalValue)
let extractedArray: [MustacheBox] = box.arrayValue!
XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
}
func testDictionaryValueForNSDictionary() {
- let originalValue = NSDictionary(object: "value", forKey: "key")
+ let originalValue = NSDictionary(object: "value", forKey: "key" as NSCopying)
let box = Box(originalValue)
let extractedValue = box.value as! NSDictionary
XCTAssertEqual(extractedValue, originalValue)
diff --git a/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift b/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift
index e0120957..5d08e809 100644
--- a/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift
+++ b/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift
@@ -33,24 +33,24 @@ class ConfigurationContentTypeTests: XCTestCase {
func testFactoryConfigurationHasHTMLContentTypeRegardlessOfDefaultConfiguration() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
var configuration = Configuration()
- XCTAssertEqual(configuration.contentType, ContentType.HTML)
+ XCTAssertEqual(configuration.contentType, ContentType.html)
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
configuration = Configuration()
- XCTAssertEqual(configuration.contentType, ContentType.HTML)
+ XCTAssertEqual(configuration.contentType, ContentType.html)
}
func testDefaultConfigurationContentTypeHTMLHasTemplateRenderEscapedInput() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
let template = try! Template(string: "{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
}
func testDefaultConfigurationContentTypeTextLHasTemplateRenderUnescapedInput() {
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
let template = try! Template(string: "{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
@@ -64,7 +64,7 @@ class ConfigurationContentTypeTests: XCTestCase {
//
// Thus we'll use a rendering object that will provide us with one:
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
let testedTemplate = try! Template(string: "")
var testedContentType: ContentType?
@@ -75,8 +75,8 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- try! template.render(Box(render))
- XCTAssertEqual(testedContentType!, ContentType.HTML)
+ _ = try! template.render(Box(render))
+ XCTAssertEqual(testedContentType!, ContentType.html)
}
func testDefaultConfigurationContentTypeTextHasTemplateRenderText() {
@@ -87,7 +87,7 @@ class ConfigurationContentTypeTests: XCTestCase {
//
// Thus we'll use a rendering object that will provide us with one:
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
let testedTemplate = try! Template(string: "")
var testedContentType: ContentType?
@@ -98,12 +98,12 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- try! template.render(Box(render))
- XCTAssertEqual(testedContentType!, ContentType.Text)
+ _ = try! template.render(Box(render))
+ XCTAssertEqual(testedContentType!, ContentType.text)
}
func testDefaultConfigurationContentTypeHTMLHasSectionTagRenderHTML() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
var testedContentType: ContentType?
let render = { (info: RenderingInfo) -> Rendering in
@@ -113,12 +113,12 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{#.}}{{/.}}")
- try! template.render(Box(render))
- XCTAssertEqual(testedContentType!, ContentType.HTML)
+ _ = try! template.render(Box(render))
+ XCTAssertEqual(testedContentType!, ContentType.html)
}
func testDefaultConfigurationContentTypeTextHasSectionTagRenderText() {
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
var testedContentType: ContentType?
let render = { (info: RenderingInfo) -> Rendering in
@@ -128,12 +128,12 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{#.}}{{/.}}")
- try! template.render(Box(render))
- XCTAssertEqual(testedContentType!, ContentType.Text)
+ _ = try! template.render(Box(render))
+ XCTAssertEqual(testedContentType!, ContentType.text)
}
func testDefaultConfigurationContentTypeHTMLHasVariableTagRenderHTML() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
var testedContentType: ContentType?
let render = { (info: RenderingInfo) -> Rendering in
@@ -143,12 +143,12 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- try! template.render(Box(render))
- XCTAssertEqual(testedContentType!, ContentType.HTML)
+ _ = try! template.render(Box(render))
+ XCTAssertEqual(testedContentType!, ContentType.html)
}
func testDefaultConfigurationContentTypeTextHasVariableTagRenderText() {
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
var testedContentType: ContentType?
@@ -159,37 +159,37 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- try! template.render(Box(render))
- XCTAssertEqual(testedContentType!, ContentType.Text)
+ _ = try! template.render(Box(render))
+ XCTAssertEqual(testedContentType!, ContentType.text)
}
func testPragmaContentTypeTextOverridesDefaultConfiguration() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
let template = try! Template(string:"{{%CONTENT_TYPE:TEXT}}{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
}
func testPragmaContentTypeHTMLOverridesDefaultConfiguration() {
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
let template = try! Template(string:"{{%CONTENT_TYPE:HTML}}{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
}
func testDefaultRepositoryConfigurationHasDefaultConfigurationContentType() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
var repo = TemplateRepository()
- XCTAssertEqual(repo.configuration.contentType, ContentType.HTML)
+ XCTAssertEqual(repo.configuration.contentType, ContentType.html)
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
repo = TemplateRepository()
- XCTAssertEqual(repo.configuration.contentType, ContentType.Text)
+ XCTAssertEqual(repo.configuration.contentType, ContentType.text)
}
func testRepositoryConfigurationContentTypeHTMLHasTemplateRenderEscapedInputWhenSettingTheWholeConfiguration() {
var configuration = Configuration()
- configuration.contentType = .HTML
+ configuration.contentType = .html
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
@@ -199,7 +199,7 @@ class ConfigurationContentTypeTests: XCTestCase {
func testRepositoryConfigurationContentTypeHTMLHasTemplateRenderEscapedInputWhenUpdatingRepositoryConfiguration() {
let repository = TemplateRepository()
- repository.configuration.contentType = .HTML
+ repository.configuration.contentType = .html
let template = try! repository.template(string: "{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
@@ -207,7 +207,7 @@ class ConfigurationContentTypeTests: XCTestCase {
func testRepositoryConfigurationContentTypeTextHasTemplateRenderUnescapedInputWhenSettingTheWholeConfiguration() {
var configuration = Configuration()
- configuration.contentType = .Text
+ configuration.contentType = .text
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
@@ -217,16 +217,16 @@ class ConfigurationContentTypeTests: XCTestCase {
func testRepositoryConfigurationContentTypeTextHasTemplateRenderUnescapedInputWhenUpdatingRepositoryConfiguration() {
let repository = TemplateRepository()
- repository.configuration.contentType = .Text
+ repository.configuration.contentType = .text
let template = try! repository.template(string: "{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
}
func testRepositoryConfigurationContentTypeTextOverridesDefaultConfigurationContentTypeHTMLWhenSettingTheWholeConfiguration() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
var configuration = Configuration()
- configuration.contentType = .Text
+ configuration.contentType = .text
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
@@ -235,18 +235,18 @@ class ConfigurationContentTypeTests: XCTestCase {
}
func testRepositoryConfigurationContentTypeTextOverridesDefaultConfigurationContentTypeHTMLWhenUpdatingRepositoryConfiguration() {
- DefaultConfiguration.contentType = .HTML
+ DefaultConfiguration.contentType = .html
let repository = TemplateRepository()
- repository.configuration.contentType = .Text
+ repository.configuration.contentType = .text
let template = try! repository.template(string: "{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
}
func testRepositoryConfigurationContentTypeHTMLOverridesDefaultConfigurationContentTypeTextWhenSettingTheWholeConfiguration() {
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
var configuration = Configuration()
- configuration.contentType = .HTML
+ configuration.contentType = .html
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
@@ -255,9 +255,9 @@ class ConfigurationContentTypeTests: XCTestCase {
}
func testRepositoryConfigurationContentTypeHTMLOverridesDefaultConfigurationContentTypeTextWhenUpdatingRepositoryConfiguration() {
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
let repository = TemplateRepository()
- repository.configuration.contentType = .HTML
+ repository.configuration.contentType = .html
let template = try! repository.template(string: "{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
@@ -265,7 +265,7 @@ class ConfigurationContentTypeTests: XCTestCase {
func testPragmaContentTypeTextOverridesRepositoryConfigurationContentTypeHTMLWhenSettingTheWholeConfiguration() {
var configuration = Configuration()
- configuration.contentType = .HTML
+ configuration.contentType = .html
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{%CONTENT_TYPE:TEXT}}{{.}}")
@@ -275,7 +275,7 @@ class ConfigurationContentTypeTests: XCTestCase {
func testPragmaContentTypeTextOverridesRepositoryConfigurationContentTypeHTMLWhenUpdatingRepositoryConfiguration() {
let repository = TemplateRepository()
- repository.configuration.contentType = .HTML
+ repository.configuration.contentType = .html
let template = try! repository.template(string: "{{%CONTENT_TYPE:TEXT}}{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
@@ -283,7 +283,7 @@ class ConfigurationContentTypeTests: XCTestCase {
func testPragmaContentTypeHTMLOverridesRepositoryConfigurationContentTypeTextWhenSettingTheWholeConfiguration() {
var configuration = Configuration()
- configuration.contentType = .Text
+ configuration.contentType = .text
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{%CONTENT_TYPE:HTML}}{{.}}")
@@ -293,7 +293,7 @@ class ConfigurationContentTypeTests: XCTestCase {
func testPragmaContentTypeHTMLOverridesRepositoryConfigurationContentTypeTextWhenUpdatingRepositoryConfiguration() {
let repository = TemplateRepository()
- repository.configuration.contentType = .Text
+ repository.configuration.contentType = .text
let template = try! repository.template(string: "{{%CONTENT_TYPE:HTML}}{{.}}")
let rendering = try! template.render(Box("&"))
XCTAssertEqual(rendering, "&")
@@ -305,7 +305,7 @@ class ConfigurationContentTypeTests: XCTestCase {
var rendering = try! repository.template(string: "{{.}}").render(Box("&"))
XCTAssertEqual(rendering, "&")
- DefaultConfiguration.contentType = .Text
+ DefaultConfiguration.contentType = .text
rendering = try! repository.template(string: "{{.}}").render(Box("&"))
XCTAssertEqual(rendering, "&")
}
@@ -316,12 +316,12 @@ class ConfigurationContentTypeTests: XCTestCase {
var rendering = try! repository.template(string: "{{.}}").render(Box("&"))
XCTAssertEqual(rendering, "&")
- repository.configuration.contentType = .Text
+ repository.configuration.contentType = .text
rendering = try! repository.template(string: "{{.}}").render(Box("&"))
XCTAssertEqual(rendering, "&")
var configuration = Configuration()
- configuration.contentType = .Text
+ configuration.contentType = .text
repository.configuration = configuration
rendering = try! repository.template(string: "{{.}}").render(Box("&"))
XCTAssertEqual(rendering, "&")
diff --git a/Tests/Public/ContextTests/ContextTests.swift b/Tests/Public/ContextTests/ContextTests.swift
index 99c8bdfb..384a29b2 100644
--- a/Tests/Public/ContextTests/ContextTests.swift
+++ b/Tests/Public/ContextTests/ContextTests.swift
@@ -50,7 +50,7 @@ class ContextTests: XCTestCase {
}
let template = try! Template(string: "{{success}}")
template.baseContext = Context(Box(willRender))
- try! template.render()
+ _ = try! template.render()
XCTAssertTrue(success)
}
diff --git a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
index 811ccbe2..537fab21 100644
--- a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
+++ b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
@@ -49,7 +49,7 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
func testFilteredExpression() {
let filter = Filter({ (string: String?) -> MustacheBox in
- return Box(string!.uppercaseString)
+ return Box(string!.uppercased())
})
let context = Context(Box(["name": Box("success"), "f": Box(filter)]))
let box = try! context.mustacheBoxForExpression("f(name)")
@@ -60,10 +60,10 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
func testParseError() {
let context = Context()
do {
- try context.mustacheBoxForExpression("a.")
+ _ = try context.mustacheBoxForExpression("a.")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError) // Invalid expression
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError) // Invalid expression
} catch {
XCTFail("Expected MustacheError")
}
@@ -72,10 +72,10 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
func testRenderError() {
let context = Context()
do {
- try context.mustacheBoxForExpression("f(x)")
+ _ = try context.mustacheBoxForExpression("f(x)")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError) // Missing filter
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError) // Missing filter
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift b/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
index 8af53931..b95500a1 100644
--- a/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
+++ b/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
@@ -39,11 +39,11 @@ class MustacheBoxDocumentationTests: XCTestCase {
func testRenderingInfoDocumentation() {
let render: RenderFunction = { (info: RenderingInfo) -> Rendering in
switch info.tag.type {
- case .Variable:
+ case .variable:
// Render a {{object}} variable tag
return Rendering("variable")
- case .Section:
+ case .section:
// Render a {{#object}}...{{/object}} section tag.
//
// Extend the current context with ["value": "foo"], and proceed
diff --git a/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift b/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
index 10ce7a4d..1540688d 100644
--- a/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
+++ b/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
@@ -29,9 +29,9 @@ class MustacheRenderableGuideTests: XCTestCase {
func testExample1() {
let render = { (info: RenderingInfo) -> Rendering in
switch info.tag.type {
- case .Variable:
+ case .variable:
return Rendering("I'm rendering a {{ variable }} tag.")
- case .Section:
+ case .section:
return Rendering("I'm rendering a {{# section }}...{{/ }} tag.")
}
}
@@ -123,7 +123,7 @@ class MustacheRenderableGuideTests: XCTestCase {
}
}
let render = { (info: RenderingInfo) -> Rendering in
- let template = try! Template(named: "Person", bundle: NSBundle(forClass: MustacheRenderableGuideTests.self))
+ let template = try! Template(named: "Person", bundle: Bundle(for: MustacheRenderableGuideTests.self))
let context = info.context.extendedContext(Box(self))
return try template.render(context)
}
@@ -149,7 +149,7 @@ class MustacheRenderableGuideTests: XCTestCase {
}
}
let render = { (info: RenderingInfo) -> Rendering in
- let template = try! Template(named: "Movie", bundle: NSBundle(forClass: MustacheRenderableGuideTests.self))
+ let template = try! Template(named: "Movie", bundle: Bundle(for: MustacheRenderableGuideTests.self))
let context = info.context.extendedContext(Box(self))
return try template.render(context)
}
@@ -181,7 +181,7 @@ class MustacheRenderableGuideTests: XCTestCase {
buffer += "\(itemRendering.string)"
}
buffer += ""
- return Rendering(buffer, .HTML)
+ return Rendering(buffer, .html)
}
let template = try! Template(string: "{{#list(nav)}}{{title}}{{/}}")
diff --git a/Tests/Public/DocumentationTests/ReadMeTests.swift b/Tests/Public/DocumentationTests/ReadMeTests.swift
index 327ff1dc..c7be05d8 100644
--- a/Tests/Public/DocumentationTests/ReadMeTests.swift
+++ b/Tests/Public/DocumentationTests/ReadMeTests.swift
@@ -23,6 +23,26 @@
import XCTest
import Mustache
+fileprivate func < (lhs: T?, rhs: T?) -> Bool {
+ switch (lhs, rhs) {
+ case let (l?, r?):
+ return l < r
+ case (nil, _?):
+ return true
+ default:
+ return false
+ }
+}
+
+fileprivate func > (lhs: T?, rhs: T?) -> Bool {
+ switch (lhs, rhs) {
+ case let (l?, r?):
+ return l > r
+ default:
+ return rhs < lhs
+ }
+}
+
class ReadMeTests: XCTestCase {
@@ -32,13 +52,13 @@ class ReadMeTests: XCTestCase {
}
func testReadmeExample1() {
- let testBundle = NSBundle(forClass: self.dynamicType)
+ let testBundle = Bundle(for: type(of: self))
let template = try! Template(named: "ReadMeExample1", bundle: testBundle)
let data = [
"name": "Chris",
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
- "in_ca": true]
+ "in_ca": true] as [String : Any]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "Hello Chris\nYou have just won 10000 dollars!\n\nWell, 6000.0 dollars, after taxes.\n")
}
@@ -68,7 +88,7 @@ class ReadMeTests: XCTestCase {
// I have 3 cats.
- let testBundle = NSBundle(forClass: self.dynamicType)
+ let testBundle = Bundle(for: type(of: self))
let template = try! Template(named: "ReadMeExample2", bundle: testBundle)
let data = ["cats": ["Kitty", "Pussy", "Melba"]]
let rendering = try! template.render(Box(data))
@@ -102,9 +122,9 @@ class ReadMeTests: XCTestCase {
}
func testReadMeExampleNSFormatter1() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
- percentFormatter.numberStyle = .PercentStyle
+ let percentFormatter = NumberFormatter()
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
+ percentFormatter.numberStyle = .percent
let template = try! Template(string: "{{ percent(x) }}")
template.registerInBaseContext("percent", Box(percentFormatter))
@@ -115,9 +135,9 @@ class ReadMeTests: XCTestCase {
}
func testReadMeExampleNSFormatter2() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
- percentFormatter.numberStyle = .PercentStyle
+ let percentFormatter = NumberFormatter()
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
+ percentFormatter.numberStyle = .percent
let template = try! Template(string: "{{# percent }}{{ x }}{{/ }}")
template.registerInBaseContext("percent", Box(percentFormatter))
@@ -134,18 +154,18 @@ class ReadMeTests: XCTestCase {
"Well, on {{format(real_date)}} due to Martian attack.\n" +
"{{/late}}"
- let dateFormatter = NSDateFormatter()
- dateFormatter.dateStyle = .MediumStyle
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .medium
let template = try! Template(string: templateString)
template.registerInBaseContext("format", Box(dateFormatter))
let data = [
"name": "Arthur",
- "date": NSDate(),
- "real_date": NSDate().dateByAddingTimeInterval(60*60*24*3),
+ "date": Date(),
+ "real_date": Date().addingTimeInterval(60*60*24*3),
"late": true
- ]
+ ] as [String : Any]
let rendering = try! template.render(Box(data))
XCTAssert(rendering.characters.count > 0)
}
diff --git a/Tests/Public/FilterTests/FilterTests.swift b/Tests/Public/FilterTests/FilterTests.swift
index fef58e12..b1c67ca5 100644
--- a/Tests/Public/FilterTests/FilterTests.swift
+++ b/Tests/Public/FilterTests/FilterTests.swift
@@ -26,15 +26,15 @@ import Mustache
class FilterTests: XCTestCase {
- enum CustomError : ErrorType {
- case Error
+ enum CustomError : Error {
+ case error
}
func testFilterCanChain() {
let box = Box([
"name": Box("Name"),
"uppercase": Box(Filter { (string: String?) -> MustacheBox in
- return Box(string?.uppercaseString)
+ return Box(string?.uppercased())
}),
"prefix": Box(Filter { (string: String?) -> MustacheBox in
return Box("prefix\(string!)")
@@ -140,40 +140,40 @@ class FilterTests: XCTestCase {
var template = try! Template(string:"<{{missing(missing)}}>")
do {
- try template.render(box)
+ _ = try template.render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
template = try! Template(string:"<{{missing(name)}}>")
do {
- try template.render(box)
+ _ = try template.render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
template = try! Template(string:"<{{replace(missing(name))}}>")
do {
- try template.render(box)
+ _ = try template.render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
template = try! Template(string:"<{{missing(replace(name))}}>")
do {
- try template.render(box)
+ _ = try template.render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
@@ -187,10 +187,10 @@ class FilterTests: XCTestCase {
let template = try! Template(string:"<{{filter(name)}}>")
do {
- try template.render(box)
+ _ = try template.render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
@@ -200,12 +200,12 @@ class FilterTests: XCTestCase {
func testMissingFilterErrorDescriptionContainsLineNumber() {
let template = try! Template(string: "\n{{f(x)}}")
do {
- try template.render()
+ _ = try template.render()
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
- XCTAssertTrue(error.description.rangeOfString("Missing filter") != nil)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
+ XCTAssertTrue(error.description.range(of: "Missing filter") != nil)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -220,12 +220,12 @@ class FilterTests: XCTestCase {
func testNotAFilterErrorDescriptionContainsLineNumber() {
let template = try! Template(string: "\n{{f(x)}}")
do {
- try template.render(Box(["f": "foo"]))
+ _ = try template.render(Box(["f": "foo"]))
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
- XCTAssertTrue(error.description.rangeOfString("Not a filter") != nil)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
+ XCTAssertTrue(error.description.range(of: "Not a filter") != nil)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -295,20 +295,20 @@ class FilterTests: XCTestCase {
func testFilterCanThrowMustacheError() {
let filter = Filter { (box: MustacheBox) in
- throw MustacheError(kind: .RenderError, message: "CustomMessage")
+ throw MustacheError(kind: .renderError, message: "CustomMessage")
}
let template = try! Template(string: "\n\n{{f(x)}}")
template.registerInBaseContext("f", Box(filter))
do {
- try template.render()
+ _ = try template.render()
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
- XCTAssertTrue(error.description.rangeOfString("CustomMessage") != nil)
- XCTAssertTrue(error.description.rangeOfString("line 3") != nil)
- XCTAssertTrue(error.description.rangeOfString("{{f(x)}}") != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
+ XCTAssertTrue(error.description.range(of: "CustomMessage") != nil)
+ XCTAssertTrue(error.description.range(of: "line 3") != nil)
+ XCTAssertTrue(error.description.range(of: "{{f(x)}}") != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -323,10 +323,10 @@ class FilterTests: XCTestCase {
template.registerInBaseContext("f", Box(filter))
do {
- try template.render()
+ _ = try template.render()
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
if let nserror = error.underlyingError as? NSError {
XCTAssertEqual(nserror.domain, "CustomErrorDomain")
} else {
@@ -339,17 +339,17 @@ class FilterTests: XCTestCase {
func testFilterCanThrowCustomError() {
let filter = Filter { (box: MustacheBox) in
- throw CustomError.Error
+ throw CustomError.error
}
let template = try! Template(string: "\n\n{{f(x)}}")
template.registerInBaseContext("f", Box(filter))
do {
- try template.render()
+ _ = try template.render()
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
if let _ = error.underlyingError as? CustomError {
} else {
XCTFail("Expected NSError")
diff --git a/Tests/Public/FilterTests/VariadicFilterTests.swift b/Tests/Public/FilterTests/VariadicFilterTests.swift
index 86a0a336..7bf7796d 100644
--- a/Tests/Public/FilterTests/VariadicFilterTests.swift
+++ b/Tests/Public/FilterTests/VariadicFilterTests.swift
@@ -28,7 +28,7 @@ class VariadicFilterTests: XCTestCase {
func testVariadicFilterCanAccessArguments() {
let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
- return Box(boxes.map { ($0.value as? String) ?? "" }.joinWithSeparator(","))
+ return Box(boxes.map { ($0.value as? String) ?? "" }.joined(separator: ","))
})
let box = Box([
"a": Box("a"),
@@ -42,7 +42,7 @@ class VariadicFilterTests: XCTestCase {
func testVariadicFilterCanReturnFilter() {
let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
- let joined = boxes.map { ($0.value as? String) ?? "" }.joinWithSeparator(",")
+ let joined = boxes.map { ($0.value as? String) ?? "" }.joined(separator: ",")
return Box(Filter({ (box: MustacheBox) -> MustacheBox in
return Box(joined + "+" + ((box.value as? String) ?? ""))
}))
diff --git a/Tests/Public/FoundationCollectionTests.swift b/Tests/Public/FoundationCollectionTests.swift
index 3687ebb7..afb388cc 100644
--- a/Tests/Public/FoundationCollectionTests.swift
+++ b/Tests/Public/FoundationCollectionTests.swift
@@ -37,7 +37,7 @@ class FoundationCollectionTests: XCTestCase {
boxedArray = Box(["collection": [["key": "value"]]])
boxedNSArray = {
let array = NSMutableArray()
- array.addObject(["key": "value"])
+ array.add(["key": "value"])
let data = NSMutableDictionary()
data.setObject(array, forKey: "collection")
return Box(data)
@@ -45,14 +45,14 @@ class FoundationCollectionTests: XCTestCase {
boxedSet = Box(["collection": Set([["key": "value"]])])
boxedNSSet = {
let set = NSMutableSet()
- set.addObject(["key": "value"])
+ set.add(["key": "value"])
let data = NSMutableDictionary()
data.setObject(set, forKey: "collection")
return Box(data)
}()
boxedNSOrderedSet = {
let orderedSet = NSMutableOrderedSet()
- orderedSet.addObject(["key": "value"])
+ orderedSet.add(["key": "value"])
let data = NSMutableDictionary()
data.setObject(orderedSet, forKey: "collection")
return Box(data)
diff --git a/Tests/Public/HookFunctionTests.swift b/Tests/Public/HookFunctionTests.swift
index 37021fdb..73f48ccb 100644
--- a/Tests/Public/HookFunctionTests.swift
+++ b/Tests/Public/HookFunctionTests.swift
@@ -26,8 +26,8 @@ import Mustache
class HookFunctionTests: XCTestCase {
- enum CustomError : ErrorType {
- case Error
+ enum CustomError : Error {
+ case error
}
func testWillRenderFunctionIsNotTriggeredByText() {
@@ -76,8 +76,8 @@ class HookFunctionTests: XCTestCase {
let rendering = try! template.render(Box(["foo": "value"]))
XCTAssertEqual(rendering, "---1---")
- XCTAssertEqual(preRenderingTagType!, TagType.Variable)
- XCTAssertEqual(postRenderingTagType!, TagType.Variable)
+ XCTAssertEqual(preRenderingTagType!, TagType.variable)
+ XCTAssertEqual(postRenderingTagType!, TagType.variable)
XCTAssertEqual((preRenderingValue?.value as! String), "value")
XCTAssertEqual((postRenderingValue?.value as! Int), 1)
}
@@ -98,8 +98,8 @@ class HookFunctionTests: XCTestCase {
let rendering = try! template.render()
XCTAssertEqual(rendering, "<>")
- XCTAssertEqual(preRenderingTagType!, TagType.Section)
- XCTAssertEqual(postRenderingTagType!, TagType.Section)
+ XCTAssertEqual(preRenderingTagType!, TagType.section)
+ XCTAssertEqual(postRenderingTagType!, TagType.section)
}
func testMultipleTagsObserver() {
@@ -132,10 +132,10 @@ class HookFunctionTests: XCTestCase {
XCTAssertTrue(preRenderingValues[1].isEmpty)
XCTAssertEqual((postRenderingValues[0].value as! String), "observer")
XCTAssertEqual((postRenderingValues[1].value as! Bool), true)
- XCTAssertEqual(preRenderingTagTypes[0], TagType.Section)
- XCTAssertEqual(preRenderingTagTypes[1], TagType.Variable)
- XCTAssertEqual(postRenderingTagTypes[0], TagType.Variable)
- XCTAssertEqual(postRenderingTagTypes[1], TagType.Section)
+ XCTAssertEqual(preRenderingTagTypes[0], TagType.section)
+ XCTAssertEqual(preRenderingTagTypes[1], TagType.variable)
+ XCTAssertEqual(postRenderingTagTypes[0], TagType.variable)
+ XCTAssertEqual(postRenderingTagTypes[1], TagType.section)
}
func testObserverInterpretsRenderedValue() {
@@ -147,7 +147,7 @@ class HookFunctionTests: XCTestCase {
return box
}
let filter = { (string: String?) -> MustacheBox in
- return Box(string?.uppercaseString)
+ return Box(string?.uppercased())
}
var template = try! Template(string: "{{subject}}")
@@ -253,7 +253,7 @@ class HookFunctionTests: XCTestCase {
template.baseContext = template.baseContext.extendedContext(Box(didRender))
failedRendering = false
do {
- try template.render(Box({ (info: RenderingInfo) -> Rendering in
+ _ = try template.render(Box({ (info: RenderingInfo) -> Rendering in
throw NSError(domain: "TagObserverError", code: 1, userInfo: nil)
}))
XCTAssert(false)
@@ -274,11 +274,11 @@ class HookFunctionTests: XCTestCase {
template.baseContext = template.baseContext.extendedContext(Box(didRender))
failedRendering = false
do {
- try template.render(Box({ (info: RenderingInfo) -> Rendering in
- throw CustomError.Error
+ _ = try template.render(Box({ (info: RenderingInfo) -> Rendering in
+ throw CustomError.error
}))
XCTAssert(false)
- } catch CustomError.Error {
+ } catch CustomError.error {
XCTAssert(true)
} catch {
XCTAssert(false)
@@ -345,7 +345,7 @@ class HookFunctionTests: XCTestCase {
"observer3": MustacheBox(willRender: willRender3, didRender: didRender3),
"observed": Box("observed")
])
- try! template.render(box)
+ _ = try! template.render(box)
XCTAssertEqual(willRenderIndex1, 2)
XCTAssertEqual(willRenderIndex2, 1)
@@ -371,7 +371,7 @@ class HookFunctionTests: XCTestCase {
let template = try! Template(string: "{{#items}}{{.}}{{/items}}")
let box = Box(["items": Box([Box(willRender1), Box(willRender2)])])
- try! template.render(box)
+ _ = try! template.render(box)
XCTAssertTrue(willRenderCalled1)
XCTAssertTrue(willRenderCalled2)
@@ -380,8 +380,8 @@ class HookFunctionTests: XCTestCase {
func testWillRenderFunctionCanProcessRenderFunction() {
let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
return Box({ (info) -> Rendering in
- let rendering = try box.render(info: info)
- return Rendering(rendering.string.uppercaseString, rendering.contentType)
+ let rendering = try box.render(info)
+ return Rendering(rendering.string.uppercased(), rendering.contentType)
})
}
@@ -394,7 +394,7 @@ class HookFunctionTests: XCTestCase {
XCTAssertEqual(rendering, "&YOU")
render = { (info: RenderingInfo) -> Rendering in
- return Rendering("&you", .HTML)
+ return Rendering("&you", .html)
}
box = Box(["object": Box(render), "observer": Box(willRender)])
template = try! Template(string: "{{# observer }}{{ object }}{{/ }}")
diff --git a/Tests/Public/LambdaTests.swift b/Tests/Public/LambdaTests.swift
index 1b7927c6..0d0a84b5 100644
--- a/Tests/Public/LambdaTests.swift
+++ b/Tests/Public/LambdaTests.swift
@@ -167,10 +167,10 @@ class LambdaTests: XCTestCase {
"lambda": Box(lambda),
]
do {
- try template.render(Box(data))
+ _ = try template.render(Box(data))
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
@@ -186,10 +186,10 @@ class LambdaTests: XCTestCase {
"lambda": Box(lambda),
]
do {
- try template.render(Box(data))
+ _ = try template.render(Box(data))
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/ObjcKeyAccessTests.swift b/Tests/Public/ObjcKeyAccessTests.swift
index bd1341c5..b9b8fff6 100644
--- a/Tests/Public/ObjcKeyAccessTests.swift
+++ b/Tests/Public/ObjcKeyAccessTests.swift
@@ -36,7 +36,7 @@ class ObjcKeyAccessTests: XCTestCase {
// test setup
XCTAssertEqual(object.property, "property")
- XCTAssertEqual((object.valueForKey("property") as! String), "property")
+ XCTAssertEqual((object.value(forKey: "property") as! String), "property")
// test context
let context = Context(Box(object))
@@ -48,7 +48,7 @@ class ObjcKeyAccessTests: XCTestCase {
// test setup
XCTAssertEqual(object.method(), "method")
- XCTAssertEqual((object.valueForKey("method") as! String), "method")
+ XCTAssertEqual((object.value(forKey: "method") as! String), "method")
// test context
let context = Context(Box(object))
diff --git a/Tests/Public/RenderFunctionTests.swift b/Tests/Public/RenderFunctionTests.swift
index 8dff579c..fed58cb4 100644
--- a/Tests/Public/RenderFunctionTests.swift
+++ b/Tests/Public/RenderFunctionTests.swift
@@ -26,8 +26,8 @@ import Mustache
class RenderFunctionTests: XCTestCase {
- enum CustomError : ErrorType {
- case Error
+ enum CustomError : Error {
+ case error
}
func testRenderFunctionInVariableTag() {
@@ -56,7 +56,7 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionHTMLRenderingOfEscapedVariableTag() {
let render = { (info: RenderingInfo) -> Rendering in
- return Rendering("&", .HTML)
+ return Rendering("&", .html)
}
let rendering = try! Template(string: "{{.}}").render(Box(render))
XCTAssertEqual(rendering, "&")
@@ -64,7 +64,7 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionHTMLRenderingOfUnescapedVariableTag() {
let render = { (info: RenderingInfo) -> Rendering in
- return Rendering("&", .HTML)
+ return Rendering("&", .html)
}
let rendering = try! Template(string: "{{{.}}}").render(Box(render))
XCTAssertEqual(rendering, "&")
@@ -88,7 +88,7 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionHTMLRenderingOfSectionTag() {
let render = { (info: RenderingInfo) -> Rendering in
- return Rendering("&", .HTML)
+ return Rendering("&", .html)
}
let rendering = try! Template(string: "{{#.}}{{/.}}").render(Box(render))
XCTAssertEqual(rendering, "&")
@@ -108,7 +108,7 @@ class RenderFunctionTests: XCTestCase {
throw NSError(domain: errorDomain, code: 0, userInfo: nil)
}
do {
- try Template(string: "{{.}}").render(Box(render))
+ _ = try Template(string: "{{.}}").render(Box(render))
XCTAssert(false)
} catch let error as NSError {
XCTAssertEqual(error.domain, errorDomain)
@@ -117,12 +117,12 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionCanThrowCustomErrorFromVariableTag() {
let render = { (info: RenderingInfo) -> Rendering in
- throw CustomError.Error
+ throw CustomError.error
}
do {
- try Template(string: "\n\n{{.}}").render(Box(render))
+ _ = try Template(string: "\n\n{{.}}").render(Box(render))
XCTAssert(false)
- } catch CustomError.Error {
+ } catch CustomError.error {
XCTAssert(true)
} catch {
XCTAssert(false)
@@ -135,7 +135,7 @@ class RenderFunctionTests: XCTestCase {
throw NSError(domain: errorDomain, code: 0, userInfo: nil)
}
do {
- try Template(string: "{{#.}}{{/.}}").render(Box(render))
+ _ = try Template(string: "{{#.}}{{/.}}").render(Box(render))
XCTAssert(false)
} catch let error as NSError {
XCTAssertEqual(error.domain, errorDomain)
@@ -144,12 +144,12 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionCanThrowCustomErrorFromSectionTag() {
let render = { (info: RenderingInfo) -> Rendering in
- throw CustomError.Error
+ throw CustomError.error
}
do {
- try Template(string: "\n\n{{#.}}\n\n{{/.}}").render(Box(render))
+ _ = try Template(string: "\n\n{{#.}}\n\n{{/.}}").render(Box(render))
XCTAssert(false)
- } catch CustomError.Error {
+ } catch CustomError.error {
XCTAssert(true)
} catch {
XCTAssert(false)
@@ -160,14 +160,14 @@ class RenderFunctionTests: XCTestCase {
var variableTagDetections = 0
let render = { (info: RenderingInfo) -> Rendering in
switch info.tag.type {
- case .Variable:
+ case .variable:
variableTagDetections += 1
default:
break
}
return Rendering("")
}
- try! Template(string: "{{.}}").render(Box(render))
+ _ = try! Template(string: "{{.}}").render(Box(render))
XCTAssertEqual(variableTagDetections, 1)
}
@@ -175,14 +175,14 @@ class RenderFunctionTests: XCTestCase {
var sectionTagDetections = 0
let render = { (info: RenderingInfo) -> Rendering in
switch info.tag.type {
- case .Section:
+ case .section:
sectionTagDetections += 1
default:
break
}
return Rendering("")
}
- try! Template(string: "{{#.}}{{/.}}").render(Box(render))
+ _ = try! Template(string: "{{#.}}{{/.}}").render(Box(render))
XCTAssertEqual(sectionTagDetections, 1)
}
@@ -192,7 +192,7 @@ class RenderFunctionTests: XCTestCase {
innerTemplateString = info.tag.innerTemplateString
return Rendering("")
}
- try! Template(string: "{{#.}}{{subject}}{{/.}}").render(Box(render))
+ _ = try! Template(string: "{{#.}}{{subject}}{{/.}}").render(Box(render))
XCTAssertEqual(innerTemplateString!, "{{subject}}")
}
@@ -202,7 +202,7 @@ class RenderFunctionTests: XCTestCase {
innerTemplateString = info.tag.innerTemplateString
return Rendering("")
}
- try! Template(string: "{{.}}").render(Box(render))
+ _ = try! Template(string: "{{.}}").render(Box(render))
XCTAssertEqual(innerTemplateString!, "")
}
@@ -214,9 +214,9 @@ class RenderFunctionTests: XCTestCase {
}
let box = Box(["render": Box(render), "subject": Box("-")])
- try! Template(string: "{{#render}}{{subject}}={{subject}}{{/render}}").render(box)
+ _ = try! Template(string: "{{#render}}{{subject}}={{subject}}{{/render}}").render(box)
XCTAssertEqual(tagRendering!.string, "-=-")
- XCTAssertEqual(tagRendering!.contentType, ContentType.HTML)
+ XCTAssertEqual(tagRendering!.contentType, ContentType.html)
}
func testRenderFunctionCanAccessRenderedContentFromEscapedVariableTag() {
@@ -226,9 +226,9 @@ class RenderFunctionTests: XCTestCase {
return tagRendering!
}
- try! Template(string: "{{.}}").render(Box(render))
+ _ = try! Template(string: "{{.}}").render(Box(render))
XCTAssertEqual(tagRendering!.string, "")
- XCTAssertEqual(tagRendering!.contentType, ContentType.HTML)
+ XCTAssertEqual(tagRendering!.contentType, ContentType.html)
}
func testRenderFunctionCanAccessRenderedContentFromUnescapedVariableTag() {
@@ -238,10 +238,10 @@ class RenderFunctionTests: XCTestCase {
return tagRendering!
}
- try! Template(string: "{{{.}}}").render(Box(render))
+ _ = try! Template(string: "{{{.}}}").render(Box(render))
XCTAssertEqual(tagRendering!.string, "")
- XCTAssertEqual(tagRendering!.contentType, ContentType.HTML)
+ XCTAssertEqual(tagRendering!.contentType, ContentType.html)
}
func testRenderFunctionCanRenderCurrentContextInAnotherTemplateFromVariableTag() {
@@ -341,7 +341,7 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionTriggersWillRenderFunctions() {
let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
switch tag.type {
- case .Section:
+ case .section:
return box
default:
return Box("delegate")
@@ -362,7 +362,7 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionTriggersWillRenderFunctionsInAnotherTemplateFromVariableTag() {
let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
switch tag.type {
- case .Section:
+ case .section:
return box
default:
return Box("delegate")
@@ -384,7 +384,7 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionTriggersWillRenderFunctionsInAnotherTemplateFromSectionTag() {
let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
switch tag.type {
- case .Section:
+ case .section:
return box
default:
return Box("delegate")
@@ -429,10 +429,10 @@ class RenderFunctionTests: XCTestCase {
func testArrayOfHTMLRenderFunctionsInEscapedVariableTag() {
let render1 = { (info: RenderingInfo) -> Rendering in
- return Rendering("<1>", .HTML)
+ return Rendering("<1>", .html)
}
let render2 = { (info: RenderingInfo) -> Rendering in
- return Rendering("<2>", .HTML)
+ return Rendering("<2>", .html)
}
let box = Box(["items": Box([Box(render1), Box(render2)])])
let rendering = try! Template(string: "{{items}}").render(box)
@@ -441,10 +441,10 @@ class RenderFunctionTests: XCTestCase {
func testArrayOfHTMLRenderFunctionsInUnescapedVariableTag() {
let render1 = { (info: RenderingInfo) -> Rendering in
- return Rendering("<1>", .HTML)
+ return Rendering("<1>", .html)
}
let render2 = { (info: RenderingInfo) -> Rendering in
- return Rendering("<2>", .HTML)
+ return Rendering("<2>", .html)
}
let box = Box(["items": Box([Box(render1), Box(render2)])])
let rendering = try! Template(string: "{{{items}}}").render(box)
@@ -480,14 +480,14 @@ class RenderFunctionTests: XCTestCase {
return Rendering("<1>")
}
let render2 = { (info: RenderingInfo) -> Rendering in
- return Rendering("<2>", .HTML)
+ return Rendering("<2>", .html)
}
let box = Box(["items": Box([Box(render1), Box(render2)])])
do {
- try Template(string: "{{items}}").render(box)
+ _ = try Template(string: "{{items}}").render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
@@ -498,14 +498,14 @@ class RenderFunctionTests: XCTestCase {
return Rendering("<1>")
}
let render2 = { (info: RenderingInfo) -> Rendering in
- return Rendering("<2>", .HTML)
+ return Rendering("<2>", .html)
}
let box = Box(["items": Box([Box(render1), Box(render2)])])
do {
- try Template(string: "{{#items}}{{/items}}").render(box)
+ _ = try Template(string: "{{#items}}{{/items}}").render(box)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.RenderError)
+ XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/ServicesTests/EachFilterTests.swift b/Tests/Public/ServicesTests/EachFilterTests.swift
index b856dbc2..c7b1c282 100644
--- a/Tests/Public/ServicesTests/EachFilterTests.swift
+++ b/Tests/Public/ServicesTests/EachFilterTests.swift
@@ -31,7 +31,7 @@ class EachFilterTests: XCTestCase {
let template = try! Template(string: "{{#each(set)}}({{@index}},{{.}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
let rendering = try! template.render(Box(["set": set]))
- XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].indexOf(rendering) != nil)
+ XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].index(of: rendering) != nil)
}
func testEachFilterEnumeratesNSSet() {
@@ -39,7 +39,7 @@ class EachFilterTests: XCTestCase {
let template = try! Template(string: "{{#each(set)}}({{@index}},{{.}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
let rendering = try! template.render(Box(["set": set]))
- XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].indexOf(rendering) != nil)
+ XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].index(of: rendering) != nil)
}
func testEachFilterTriggersRenderFunctionsInArray() {
diff --git a/Tests/Public/ServicesTests/LocalizerTests.swift b/Tests/Public/ServicesTests/LocalizerTests.swift
index 3757790b..86700cc2 100644
--- a/Tests/Public/ServicesTests/LocalizerTests.swift
+++ b/Tests/Public/ServicesTests/LocalizerTests.swift
@@ -26,11 +26,11 @@ import Mustache
class LocalizerTests: XCTestCase {
- lazy var localizableBundle: NSBundle = NSBundle(path: NSBundle(forClass: self.dynamicType).pathForResource("LocalizerTestsBundle", ofType: nil)!)!
+ lazy var localizableBundle: Bundle = Bundle(path: Bundle(for: type(of: self)).path(forResource: "LocalizerTestsBundle", ofType: nil)!)!
lazy var localizer: StandardLibrary.Localizer = StandardLibrary.Localizer(bundle: self.localizableBundle, table: nil)
func testLocalizableBundle() {
- let testable = localizableBundle.localizedStringForKey("testable?", value:"", table:nil)
+ let testable = localizableBundle.localizedString(forKey: "testable?", value:"", table:nil)
XCTAssertEqual(testable, "YES")
}
@@ -53,13 +53,13 @@ class LocalizerTests: XCTestCase {
var template = try! Template(string: "{{#localize}}%d{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
var rendering = try! template.render()
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("%d", value: nil, table: nil), "ha ha percent d %d")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%d", value: nil, table: nil), "ha ha percent d %d")
XCTAssertEqual(rendering, "ha ha percent d %d")
template = try! Template(string: "{{#localize}}%@{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
rendering = try! template.render()
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("%@", value: nil, table: nil), "ha ha percent @ %@")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%@", value: nil, table: nil), "ha ha percent @ %@")
XCTAssertEqual(rendering, "ha ha percent @ %@")
}
@@ -67,13 +67,13 @@ class LocalizerTests: XCTestCase {
var template = try! Template(string: "{{#localize}}%d {{foo}}{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
var rendering = try! template.render(Box(["foo": "bar"]))
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("%%d %@", value: nil, table: nil), "ha ha percent d %%d %@")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%%d %@", value: nil, table: nil), "ha ha percent d %%d %@")
XCTAssertEqual(rendering, "ha ha percent d %d bar")
template = try! Template(string: "{{#localize}}%@ {{foo}}{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
rendering = try! template.render(Box(["foo": "bar"]))
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("%%@ %@", value: nil, table: nil), "ha ha percent @ %%@ %@")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%%@ %@", value: nil, table: nil), "ha ha percent @ %%@ %@")
XCTAssertEqual(rendering, "ha ha percent @ %@ bar")
}
@@ -81,7 +81,7 @@ class LocalizerTests: XCTestCase {
let template = try! Template(string: "{{localize(foo)}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
let rendering = try! template.render(Box(["foo": "bar"]))
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("bar", value: nil, table: nil), "translated_bar")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "bar", value: nil, table: nil), "translated_bar")
XCTAssertEqual(rendering, "translated_bar")
}
@@ -89,7 +89,7 @@ class LocalizerTests: XCTestCase {
let template = try! Template(string: "{{#localize}}bar{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
let rendering = try! template.render()
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("bar", value: nil, table: nil), "translated_bar")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "bar", value: nil, table: nil), "translated_bar")
XCTAssertEqual(rendering, "translated_bar")
}
@@ -97,7 +97,7 @@ class LocalizerTests: XCTestCase {
let template = try! Template(string: "{{#localize}}..{{foo}}..{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
let rendering = try! template.render(Box(["foo": "bar"]))
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey("..%@..", value: nil, table: nil), "!!%@!!")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "..%@..", value: nil, table: nil), "!!%@!!")
XCTAssertEqual(rendering, "!!bar!!")
}
@@ -105,7 +105,7 @@ class LocalizerTests: XCTestCase {
let template = try! Template(string: "{{#localize}}.{{foo}}.{{^false}}{{baz}}{{/}}.{{/}}")
template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
let rendering = try! template.render(Box(["foo": "bar", "baz": "truc"]))
- XCTAssertEqual(self.localizer.bundle.localizedStringForKey(".%@.%@.", value: nil, table: nil), "!%@!%@!")
+ XCTAssertEqual(self.localizer.bundle.localizedString(forKey: ".%@.%@.", value: nil, table: nil), "!%@!%@!")
XCTAssertEqual(rendering, "!bar!truc!")
}
diff --git a/Tests/Public/ServicesTests/NSFormatterTests.swift b/Tests/Public/ServicesTests/NSFormatterTests.swift
index 134b285e..9b53e5b8 100644
--- a/Tests/Public/ServicesTests/NSFormatterTests.swift
+++ b/Tests/Public/ServicesTests/NSFormatterTests.swift
@@ -27,12 +27,12 @@ import Mustache
class NSFormatterTests: XCTestCase {
func testFormatterIsAFilterForProcessableValues() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
// test that number is processable
- XCTAssertEqual(percentFormatter.stringFromNumber(0.5)!, "50%")
+ XCTAssertEqual(percentFormatter.string(from: 0.5)!, "50%")
// test filtering a number
let template = try! Template(string: "{{ percent(number) }}")
@@ -42,12 +42,12 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterIsAFilterForUnprocessableValues() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
// test that number is processable
- XCTAssertTrue(percentFormatter.stringForObjectValue("foo") == nil)
+ XCTAssertTrue(percentFormatter.string(for: "foo") == nil)
// test filtering a string
let template = try! Template(string: "{{ percent(string) }}")
@@ -57,9 +57,9 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterSectionFormatsInnerVariableTags() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{ number }} {{ number }}{{/ percent }}")
let box = Box(["number": Box(0.5), "percent": Box(percentFormatter)])
@@ -68,9 +68,9 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterSectionDoesNotFormatUnprocessableInnerVariableTags() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{ value }}{{/ percent }}")
let box = Box(["value": Box("foo"), "percent": Box(percentFormatter)])
@@ -79,9 +79,9 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterAsSectionFormatsDeepInnerVariableTags() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{# number }}Number is {{ number }}.{{/ number }}{{/ percent }}")
let box = Box(["number": Box(0.5), "percent": Box(percentFormatter)])
@@ -90,9 +90,9 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterAsSectionDoesNotFormatInnerSectionTags() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "NO is {{ NO }}. {{^ NO }}NO is false.{{/ NO }} percent(NO) is {{ percent(NO) }}. {{# percent(NO) }}percent(NO) is true.{{/ percent(NO) }} {{# percent }}{{^ NO }}NO is now {{ NO }} and is still false.{{/ NO }}{{/ percent }}")
let box = Box(["number": Box(0.5), "NO": Box(0), "percent": Box(percentFormatter)])
@@ -101,7 +101,7 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterIsTruthy() {
- let formatter = NSFormatter()
+ let formatter = Formatter()
let template = try! Template(string: "{{# formatter }}Formatter is true.{{/ formatter }}{{^ formatter }}Formatter is false.{{/ formatter }}")
let box = Box(["formatter": Box(formatter)])
let rendering = try! template.render(box)
@@ -109,7 +109,7 @@ class NSFormatterTests: XCTestCase {
}
func testFormatterRendersSelfAsSomething() {
- let formatter = NSFormatter()
+ let formatter = Formatter()
let template = try! Template(string: "{{ formatter }}")
let box = Box(["formatter": Box(formatter)])
let rendering = try! template.render(box)
@@ -120,9 +120,9 @@ class NSFormatterTests: XCTestCase {
// Check that NSNumberFormatter does not have surprising behavior, and
// does not format nil.
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let box = Box(["format": Box(percentFormatter)])
@@ -136,9 +136,9 @@ class NSFormatterTests: XCTestCase {
}
func testNumberFormatterRendersNothingForNSNull() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let box = Box(["format": Box(percentFormatter), "value": Box(NSNull())])
@@ -152,9 +152,9 @@ class NSFormatterTests: XCTestCase {
}
func testNumberFormatterRendersNothingForNSString() {
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
var box = Box(["format": Box(percentFormatter), "value": Box("1")])
@@ -191,11 +191,11 @@ class NSFormatterTests: XCTestCase {
// Check that NSNumberFormatter does not have surprising behavior, and
// does not format NSDate.
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- percentFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": Box(percentFormatter), "value": Box(NSDate())])
+ let box = Box(["format": Box(percentFormatter), "value": Box(Date())])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -210,8 +210,8 @@ class NSFormatterTests: XCTestCase {
// Check that NSDateFormatter does not have surprising behavior, and
// does not format nil.
- let dateFormatter = NSDateFormatter()
- dateFormatter.dateStyle = .FullStyle
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .full
let box = Box(["format": Box(dateFormatter)])
@@ -225,8 +225,8 @@ class NSFormatterTests: XCTestCase {
}
func testDateFormatterRendersNothingForNSNull() {
- let dateFormatter = NSDateFormatter()
- dateFormatter.dateStyle = .FullStyle
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .full
let box = Box(["format": Box(dateFormatter), "value": Box(NSNull())])
@@ -240,8 +240,8 @@ class NSFormatterTests: XCTestCase {
}
func testDateFormatterRendersNothingForNSString() {
- let dateFormatter = NSDateFormatter()
- dateFormatter.dateStyle = .FullStyle
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .full
var box = Box(["format": Box(dateFormatter), "value": Box("1")])
@@ -278,8 +278,8 @@ class NSFormatterTests: XCTestCase {
// Check that NSDateFormatter does not have surprising behavior, and
// does not format NSNumber.
- let dateFormatter = NSDateFormatter()
- dateFormatter.dateStyle = .FullStyle
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .full
let box = Box(["format": Box(dateFormatter), "value": Box(0)])
diff --git a/Tests/Public/ServicesTests/StandardLibraryTests.swift b/Tests/Public/ServicesTests/StandardLibraryTests.swift
index a764d193..28cb7b3a 100644
--- a/Tests/Public/ServicesTests/StandardLibraryTests.swift
+++ b/Tests/Public/ServicesTests/StandardLibraryTests.swift
@@ -44,7 +44,7 @@ class StandardLibraryTests: XCTestCase {
func testStandardLibraryHTMLEscapeDoesEscapeHTML() {
let render = Box({ (info: RenderingInfo) -> Rendering in
- return Rendering("
", .HTML)
+ return Rendering("
", .html)
})
var template = try! Template(string: "{{# HTMLEscape }}{{ object }}{{/ }}")
diff --git a/Tests/Public/SuitesTests/SuiteTestCase.swift b/Tests/Public/SuitesTests/SuiteTestCase.swift
index b5b6f743..5b22bc60 100644
--- a/Tests/Public/SuitesTests/SuiteTestCase.swift
+++ b/Tests/Public/SuitesTests/SuiteTestCase.swift
@@ -26,21 +26,21 @@ import Mustache
class SuiteTestCase: XCTestCase {
- func runTestsFromResource(name: String, directory: String) {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let path: String! = testBundle.pathForResource(name, ofType: nil, inDirectory: directory)
+ func runTestsFromResource(_ name: String, directory: String) {
+ let testBundle = Bundle(for: type(of: self))
+ let path: String! = testBundle.path(forResource: name, ofType: nil, inDirectory: directory)
if path == nil {
XCTFail("No such test suite \(directory)/\(name)")
return
}
- let data: NSData! = NSData(contentsOfFile:path)
+ let data: Data! = try? Data(contentsOf: URL(fileURLWithPath: path))
if data == nil {
XCTFail("No test suite in \(path)")
return
}
- let testSuite = try! NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions(rawValue: 0)) as! NSDictionary
+ let testSuite = try! JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions(rawValue: 0)) as! NSDictionary
let tests = testSuite["tests"] as! NSArray!
if tests == nil {
@@ -48,7 +48,7 @@ class SuiteTestCase: XCTestCase {
return
}
- for testDictionary in tests {
+ for testDictionary in tests! {
let test = Test(path: path, dictionary: testDictionary as! NSDictionary)
test.run()
}
@@ -78,7 +78,7 @@ class SuiteTestCase: XCTestCase {
// Support for filters.json
template.registerInBaseContext("capitalized", Box(Filter({ (string: String?) -> MustacheBox in
- return Box(string?.capitalizedString)
+ return Box(string?.capitalized)
})))
testRendering(template)
@@ -103,12 +103,12 @@ class SuiteTestCase: XCTestCase {
let templateExtension = (templateName as NSString).pathExtension
for (directoryPath, encoding) in pathsAndEncodingsToPartials(partialsDictionary) {
do {
- let template = try TemplateRepository(directoryPath: directoryPath, templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).stringByDeletingPathExtension)
+ let template = try TemplateRepository(directoryPath: directoryPath, templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).deletingPathExtension)
templates.append(template)
} catch {
testError(error, replayOnFailure: {
do {
- try TemplateRepository(directoryPath: directoryPath, templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).stringByDeletingPathExtension)
+ _ = try TemplateRepository(directoryPath: directoryPath, templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).deletingPathExtension)
} catch {
// ignore error on replay
}
@@ -116,12 +116,12 @@ class SuiteTestCase: XCTestCase {
}
do {
- let template = try TemplateRepository(baseURL: NSURL.fileURLWithPath(directoryPath), templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).stringByDeletingPathExtension)
+ let template = try TemplateRepository(baseURL: URL(fileURLWithPath: directoryPath), templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).deletingPathExtension)
templates.append(template)
} catch {
testError(error, replayOnFailure: {
do {
- try TemplateRepository(baseURL: NSURL.fileURLWithPath(directoryPath), templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).stringByDeletingPathExtension)
+ _ = try TemplateRepository(baseURL: URL(fileURLWithPath: directoryPath), templateExtension: templateExtension, encoding: encoding).template(named: (templateName as NSString).deletingPathExtension)
} catch {
// ignore error on replay
}
@@ -138,7 +138,7 @@ class SuiteTestCase: XCTestCase {
} catch {
testError(error, replayOnFailure: {
do {
- try TemplateRepository(directoryPath: directoryPath, templateExtension: "", encoding: encoding).template(string: templateString)
+ _ = try TemplateRepository(directoryPath: directoryPath, templateExtension: "", encoding: encoding).template(string: templateString)
} catch {
// ignore error on replay
}
@@ -146,12 +146,12 @@ class SuiteTestCase: XCTestCase {
}
do {
- let template = try TemplateRepository(baseURL: NSURL.fileURLWithPath(directoryPath), templateExtension: "", encoding: encoding).template(string: templateString)
+ let template = try TemplateRepository(baseURL: URL(fileURLWithPath: directoryPath), templateExtension: "", encoding: encoding).template(string: templateString)
templates.append(template)
} catch {
testError(error, replayOnFailure: {
do {
- try TemplateRepository(baseURL: NSURL.fileURLWithPath(directoryPath), templateExtension: "", encoding: encoding).template(string: templateString)
+ _ = try TemplateRepository(baseURL: URL(fileURLWithPath: directoryPath), templateExtension: "", encoding: encoding).template(string: templateString)
} catch {
// ignore error on replay
}
@@ -175,7 +175,7 @@ class SuiteTestCase: XCTestCase {
} catch {
testError(error, replayOnFailure: {
do {
- try TemplateRepository().template(string: templateString)
+ _ = try TemplateRepository().template(string: templateString)
} catch {
// ignore error on replay
}
@@ -189,7 +189,7 @@ class SuiteTestCase: XCTestCase {
}
}
- func testRendering(template: Template) {
+ func testRendering(_ template: Template) {
do {
let rendering = try template.render(renderedValue)
if let expectedRendering = expectedRendering as String! {
@@ -199,7 +199,7 @@ class SuiteTestCase: XCTestCase {
}
testSuccess(replayOnFailure: {
do {
- try template.render(self.renderedValue)
+ _ = try template.render(self.renderedValue)
} catch {
// ignore error on replay
}
@@ -207,7 +207,7 @@ class SuiteTestCase: XCTestCase {
} catch {
testError(error, replayOnFailure: {
do {
- try template.render(self.renderedValue)
+ _ = try template.render(self.renderedValue)
} catch {
// ignore error on replay
}
@@ -215,12 +215,12 @@ class SuiteTestCase: XCTestCase {
}
}
- func testError(error: ErrorType, replayOnFailure replayBlock: ()->()) {
+ func testError(_ error: Error, replayOnFailure replayBlock: ()->()) {
if let expectedError = expectedError {
do {
- let reg = try NSRegularExpression(pattern: expectedError, options: NSRegularExpressionOptions(rawValue: 0))
+ let reg = try NSRegularExpression(pattern: expectedError, options: NSRegularExpression.Options(rawValue: 0))
let errorMessage = "\(error)"
- let matches = reg.matchesInString(errorMessage, options: NSMatchingOptions(rawValue: 0), range:NSMakeRange(0, (errorMessage as NSString).length))
+ let matches = reg.matches(in: errorMessage, options: NSRegularExpression.MatchingOptions(rawValue: 0), range:NSMakeRange(0, (errorMessage as NSString).length))
if matches.count == 0 {
XCTFail("`\(errorMessage)` does not match /\(expectedError)/ in \(description)")
replayBlock()
@@ -242,21 +242,21 @@ class SuiteTestCase: XCTestCase {
}
}
- func pathsAndEncodingsToPartials(partialsDictionary: [String: String]) -> [(String, NSStringEncoding)] {
- var templatesPaths: [(String, NSStringEncoding)] = []
+ func pathsAndEncodingsToPartials(_ partialsDictionary: [String: String]) -> [(String, String.Encoding)] {
+ var templatesPaths: [(String, String.Encoding)] = []
- let fm = NSFileManager.defaultManager()
- let encodings: [NSStringEncoding] = [NSUTF8StringEncoding, NSUTF16StringEncoding]
+ let fm = FileManager.default
+ let encodings: [String.Encoding] = [String.Encoding.utf8, String.Encoding.utf16]
for encoding in encodings {
- let templatesPath = ((NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("GRMustacheTest") as NSString).stringByAppendingPathComponent("encoding_\(encoding)")
- if fm.fileExistsAtPath(templatesPath) {
- try! fm.removeItemAtPath(templatesPath)
+ let templatesPath = ((NSTemporaryDirectory() as NSString).appendingPathComponent("GRMustacheTest") as NSString).appendingPathComponent("encoding_\(encoding)")
+ if fm.fileExists(atPath: templatesPath) {
+ try! fm.removeItem(atPath: templatesPath)
}
for (partialName, partialString) in partialsDictionary {
- let partialPath = (templatesPath as NSString).stringByAppendingPathComponent(partialName)
+ let partialPath = (templatesPath as NSString).appendingPathComponent(partialName)
do {
- try fm.createDirectoryAtPath((partialPath as NSString).stringByDeletingLastPathComponent, withIntermediateDirectories: true, attributes: nil)
- if !fm.createFileAtPath(partialPath, contents: partialString.dataUsingEncoding(encoding, allowLossyConversion: false), attributes: nil) {
+ try fm.createDirectory(atPath: (partialPath as NSString).deletingLastPathComponent, withIntermediateDirectories: true, attributes: nil)
+ if !fm.createFile(atPath: partialPath, contents: partialString.data(using: encoding, allowLossyConversion: false), attributes: nil) {
XCTFail("Could not save template in \(description)")
return []
}
diff --git a/Tests/Public/TagTests/TagTests.swift b/Tests/Public/TagTests/TagTests.swift
index 145e488d..355bc024 100644
--- a/Tests/Public/TagTests/TagTests.swift
+++ b/Tests/Public/TagTests/TagTests.swift
@@ -36,22 +36,22 @@ class TagTests: XCTestCase {
tagDescription = nil
var template = try! Template(string: "{{name}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString("{{name}}")
+ _ = try! template.render()
+ var range = tagDescription?.range(of: "{{name}}")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "{{#name}}{{/name}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString("{{#name}}")
+ _ = try! template.render()
+ range = tagDescription?.range(of: "{{#name}}")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "{{ name\t}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString("{{ name\t}}")
+ _ = try! template.render()
+ range = tagDescription?.range(of: "{{ name\t}}")
XCTAssertTrue(range != nil)
}
@@ -65,22 +65,22 @@ class TagTests: XCTestCase {
tagDescription = nil
var template = try! Template(string: "{{name}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString("line 1")
+ _ = try! template.render()
+ var range = tagDescription?.range(of: "line 1")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "\n {{\nname}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString("line 2")
+ _ = try! template.render()
+ range = tagDescription?.range(of: "line 2")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "\n\n {{#\nname}}\n\n{{/name}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString("line 3")
+ _ = try! template.render()
+ range = tagDescription?.range(of: "line 3")
XCTAssertTrue(range != nil)
}
@@ -92,19 +92,19 @@ class TagTests: XCTestCase {
}
tagDescription = nil
- let bundle = NSBundle(forClass: self.dynamicType)
+ let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(bundle: bundle)
var template = try! templateRepository.template(named: "TagTests")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(named: "TagTests", bundle: bundle)
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
}
@@ -116,19 +116,19 @@ class TagTests: XCTestCase {
}
tagDescription = nil
- let bundle = NSBundle(forClass: self.dynamicType)
+ let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(baseURL: bundle.resourceURL!)
var template = try! templateRepository.template(named: "TagTests")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
- template = try! Template(URL: bundle.URLForResource("TagTests", withExtension: "mustache")!)
+ template = try! Template(URL: bundle.url(forResource: "TagTests", withExtension: "mustache")!)
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
}
@@ -140,19 +140,19 @@ class TagTests: XCTestCase {
}
tagDescription = nil
- let bundle = NSBundle(forClass: self.dynamicType)
+ let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(directoryPath: bundle.resourcePath!)
var template = try! templateRepository.template(named: "TagTests")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
- template = try! Template(path: bundle.pathForResource("TagTests", ofType: "mustache")!)
+ template = try! Template(path: bundle.path(forResource: "TagTests", ofType: "mustache")!)
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
}
@@ -164,26 +164,26 @@ class TagTests: XCTestCase {
}
tagDescription = nil
- let bundle = NSBundle(forClass: self.dynamicType)
+ let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(bundle: bundle)
var template = try! templateRepository.template(named: "TagTests_wrapper")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! templateRepository.template(string: "{{> TagTests }}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(named: "TagTests_wrapper", bundle: bundle)
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
}
@@ -195,26 +195,26 @@ class TagTests: XCTestCase {
}
tagDescription = nil
- let bundle = NSBundle(forClass: self.dynamicType)
+ let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(baseURL: bundle.resourceURL!)
var template = try! templateRepository.template(named: "TagTests_wrapper")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! templateRepository.template(string: "{{> TagTests }}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
- template = try! Template(URL: bundle.URLForResource("TagTests_wrapper", withExtension: "mustache")!)
+ template = try! Template(URL: bundle.url(forResource: "TagTests_wrapper", withExtension: "mustache")!)
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
}
@@ -226,26 +226,26 @@ class TagTests: XCTestCase {
}
tagDescription = nil
- let bundle = NSBundle(forClass: self.dynamicType)
+ let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(directoryPath: bundle.resourcePath!)
var template = try! templateRepository.template(named: "TagTests_wrapper")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- var range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! templateRepository.template(string: "{{> TagTests }}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
- template = try! Template(path: bundle.pathForResource("TagTests_wrapper", ofType: "mustache")!)
+ template = try! Template(path: bundle.path(forResource: "TagTests_wrapper", ofType: "mustache")!)
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- try! template.render()
- range = tagDescription?.rangeOfString(bundle.pathForResource("TagTests", ofType: "mustache")!)
+ _ = try! template.render()
+ range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
}
}
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryBundleTests/TemplateRepositoryBundleTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryBundleTests/TemplateRepositoryBundleTests.swift
index a280dbf9..6cf39188 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryBundleTests/TemplateRepositoryBundleTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryBundleTests/TemplateRepositoryBundleTests.swift
@@ -27,12 +27,12 @@ import Mustache
class TemplateRepositoryBundleTests: XCTestCase {
func testTemplateRepositoryWithBundle() {
- let repo = TemplateRepository(bundle: NSBundle(forClass: self.dynamicType))
+ let repo = TemplateRepository(bundle: Bundle(for: type(of: self)))
var template: Template
var rendering: String
do {
- try repo.template(named: "notFound")
+ _ = try repo.template(named: "notFound")
XCTAssert(false)
} catch {
}
@@ -51,12 +51,12 @@ class TemplateRepositoryBundleTests: XCTestCase {
}
func testTemplateRepositoryWithBundleTemplateExtensionEncoding() {
- var repo = TemplateRepository(bundle: NSBundle(forClass: self.dynamicType), templateExtension: "text", encoding: NSUTF8StringEncoding)
+ var repo = TemplateRepository(bundle: Bundle(for: type(of: self)), templateExtension: "text", encoding: String.Encoding.utf8)
var template: Template
var rendering: String
do {
- try repo.template(named: "notFound")
+ _ = try repo.template(named: "notFound")
XCTAssert(false)
} catch {
}
@@ -69,10 +69,10 @@ class TemplateRepositoryBundleTests: XCTestCase {
rendering = try! template.render()
XCTAssertEqual(rendering, "TemplateRepositoryBundleTests.text TemplateRepositoryBundleTests_partial.text")
- repo = TemplateRepository(bundle: NSBundle(forClass: self.dynamicType), templateExtension: "", encoding: NSUTF8StringEncoding)
+ repo = TemplateRepository(bundle: Bundle(for: type(of: self)), templateExtension: "", encoding: String.Encoding.utf8)
do {
- try repo.template(named: "notFound")
+ _ = try repo.template(named: "notFound")
XCTAssert(false)
} catch {
}
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDataSourceTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDataSourceTests.swift
index 8e73a6a5..6c4bebdc 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDataSourceTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDataSourceTests.swift
@@ -26,13 +26,13 @@ import Mustache
class TemplateRepositoryDataSourceTests: XCTestCase {
- enum CustomError : ErrorType {
- case Error
+ enum CustomError : Error {
+ case error
}
func testTemplateRepositoryDataSource() {
class TestedDataSource: TemplateRepositoryDataSource {
- func templateIDForName(name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
+ func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
switch name {
case "not_found":
return nil
@@ -40,16 +40,16 @@ class TemplateRepositoryDataSourceTests: XCTestCase {
return name
}
}
- func templateStringForTemplateID(templateID: TemplateID) throws -> String {
+ func templateStringForTemplateID(_ templateID: TemplateID) throws -> String {
switch templateID {
case "not_found":
fatalError("Unexpected")
case "CustomError":
- throw CustomError.Error
+ throw CustomError.error
case "CustomNSError":
throw NSError(domain: "CustomNSError", code: 0, userInfo: nil)
case "MustacheErrorCodeTemplateNotFound":
- throw MustacheError(kind: .TemplateNotFound, message: "Custom Not Found Error")
+ throw MustacheError(kind: .templateNotFound, message: "Custom Not Found Error")
default:
return templateID
}
@@ -69,35 +69,35 @@ class TemplateRepositoryDataSourceTests: XCTestCase {
XCTAssertEqual(rendering, "foo")
do {
- try repo.template(string: "{{>not_found}}")
+ _ = try repo.template(string: "{{>not_found}}")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
do {
- try repo.template(string: "{{>CustomNSError}}")
+ _ = try repo.template(string: "{{>CustomNSError}}")
XCTAssert(false)
} catch let error as NSError {
XCTAssertEqual(error.domain, "CustomNSError")
}
do {
- try repo.template(string: "{{>CustomError}}")
+ _ = try repo.template(string: "{{>CustomError}}")
XCTAssert(false)
- } catch CustomError.Error {
+ } catch CustomError.error {
XCTAssert(true)
} catch {
XCTAssert(false)
}
do {
- try repo.template(string: "{{>MustacheErrorCodeTemplateNotFound}}")
+ _ = try repo.template(string: "{{>MustacheErrorCodeTemplateNotFound}}")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDictionaryTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDictionaryTests.swift
index 598a3142..282e749e 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDictionaryTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryDictionaryTests.swift
@@ -36,19 +36,19 @@ class TemplateRepositoryDictionaryTests: XCTestCase {
var rendering: String
do {
- try repo.template(named: "not_found")
+ _ = try repo.template(named: "not_found")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
do {
- try repo.template(string: "{{>not_found}}")
+ _ = try repo.template(string: "{{>not_found}}")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryPathTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryPathTests.swift
index e9895198..d4a1f9ce 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryPathTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryPathTests.swift
@@ -27,14 +27,14 @@ import Mustache
class TemplateRepositoryPathTests: XCTestCase {
func testTemplateRepositoryWithURL() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
+ let testBundle = Bundle(for: type(of: self))
+ let directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
let repo = TemplateRepository(directoryPath: directoryPath)
var template: Template
var rendering: String
do {
- try repo.template(named: "notFound")
+ _ = try repo.template(named: "notFound")
XCTAssert(false)
} catch {
}
@@ -57,52 +57,52 @@ class TemplateRepositoryPathTests: XCTestCase {
}
func testTemplateRepositoryWithURLTemplateExtensionEncoding() {
- let testBundle = NSBundle(forClass: self.dynamicType)
+ let testBundle = Bundle(for: type(of: self))
var directoryPath: String
var repo: TemplateRepository
var template: Template
var rendering: String
- directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
- repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "mustache", encoding: NSUTF8StringEncoding)
+ directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
+ repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "mustache", encoding: String.Encoding.utf8)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.mustache\ndir/é1.mustache\ndir/dir/é1.mustache\ndir/dir/é2.mustache\n\n\ndir/é2.mustache\n\n\né2.mustache\n\n")
- directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
- repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "txt", encoding: NSUTF8StringEncoding)
+ directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
+ repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "txt", encoding: String.Encoding.utf8)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.txt\ndir/é1.txt\ndir/dir/é1.txt\ndir/dir/é2.txt\n\n\ndir/é2.txt\n\n\né2.txt\n\n")
- directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
- repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "", encoding: NSUTF8StringEncoding)
+ directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_UTF8", ofType: nil)!
+ repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "", encoding: String.Encoding.utf8)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1\ndir/é1\ndir/dir/é1\ndir/dir/é2\n\n\ndir/é2\n\n\né2\n\n")
- directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_ISOLatin1", ofType: nil)!
- repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "mustache", encoding: NSISOLatin1StringEncoding)
+ directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_ISOLatin1", ofType: nil)!
+ repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "mustache", encoding: String.Encoding.isoLatin1)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.mustache\ndir/é1.mustache\ndir/dir/é1.mustache\ndir/dir/é2.mustache\n\n\ndir/é2.mustache\n\n\né2.mustache\n\n")
- directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_ISOLatin1", ofType: nil)!
- repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "txt", encoding: NSISOLatin1StringEncoding)
+ directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_ISOLatin1", ofType: nil)!
+ repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "txt", encoding: String.Encoding.isoLatin1)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.txt\ndir/é1.txt\ndir/dir/é1.txt\ndir/dir/é2.txt\n\n\ndir/é2.txt\n\n\né2.txt\n\n")
- directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests_ISOLatin1", ofType: nil)!
- repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "", encoding: NSISOLatin1StringEncoding)
+ directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests_ISOLatin1", ofType: nil)!
+ repo = TemplateRepository(directoryPath: directoryPath, templateExtension: "", encoding: String.Encoding.isoLatin1)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1\ndir/é1\ndir/dir/é1\ndir/dir/é2\n\n\ndir/é2\n\n\né2\n\n")
}
func testAbsolutePartialName() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests", ofType: nil)!
+ let testBundle = Bundle(for: type(of: self))
+ let directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests", ofType: nil)!
let repo = TemplateRepository(directoryPath: directoryPath)
let template = try! repo.template(named: "base")
let rendering = try! template.render()
@@ -110,19 +110,19 @@ class TemplateRepositoryPathTests: XCTestCase {
}
func testPartialNameCanNotEscapeTemplateRepositoryRootDirectory() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let directoryPath = testBundle.pathForResource("TemplateRepositoryFileSystemTests", ofType: nil)!
- let repo = TemplateRepository(directoryPath: (directoryPath as NSString).stringByAppendingPathComponent("partials"))
+ let testBundle = Bundle(for: type(of: self))
+ let directoryPath = testBundle.path(forResource: "TemplateRepositoryFileSystemTests", ofType: nil)!
+ let repo = TemplateRepository(directoryPath: (directoryPath as NSString).appendingPathComponent("partials"))
let template = try! repo.template(named: "partial2")
let rendering = try! template.render()
XCTAssertEqual(rendering, "success")
do {
- try repo.template(named: "up")
+ _ = try repo.template(named: "up")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift
index 9a5aa434..479473fd 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift
@@ -27,8 +27,8 @@ import Mustache
class TemplateRepositoryURLTests: XCTestCase {
func testTemplateRepositoryWithURL() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
+ let testBundle = Bundle(for: type(of: self))
+ let URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
let repo = TemplateRepository(baseURL: URL)
var template: Template
var rendering: String
@@ -57,52 +57,52 @@ class TemplateRepositoryURLTests: XCTestCase {
}
func testTemplateRepositoryWithURLTemplateExtensionEncoding() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- var URL: NSURL
+ let testBundle = Bundle(for: type(of: self))
+ var URL: Foundation.URL
var repo: TemplateRepository
var template: Template
var rendering: String
- URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
- repo = TemplateRepository(baseURL: URL, templateExtension: "mustache", encoding: NSUTF8StringEncoding)
+ URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
+ repo = TemplateRepository(baseURL: URL, templateExtension: "mustache", encoding: String.Encoding.utf8)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.mustache\ndir/é1.mustache\ndir/dir/é1.mustache\ndir/dir/é2.mustache\n\n\ndir/é2.mustache\n\n\né2.mustache\n\n")
- URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
- repo = TemplateRepository(baseURL: URL, templateExtension: "txt", encoding: NSUTF8StringEncoding)
+ URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
+ repo = TemplateRepository(baseURL: URL, templateExtension: "txt", encoding: String.Encoding.utf8)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.txt\ndir/é1.txt\ndir/dir/é1.txt\ndir/dir/é2.txt\n\n\ndir/é2.txt\n\n\né2.txt\n\n")
- URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
- repo = TemplateRepository(baseURL: URL, templateExtension: "", encoding: NSUTF8StringEncoding)
+ URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_UTF8", withExtension: nil)!
+ repo = TemplateRepository(baseURL: URL, templateExtension: "", encoding: String.Encoding.utf8)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1\ndir/é1\ndir/dir/é1\ndir/dir/é2\n\n\ndir/é2\n\n\né2\n\n")
- URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_ISOLatin1", withExtension: nil)!
- repo = TemplateRepository(baseURL: URL, templateExtension: "mustache", encoding: NSISOLatin1StringEncoding)
+ URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_ISOLatin1", withExtension: nil)!
+ repo = TemplateRepository(baseURL: URL, templateExtension: "mustache", encoding: String.Encoding.isoLatin1)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.mustache\ndir/é1.mustache\ndir/dir/é1.mustache\ndir/dir/é2.mustache\n\n\ndir/é2.mustache\n\n\né2.mustache\n\n")
- URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_ISOLatin1", withExtension: nil)!
- repo = TemplateRepository(baseURL: URL, templateExtension: "txt", encoding: NSISOLatin1StringEncoding)
+ URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_ISOLatin1", withExtension: nil)!
+ repo = TemplateRepository(baseURL: URL, templateExtension: "txt", encoding: String.Encoding.isoLatin1)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1.txt\ndir/é1.txt\ndir/dir/é1.txt\ndir/dir/é2.txt\n\n\ndir/é2.txt\n\n\né2.txt\n\n")
- URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests_ISOLatin1", withExtension: nil)!
- repo = TemplateRepository(baseURL: URL, templateExtension: "", encoding: NSISOLatin1StringEncoding)
+ URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests_ISOLatin1", withExtension: nil)!
+ repo = TemplateRepository(baseURL: URL, templateExtension: "", encoding: String.Encoding.isoLatin1)
template = try! repo.template(named: "file1")
rendering = try! template.render()
XCTAssertEqual(rendering, "é1\ndir/é1\ndir/dir/é1\ndir/dir/é2\n\n\ndir/é2\n\n\né2\n\n")
}
func testAbsolutePartialName() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests", withExtension: nil)!
+ let testBundle = Bundle(for: type(of: self))
+ let URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests", withExtension: nil)!
let repo = TemplateRepository(baseURL: URL)
let template = try! repo.template(named: "base")
let rendering = try! template.render()
@@ -110,9 +110,9 @@ class TemplateRepositoryURLTests: XCTestCase {
}
func testPartialNameCanNotEscapeTemplateRepositoryRootURL() {
- let testBundle = NSBundle(forClass: self.dynamicType)
- let URL = testBundle.URLForResource("TemplateRepositoryFileSystemTests", withExtension: nil)!
- let repo = TemplateRepository(baseURL: URL.URLByAppendingPathComponent("partials")!)
+ let testBundle = Bundle(for: type(of: self))
+ let URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests", withExtension: nil)!
+ let repo = TemplateRepository(baseURL: URL.appendingPathComponent("partials")!)
let template = try! repo.template(named: "partial2")
let rendering = try! template.render()
@@ -122,7 +122,7 @@ class TemplateRepositoryURLTests: XCTestCase {
try repo.template(named: "up")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift
index 7a69a022..bc488397 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift
@@ -30,19 +30,19 @@ class TemplateRepositoryTests: XCTestCase {
let repo = TemplateRepository()
do {
- try repo.template(named:"partial")
+ _ = try repo.template(named:"partial")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
do {
- try repo.template(string:"{{>partial}}")
+ _ = try repo.template(string:"{{>partial}}")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.TemplateNotFound)
+ XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
} catch {
XCTFail("Expected MustacheError")
}
@@ -76,17 +76,17 @@ class TemplateRepositoryTests: XCTestCase {
init(templates: [String: String]) {
self.templates = templates
}
- func templateIDForName(name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
+ func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID? {
return name
}
- func templateStringForTemplateID(templateID: TemplateID) throws -> String {
+ func templateStringForTemplateID(_ templateID: TemplateID) throws -> String {
if let string = templates[templateID] {
return string
} else {
- throw MustacheError(kind: .TemplateNotFound)
+ throw MustacheError(kind: .templateNotFound)
}
}
- func setTemplateString(templateString: String, forKey key: String) {
+ func setTemplateString(_ templateString: String, forKey key: String) {
templates[key] = templateString
}
}
diff --git a/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift b/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
index 099610ac..adc3d728 100644
--- a/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
+++ b/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
@@ -26,7 +26,7 @@ import Mustache
class TemplateFromMethodsTests: XCTestCase {
- func makeKeyedSubscriptFunction(string: String) -> KeyedSubscriptFunction {
+ func makeKeyedSubscriptFunction(_ string: String) -> KeyedSubscriptFunction {
return { (key: String) -> MustacheBox in
if key == "string" {
return Box(string)
@@ -36,44 +36,44 @@ class TemplateFromMethodsTests: XCTestCase {
}
}
- var testBundle: NSBundle { return NSBundle(forClass: self.dynamicType) }
+ var testBundle: Bundle { return Bundle(for: type(of: self)) }
let templateName = "TemplateFromMethodsTests"
- var templateURL: NSURL { return testBundle.URLForResource(templateName, withExtension: "mustache")! }
- var templatePath: String { return templateURL.path! }
- var templateString: String { return try! String(contentsOfFile: templatePath, encoding: NSUTF8StringEncoding) }
+ var templateURL: URL { return testBundle.url(forResource: templateName, withExtension: "mustache")! }
+ var templatePath: String { return templateURL.path }
+ var templateString: String { return try! String(contentsOfFile: templatePath, encoding: String.Encoding.utf8) }
let parserErrorTemplateName = "TemplateFromMethodsTests_parserError"
- var parserErrorTemplateURL: NSURL { return testBundle.URLForResource(parserErrorTemplateName, withExtension: "mustache")! }
- var parserErrorTemplatePath: String { return parserErrorTemplateURL.path! }
- var parserErrorTemplateString: String { return try! String(contentsOfFile: parserErrorTemplatePath, encoding: NSUTF8StringEncoding) }
+ var parserErrorTemplateURL: URL { return testBundle.url(forResource: parserErrorTemplateName, withExtension: "mustache")! }
+ var parserErrorTemplatePath: String { return parserErrorTemplateURL.path }
+ var parserErrorTemplateString: String { return try! String(contentsOfFile: parserErrorTemplatePath, encoding: String.Encoding.utf8) }
let parserErrorTemplateWrapperName = "TemplateFromMethodsTests_parserErrorWrapper"
- var parserErrorTemplateWrapperURL: NSURL { return testBundle.URLForResource(parserErrorTemplateWrapperName, withExtension: "mustache")! }
- var parserErrorTemplateWrapperPath: String { return parserErrorTemplateWrapperURL.path! }
- var parserErrorTemplateWrapperString: String { return try! String(contentsOfFile: parserErrorTemplateWrapperPath, encoding: NSUTF8StringEncoding) }
+ var parserErrorTemplateWrapperURL: URL { return testBundle.url(forResource: parserErrorTemplateWrapperName, withExtension: "mustache")! }
+ var parserErrorTemplateWrapperPath: String { return parserErrorTemplateWrapperURL.path }
+ var parserErrorTemplateWrapperString: String { return try! String(contentsOfFile: parserErrorTemplateWrapperPath, encoding: String.Encoding.utf8) }
let compilerErrorTemplateName = "TemplateFromMethodsTests_compilerError"
- var compilerErrorTemplateURL: NSURL { return testBundle.URLForResource(compilerErrorTemplateName, withExtension: "mustache")! }
- var compilerErrorTemplatePath: String { return compilerErrorTemplateURL.path! }
- var compilerErrorTemplateString: String { return try! String(contentsOfFile: compilerErrorTemplatePath, encoding: NSUTF8StringEncoding) }
+ var compilerErrorTemplateURL: URL { return testBundle.url(forResource: compilerErrorTemplateName, withExtension: "mustache")! }
+ var compilerErrorTemplatePath: String { return compilerErrorTemplateURL.path }
+ var compilerErrorTemplateString: String { return try! String(contentsOfFile: compilerErrorTemplatePath, encoding: String.Encoding.utf8) }
let compilerErrorTemplateWrapperName = "TemplateFromMethodsTests_compilerErrorWrapper"
- var compilerErrorTemplateWrapperURL: NSURL { return testBundle.URLForResource(compilerErrorTemplateWrapperName, withExtension: "mustache")! }
- var compilerErrorTemplateWrapperPath: String { return compilerErrorTemplateWrapperURL.path! }
- var compilerErrorTemplateWrapperString: String { return try! String(contentsOfFile: compilerErrorTemplateWrapperPath, encoding: NSUTF8StringEncoding) }
+ var compilerErrorTemplateWrapperURL: URL { return testBundle.url(forResource: compilerErrorTemplateWrapperName, withExtension: "mustache")! }
+ var compilerErrorTemplateWrapperPath: String { return compilerErrorTemplateWrapperURL.path }
+ var compilerErrorTemplateWrapperString: String { return try! String(contentsOfFile: compilerErrorTemplateWrapperPath, encoding: String.Encoding.utf8) }
- func valueForKey(key: String, inRendering rendering: String) -> AnyObject? {
- let data = rendering.dataUsingEncoding(NSUTF8StringEncoding)!
- let object: AnyObject = try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(rawValue: 0))
- return object.valueForKey(key)
+ func valueForKey(_ key: String, inRendering rendering: String) -> AnyObject? {
+ let data = rendering.data(using: String.Encoding.utf8)!
+ let object: AnyObject = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) as AnyObject
+ return object.value(forKey: key)
}
- func valueForStringPropertyInRendering(rendering: String) -> String? {
+ func valueForStringPropertyInRendering(_ rendering: String) -> String? {
return valueForKey("string", inRendering: rendering) as! String?
}
- func extensionOfTemplateFileInRendering(rendering: String) -> String? {
+ func extensionOfTemplateFileInRendering(_ rendering: String) -> String? {
return (valueForKey("fileName", inRendering: rendering) as! NSString?)?.pathExtension
}
@@ -111,8 +111,8 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(string: parserErrorTemplateString)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -123,9 +123,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(path: parserErrorTemplatePath)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(parserErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: parserErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -134,9 +134,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(path: parserErrorTemplateWrapperPath)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(parserErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: parserErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -147,9 +147,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(URL: parserErrorTemplateURL)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(parserErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: parserErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -158,9 +158,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(URL: parserErrorTemplateWrapperURL)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(parserErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: parserErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -171,9 +171,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(named: parserErrorTemplateName, bundle: testBundle)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(parserErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: parserErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -182,9 +182,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(named: parserErrorTemplateWrapperName, bundle: testBundle)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(parserErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: parserErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -195,8 +195,8 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(string: compilerErrorTemplateString)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -207,9 +207,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(path: compilerErrorTemplatePath)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(compilerErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: compilerErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -218,9 +218,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(path: compilerErrorTemplateWrapperPath)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(compilerErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: compilerErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -231,9 +231,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(URL: compilerErrorTemplateURL)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(compilerErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: compilerErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -242,9 +242,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(URL: compilerErrorTemplateWrapperURL)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(compilerErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: compilerErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -255,9 +255,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(named: compilerErrorTemplateName, bundle: testBundle)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(compilerErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: compilerErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
@@ -266,9 +266,9 @@ class TemplateFromMethodsTests: XCTestCase {
let _ = try Template(named: compilerErrorTemplateWrapperName, bundle: testBundle)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
- XCTAssertEqual(error.kind, MustacheError.Kind.ParseError)
- XCTAssertTrue(error.description.rangeOfString("line 2") != nil)
- XCTAssertTrue(error.description.rangeOfString(compilerErrorTemplatePath) != nil)
+ XCTAssertEqual(error.kind, MustacheError.Kind.parseError)
+ XCTAssertTrue(error.description.range(of: "line 2") != nil)
+ XCTAssertTrue(error.description.range(of: compilerErrorTemplatePath) != nil)
} catch {
XCTFail("Expected MustacheError")
}
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index ff30484d..04792a0f 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -996,9 +996,11 @@
};
563AE19619FBE47A0028C6C1 = {
CreatedOnToolsVersion = 6.1;
+ LastSwiftMigration = 0800;
};
563AE1A119FBE47A0028C6C1 = {
CreatedOnToolsVersion = 6.1;
+ LastSwiftMigration = 0800;
};
};
};
@@ -1660,6 +1662,7 @@
PRODUCT_NAME = Mustache;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -1682,6 +1685,7 @@
PRODUCT_NAME = Mustache;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@@ -1702,6 +1706,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -1718,6 +1723,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
From c899e4857c44b0651faa24c8edd25d82f32c65d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Wed, 21 Sep 2016 08:43:36 +0200
Subject: [PATCH 02/46] WIP: Swift 3 (#32)
---
Sources/Box.swift | 95 ++++++++++++-------
Tests/Public/BoxTests.swift | 30 +++---
.../ContextRegisteredKeyTests.swift | 2 +-
Tests/Public/ContextTests/ContextTests.swift | 2 +-
...ntextValueForMustacheExpressionTests.swift | 2 +-
.../DocumentationTests/ReadMeTests.swift | 10 +-
Tests/Public/FoundationCollectionTests.swift | 18 ++--
Tests/Public/HookFunctionTests.swift | 2 +-
.../ServicesTests/EachFilterTests.swift | 8 +-
Tests/Public/ServicesTests/LoggerTests.swift | 8 +-
.../TemplateRepositoryURLTests.swift | 4 +-
.../TemplateFromMethodsTests.swift | 4 +-
12 files changed, 107 insertions(+), 78 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index cacec431..e0183657 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -544,6 +544,31 @@ extension NSObject : MustacheBoxable {
}
+/**
+ */
+
+extension ReferenceConvertible where Self: MustacheBoxable {
+ public var mustacheBox: MustacheBox {
+ if let object = self as? ReferenceType {
+ return object.mustacheBox
+ } else {
+ NSLog("Value `\(self)` can not feed Mustache templates: it is discarded.")
+ return Box()
+ }
+ }
+}
+
+extension Data : MustacheBoxable { }
+extension Date : MustacheBoxable { }
+extension DateComponents : MustacheBoxable { }
+extension IndexPath : MustacheBoxable { }
+extension IndexSet : MustacheBoxable { }
+extension URL : MustacheBoxable { }
+extension URLComponents : MustacheBoxable { }
+extension URLQueryItem : MustacheBoxable { }
+extension URLRequest : MustacheBoxable { }
+extension UUID : MustacheBoxable { }
+
/**
GRMustache provides built-in support for rendering `NSNull`.
*/
@@ -713,37 +738,37 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
}
-// IMPLEMENTATION NOTE
-//
-// Why is there a Box(NSObject?) function, when Box(MustacheBoxable?) should be
-// enough, given NSObject adopts MustacheBoxable?
-//
-// Well, this is another Swift oddity.
-//
-// Without this explicit NSObject support, many compound values like the ones
-// below could not be boxed:
-//
-// - ["cats": ["Kitty", "Pussy", "Melba"]]
-// - [[0,1],[2,3]]
-//
-// It looks like Box(object: NSObject?) triggers the silent conversion of those
-// values to NSArray and NSDictionary.
-//
-// It's an extra commodity we want to keep, in order to prevent the user to
-// rewrite them as:
-//
-// - ["cats": Box([Box("Kitty"), Box("Pussy"), Box("Melba")])]
-// - [Box([0,1]), Box([2,3])]
-
-/**
-See the documentation of `NSObject.mustacheBox`.
-
-- parameter object: An NSObject.
-- returns: A MustacheBox that wraps *object*.
-*/
-public func Box(_ object: NSObject?) -> MustacheBox {
- return object?.mustacheBox ?? Box()
-}
+//// IMPLEMENTATION NOTE
+////
+//// Why is there a Box(NSObject?) function, when Box(MustacheBoxable?) should be
+//// enough, given NSObject adopts MustacheBoxable?
+////
+//// Well, this is another Swift oddity.
+////
+//// Without this explicit NSObject support, many compound values like the ones
+//// below could not be boxed:
+////
+//// - ["cats": ["Kitty", "Pussy", "Melba"]]
+//// - [[0,1],[2,3]]
+////
+//// It looks like Box(object: NSObject?) triggers the silent conversion of those
+//// values to NSArray and NSDictionary.
+////
+//// It's an extra commodity we want to keep, in order to prevent the user to
+//// rewrite them as:
+////
+//// - ["cats": Box([Box("Kitty"), Box("Pussy"), Box("Melba")])]
+//// - [Box([0,1]), Box([2,3])]
+//
+///**
+//See the documentation of `NSObject.mustacheBox`.
+//
+//- parameter object: An NSObject.
+//- returns: A MustacheBox that wraps *object*.
+//*/
+//public func Box(_ object: NSObject?) -> MustacheBox {
+// return object?.mustacheBox ?? Box()
+//}
// IMPLEMENTATION NOTE
@@ -1381,11 +1406,11 @@ dictionary, whatever the actual type of the raw boxed value.
- returns: A MustacheBox that wraps *dictionary*.
*/
-public func Box(_ dictionary: [String: T]?) -> MustacheBox {
+public func Box(_ dictionary: [String: MustacheBoxable]?) -> MustacheBox {
if let dictionary = dictionary {
return MustacheBox(
converter: MustacheBox.Converter(
- dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: T)) in
+ dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: MustacheBoxable)) in
var boxDictionary = boxDictionary
boxDictionary[item.key] = Box(item.value)
return boxDictionary
@@ -1447,11 +1472,11 @@ dictionary, whatever the actual type of the raw boxed value.
- returns: A MustacheBox that wraps *dictionary*.
*/
-public func Box(_ dictionary: [String: T?]?) -> MustacheBox {
+public func Box(_ dictionary: [String: MustacheBoxable?]?) -> MustacheBox {
if let dictionary = dictionary {
return MustacheBox(
converter: MustacheBox.Converter(
- dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: T?)) in
+ dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: MustacheBoxable?)) in
var boxDictionary = boxDictionary
boxDictionary[item.key] = Box(item.value)
return boxDictionary
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index 9e1c3b2d..101aaa91 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -70,28 +70,32 @@ class BoxTests: XCTestCase {
let boxableStruct = BoxableStruct(name: "BoxableStruct")
let boxableClass = BoxableClass(name: "BoxableClass")
let optionalBoxableClass: BoxableClass? = BoxableClass(name: "BoxableClass")
- let object = Date()
+ let nsObject = NSDate()
+ let referenceConvertible = Date()
let boxedBoxableStruct = boxableStruct.mustacheBox()
let boxedStruct = MustacheBox(value: Struct(name: "Struct"))
let boxedBoxableClass = boxableClass.mustacheBox()
let boxedOptionalBoxableClass = optionalBoxableClass!.mustacheBox()
let boxedClass = MustacheBox(value: Class(name: "Class"))
- let boxedObject = Box(object)
+ let boxedNSObject = Box(nsObject)
+ let boxedReferenceConvertible = Box(referenceConvertible)
let extractedBoxableStruct = boxedBoxableStruct.value as! BoxableStruct
let extractedStruct = boxedStruct.value as! Struct
let extractedBoxableClass = boxedBoxableClass.value as! BoxableClass
let extractedOptionalBoxableClass = boxedOptionalBoxableClass.value as? BoxableClass
let extractedClass = boxedClass.value as! Class
- let extractedObject = boxedObject.value as! Date
+ let extractedNSObject = boxedNSObject.value as! NSDate
+ let extractedReferenceConvertible = boxedNSObject.value as! Date
XCTAssertEqual(extractedBoxableStruct.name, "BoxableStruct")
XCTAssertEqual(extractedStruct.name, "Struct")
XCTAssertEqual(extractedBoxableClass.name, "BoxableClass")
XCTAssertEqual(extractedOptionalBoxableClass!.name, "BoxableClass")
XCTAssertEqual(extractedClass.name, "Class")
- XCTAssertEqual(extractedObject, object)
+ XCTAssertEqual(extractedNSObject, nsObject)
+ XCTAssertEqual(extractedReferenceConvertible, referenceConvertible)
}
// TODO: why is this test commented out?
@@ -202,32 +206,32 @@ class BoxTests: XCTestCase {
XCTAssertEqual(rendering, "0123")
}
- func testArrayOfArrayOfInt() {
- let value: Array> = [[0,1],[2,3]]
+ func testNSArrayOfArrayOfInt() {
+ let value: NSArray = [[0,1],[2,3]]
let template = try! Template(string: "{{#.}}[{{#.}}{{.}},{{/}}],{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "[0,1,],[2,3,],")
}
- func testArrayOfArrayOfArrayOfInt() {
- let value: Array>> = [[[0,1],[2,3]], [[4,5],[6,7]]]
+ func testNSArrayOfArrayOfArrayOfInt() {
+ let value: NSArray = [[[0,1],[2,3]], [[4,5],[6,7]]]
let template = try! Template(string: "{{#.}}[{{#.}}[{{#.}}{{.}},{{/}}],{{/}}],{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "[[0,1,],[2,3,],],[[4,5,],[6,7,],],")
}
- func testArrayOfArrayOfArrayOfDictionaryOfInt() {
- let value: Array>>> = [[[["a":0],["a":1]],[["a":2],["a":3]]], [[["a":4],["a":5]],[["a":6],["a":7]]]]
+ func testNSArrayOfArrayOfArrayOfDictionaryOfInt() {
+ let value: NSArray = [[[["a":0],["a":1]],[["a":2],["a":3]]], [[["a":4],["a":5]],[["a":6],["a":7]]]]
let template = try! Template(string: "{{#.}}[{{#.}}[{{#.}}{{a}},{{/}}],{{/}}],{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "[[0,1,],[2,3,],],[[4,5,],[6,7,],],")
}
- func testDictionaryOfArrayOfArrayOfArrayOfDictionaryOfInt() {
- let value: Dictionary>>>> = ["a": [[[["1": 1], ["2": 2]], [["3": 3], ["4": 4]]], [[["5": 5], ["6": 6]], [["7": 7], ["8": 8]]]]]
+ func testNSDictionaryOfArrayOfArrayOfArrayOfDictionaryOfInt() {
+ let value: NSDictionary = ["a": [[[["1": 1], ["2": 2]], [["3": 3], ["4": 4]]], [[["5": 5], ["6": 6]], [["7": 7], ["8": 8]]]]]
let template = try! Template(string: "{{#a}}[{{#.}}[{{#.}}[{{#each(.)}}{{@key}}:{{.}}{{/}}]{{/}}]{{/}}]{{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
let box = Box(value)
@@ -282,7 +286,7 @@ class BoxTests: XCTestCase {
func testArrayValueForRange() {
let originalValue = 1...3
let box = Box(originalValue)
- let extractedValue = box.value as! CountableRange
+ let extractedValue = box.value as! CountableClosedRange
XCTAssertEqual(extractedValue, originalValue)
let extractedArray: [MustacheBox] = box.arrayValue!
XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
diff --git a/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift b/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift
index 0c59d954..6fb52e57 100644
--- a/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift
+++ b/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift
@@ -66,7 +66,7 @@ class ContextRegisteredKeyTests: XCTestCase {
// This is more a caveat than a feature, isn't it?
let template = try! Template(string: "{{#safe}}{{#evil}}{{name}}{{/evil}}{{/safe}}")
template.registerInBaseContext("safe", Box(["name": "important"]))
- let rendering = try! template.render(Box(["evil": ["name": "hacked"]]))
+ let rendering = try! template.render(Box(["evil": ["name": "hacked"]] as NSDictionary))
XCTAssertEqual(rendering, "hacked")
}
}
diff --git a/Tests/Public/ContextTests/ContextTests.swift b/Tests/Public/ContextTests/ContextTests.swift
index 384a29b2..e28d78be 100644
--- a/Tests/Public/ContextTests/ContextTests.swift
+++ b/Tests/Public/ContextTests/ContextTests.swift
@@ -75,7 +75,7 @@ class ContextTests: XCTestCase {
}
func testSubscript() {
- let context = Context(Box(["name": "name1", "a": ["name": "name2"]]))
+ let context = Context(Box(["name": "name1", "a": ["name": "name2"]] as NSDictionary))
// '.' is an expression, not a key
XCTAssertTrue(context.mustacheBoxForKey(".").isEmpty)
diff --git a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
index 537fab21..0d06413d 100644
--- a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
+++ b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
@@ -41,7 +41,7 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
}
func testScopedExpression() {
- let context = Context(Box(["a": ["name": "success"]]))
+ let context = Context(Box(["a": ["name": "success"]] as NSDictionary))
let box = try! context.mustacheBoxForExpression("a.name")
let string = box.value as? String
XCTAssertEqual(string!, "success")
diff --git a/Tests/Public/DocumentationTests/ReadMeTests.swift b/Tests/Public/DocumentationTests/ReadMeTests.swift
index c7be05d8..6d4256d8 100644
--- a/Tests/Public/DocumentationTests/ReadMeTests.swift
+++ b/Tests/Public/DocumentationTests/ReadMeTests.swift
@@ -54,11 +54,11 @@ class ReadMeTests: XCTestCase {
func testReadmeExample1() {
let testBundle = Bundle(for: type(of: self))
let template = try! Template(named: "ReadMeExample1", bundle: testBundle)
- let data = [
+ let data: [String: MustacheBoxable?] = [
"name": "Chris",
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
- "in_ca": true] as [String : Any]
+ "in_ca": true]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "Hello Chris\nYou have just won 10000 dollars!\n\nWell, 6000.0 dollars, after taxes.\n")
}
@@ -90,7 +90,7 @@ class ReadMeTests: XCTestCase {
let testBundle = Bundle(for: type(of: self))
let template = try! Template(named: "ReadMeExample2", bundle: testBundle)
- let data = ["cats": ["Kitty", "Pussy", "Melba"]]
+ let data: NSDictionary = ["cats": ["Kitty", "Pussy", "Melba"]]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "I have 3 cats.")
}
@@ -160,12 +160,12 @@ class ReadMeTests: XCTestCase {
let template = try! Template(string: templateString)
template.registerInBaseContext("format", Box(dateFormatter))
- let data = [
+ let data: NSDictionary = [
"name": "Arthur",
"date": Date(),
"real_date": Date().addingTimeInterval(60*60*24*3),
"late": true
- ] as [String : Any]
+ ]
let rendering = try! template.render(Box(data))
XCTAssert(rendering.characters.count > 0)
}
diff --git a/Tests/Public/FoundationCollectionTests.swift b/Tests/Public/FoundationCollectionTests.swift
index afb388cc..1896c521 100644
--- a/Tests/Public/FoundationCollectionTests.swift
+++ b/Tests/Public/FoundationCollectionTests.swift
@@ -34,27 +34,27 @@ class FoundationCollectionTests: XCTestCase {
var boxedNSOrderedSet: MustacheBox!
override func setUp() {
- boxedArray = Box(["collection": [["key": "value"]]])
+ boxedArray = Box(["collection": Box([["key": "value"] as NSDictionary])])
boxedNSArray = {
let array = NSMutableArray()
array.add(["key": "value"])
let data = NSMutableDictionary()
- data.setObject(array, forKey: "collection")
+ data.setObject(array, forKey: "collection" as NSString)
return Box(data)
}()
- boxedSet = Box(["collection": Set([["key": "value"]])])
+ boxedSet = Box(["collection": Box(Set([["key": "value"] as NSDictionary]))])
boxedNSSet = {
let set = NSMutableSet()
set.add(["key": "value"])
let data = NSMutableDictionary()
- data.setObject(set, forKey: "collection")
+ data.setObject(set, forKey: "collection" as NSString)
return Box(data)
}()
boxedNSOrderedSet = {
let orderedSet = NSMutableOrderedSet()
orderedSet.add(["key": "value"])
let data = NSMutableDictionary()
- data.setObject(orderedSet, forKey: "collection")
+ data.setObject(orderedSet, forKey: "collection" as NSString)
return Box(data)
}()
}
@@ -118,8 +118,8 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":[]])), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":["foo"]])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box([])])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(["foo"])])), "Not empty")
}
func testArrayCountKey() {
@@ -197,8 +197,8 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Set()])), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Set(["foo"])])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(Set())])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(Set(["foo"]))])), "Not empty")
}
func testSetCountKey() {
diff --git a/Tests/Public/HookFunctionTests.swift b/Tests/Public/HookFunctionTests.swift
index 73f48ccb..cc7dd758 100644
--- a/Tests/Public/HookFunctionTests.swift
+++ b/Tests/Public/HookFunctionTests.swift
@@ -190,7 +190,7 @@ class HookFunctionTests: XCTestCase {
template.baseContext = template.baseContext.extendedContext(Box(willRender))
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": ["foo": "bar"]]))
+ rendering = try! template.render(Box(["subject": ["foo": "bar"]] as NSDictionary))
XCTAssertEqual(rendering, "bar")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! String), "bar")
diff --git a/Tests/Public/ServicesTests/EachFilterTests.swift b/Tests/Public/ServicesTests/EachFilterTests.swift
index c7b1c282..1bf4f132 100644
--- a/Tests/Public/ServicesTests/EachFilterTests.swift
+++ b/Tests/Public/ServicesTests/EachFilterTests.swift
@@ -30,7 +30,7 @@ class EachFilterTests: XCTestCase {
let set = Set(["a", "b"])
let template = try! Template(string: "{{#each(set)}}({{@index}},{{.}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["set": set]))
+ let rendering = try! template.render(Box(["set": Box(set)]))
XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].index(of: rendering) != nil)
}
@@ -74,7 +74,7 @@ class EachFilterTests: XCTestCase {
let template = try! Template(string: "{{#each(items)}}({{@index}},{{increment(.)}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
template.registerInBaseContext("increment", Box(increment))
- let rendering = try! template.render(Box(["items": items]))
+ let rendering = try! template.render(Box(["items": Box(items)]))
XCTAssertEqual(rendering, "(0,2)(1,3)(2,4)")
}
@@ -82,7 +82,7 @@ class EachFilterTests: XCTestCase {
let items = ["a","bb","ccc"]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{length}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["items": items]))
+ let rendering = try! template.render(Box(["items": Box(items)]))
XCTAssertEqual(rendering, "(0,1)(1,2)(2,3)")
}
@@ -91,7 +91,7 @@ class EachFilterTests: XCTestCase {
let items = [Box(item)]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{.}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["items": items]))
+ let rendering = try! template.render(Box(["items": Box(items)]))
XCTAssertEqual(rendering, "(0,foo)")
}
}
diff --git a/Tests/Public/ServicesTests/LoggerTests.swift b/Tests/Public/ServicesTests/LoggerTests.swift
index 0690a35d..6e92d853 100644
--- a/Tests/Public/ServicesTests/LoggerTests.swift
+++ b/Tests/Public/ServicesTests/LoggerTests.swift
@@ -33,8 +33,8 @@ class LoggerTests : XCTestCase {
let template = try! Template(string: "{{#people}}- {{name}} has a Mustache.\n{{/people}}")
template.extendBaseContext(Box(logger))
- let data = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
- try! template.render(Box(data))
+ let data: NSDictionary = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
+ _ = try! template.render(Box(data))
XCTAssertEqual(logMessages.count, 5)
XCTAssertEqual(logMessages[0], "{{#people}} at line 1 will render [[\"name\":\"Frank Zappa\"],[\"name\":\"Charlie Chaplin\"],[\"name\":\"Albert Einstein\"]]")
@@ -51,8 +51,8 @@ class LoggerTests : XCTestCase {
let template = try! Template(string: "{{#people}}{{#log}}- {{name}} has a Mustache.\n{{/log}}{{/people}}{{#log}}{{missing}}{{/log}}")
template.registerInBaseContext("log", Box(logger))
- let data = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
- try! template.render(Box(data))
+ let data: NSDictionary = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
+ _ = try! template.render(Box(data))
XCTAssertEqual(logMessages.count, 4)
XCTAssertEqual(logMessages[0], "{{name}} at line 1 did render \"Frank Zappa\" as \"Frank Zappa\"")
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift
index 479473fd..557520b9 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryFileSystemTests/TemplateRepositoryURLTests.swift
@@ -112,14 +112,14 @@ class TemplateRepositoryURLTests: XCTestCase {
func testPartialNameCanNotEscapeTemplateRepositoryRootURL() {
let testBundle = Bundle(for: type(of: self))
let URL = testBundle.url(forResource: "TemplateRepositoryFileSystemTests", withExtension: nil)!
- let repo = TemplateRepository(baseURL: URL.appendingPathComponent("partials")!)
+ let repo = TemplateRepository(baseURL: URL.appendingPathComponent("partials"))
let template = try! repo.template(named: "partial2")
let rendering = try! template.render()
XCTAssertEqual(rendering, "success")
do {
- try repo.template(named: "up")
+ _ = try repo.template(named: "up")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
diff --git a/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift b/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
index adc3d728..9689f7f3 100644
--- a/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
+++ b/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
@@ -63,9 +63,9 @@ class TemplateFromMethodsTests: XCTestCase {
var compilerErrorTemplateWrapperPath: String { return compilerErrorTemplateWrapperURL.path }
var compilerErrorTemplateWrapperString: String { return try! String(contentsOfFile: compilerErrorTemplateWrapperPath, encoding: String.Encoding.utf8) }
- func valueForKey(_ key: String, inRendering rendering: String) -> AnyObject? {
+ func valueForKey(_ key: String, inRendering rendering: String) -> Any? {
let data = rendering.data(using: String.Encoding.utf8)!
- let object: AnyObject = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) as AnyObject
+ let object = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) as! NSObject
return object.value(forKey: key)
}
From b4be2d5733eadf42118247a7fa51e7f14f5e8a23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Thu, 22 Sep 2016 08:05:56 +0200
Subject: [PATCH 03/46] WIP: Swift 3 (#32)
---
Tests/Public/BoxTests.swift | 2 +-
Tests/Public/FoundationCollectionTests.swift | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index 101aaa91..944c91da 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -87,7 +87,7 @@ class BoxTests: XCTestCase {
let extractedOptionalBoxableClass = boxedOptionalBoxableClass.value as? BoxableClass
let extractedClass = boxedClass.value as! Class
let extractedNSObject = boxedNSObject.value as! NSDate
- let extractedReferenceConvertible = boxedNSObject.value as! Date
+ let extractedReferenceConvertible = boxedReferenceConvertible.value as! Date
XCTAssertEqual(extractedBoxableStruct.name, "BoxableStruct")
XCTAssertEqual(extractedStruct.name, "Struct")
diff --git a/Tests/Public/FoundationCollectionTests.swift b/Tests/Public/FoundationCollectionTests.swift
index 1896c521..68c7ec51 100644
--- a/Tests/Public/FoundationCollectionTests.swift
+++ b/Tests/Public/FoundationCollectionTests.swift
@@ -118,7 +118,7 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box([])])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box([] as [String])])), "Not empty")
XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(["foo"])])), "Not empty")
}
From 3be6ac949a7fa94cb570517e110f8b8fc82f7f54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Thu, 22 Sep 2016 08:43:31 +0200
Subject: [PATCH 04/46] WIP: Swift 3 (#32)
---
CHANGELOG.md | 9 ++
README.md | 16 ++--
Sources/Box.swift | 239 ++++++++++++++++++++++++++++++++--------------
TODO.md | 1 +
4 files changed, 187 insertions(+), 78 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de68d43b..f4b313b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,15 @@
Release Notes
=============
+## Next Version
+
+**New**
+
+- Swift 3
+- Templates learned to render Int64 and UInt64
+- TODO: describe modified relationships with Foundation
+
+
## v1.1.0
Released on September 19, 2016
diff --git a/README.md b/README.md
index ab6d2d20..7f2f4ce3 100644
--- a/README.md
+++ b/README.md
@@ -205,7 +205,7 @@ Templates may come from various sources:
templateExtension: "sh")
// Disable HTML escaping for Bash scripts:
- repo.configuration.contentType = .Text
+ repo.configuration.contentType = .text
// Load the "script.sh" resource:
let template = repo.template(named: "script")!
@@ -231,7 +231,7 @@ do {
// Unclosed Mustache tag.
error.description
- // TemplateNotFound, ParseError, or RenderError
+ // templateNotFound, parseError, or renderError
error.kind
// The eventual template at the source of the error. Can be a path, a URL,
@@ -887,7 +887,7 @@ Standard Swift Types Reference
GRMustache.swift comes with built-in support for the following standard Swift types:
- [Bool](#bool)
-- [Numeric Types](#numeric-types): Int, UInt and Double
+- [Numeric Types](#numeric-types): Int, UInt, Int64, UInt64 and Double
- [String](#string)
- [Set](#set) (and similar collections)
- [Array](#array) (and similar collections)
@@ -905,7 +905,7 @@ GRMustache.swift comes with built-in support for the following standard Swift ty
### Numeric Types
-GRMustache supports `Int`, `UInt` and `Double`:
+GRMustache supports `Int`, `UInt`, `Int64`, `UInt64` and `Double`:
- `{{number}}` renders the standard Swift string interpolation of *number*.
- `{{#number}}...{{/number}}` renders if and only if *number* is not 0 (zero).
@@ -1000,7 +1000,7 @@ The rendering of NSObject depends on the actual class:
When an object conforms to the NSFastEnumeration protocol, like **NSArray**, it renders just like Swift [Array](#array). **NSSet** is an exception, rendered as a Swift [Set](#set). **NSDictionary**, the other exception, renders as a Swift [Dictionary](#dictionary).
-- **NSNumber** is rendered as a Swift [Bool](#bool), [Int, UInt or Double](#numeric-types), depending on its value.
+- **NSNumber** is rendered as a Swift [Bool](#bool), [Int, UInt, Int64, UInt64 or Double](#numeric-types), depending on its value.
- **NSString** is rendered as [String](#string)
@@ -1204,13 +1204,17 @@ Filters can accept a precisely typed argument as above. You may prefer managing
```swift
// Define the `abs` filter.
//
-// abs(x) evaluates to the absolute value of x (Int, UInt or Double):
+// abs(x) evaluates to the absolute value of x (Int, UInt, Int64, UInt64 or Double):
let absFilter = Filter { (box: MustacheBox) in
switch box.value {
case let int as Int:
return Box(abs(int))
+ case let int64 as Int64:
+ return Box(abs(int64))
case let uint as UInt:
return Box(uint)
+ case let uint64 as UInt64:
+ return Box(uint64)
case let double as Double:
return Box(abs(double))
default:
diff --git a/Sources/Box.swift b/Sources/Box.swift
index e0183657..21a6a34c 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -203,6 +203,63 @@ extension Bool : MustacheBoxable {
}
+/**
+GRMustache provides built-in support for rendering `Int64`.
+*/
+
+extension Int64 : MustacheBoxable {
+
+ /**
+ `Int64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 1.mustacheBox // Valid, but discouraged
+ Box(1) // Preferred
+
+
+ ### Rendering
+
+ - `{{int}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
+
+ - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ int }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# ints }}...{{/ ints }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# int }}...{{/ int }}
+ //
+ // Ints do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
/**
GRMustache provides built-in support for rendering `Int`.
*/
@@ -260,6 +317,63 @@ extension Int : MustacheBoxable {
}
+/**
+GRMustache provides built-in support for rendering `UInt64`.
+*/
+
+extension UInt64 : MustacheBoxable {
+
+ /**
+ `UInt64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 1.mustacheBox // Valid, but discouraged
+ Box(1) // Preferred
+
+
+ ### Rendering
+
+ - `{{uint}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
+
+ - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ uint }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# uints }}...{{/ uints }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# uint }}...{{/ uint }}
+ //
+ // Uints do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
/**
GRMustache provides built-in support for rendering `UInt`.
*/
@@ -518,7 +632,7 @@ extension NSObject : MustacheBoxable {
// Enumerable
// Turn enumerable into a Swift array of MustacheBoxes that we know how to box
- let array = IteratorSequence(NSFastEnumerationIterator(enumerable)).map(BoxAnyObject)
+ let array = IteratorSequence(NSFastEnumerationIterator(enumerable)).map(BoxAny)
return array.mustacheBoxWithArrayValue(self, box: { $0 })
} else {
@@ -530,7 +644,7 @@ extension NSObject : MustacheBoxable {
keyedSubscript: { (key: String) in
if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
// Use valueForKey: for safe keys
- return BoxAnyObject(self.value(forKey: key))
+ return BoxAny(self.value(forKey: key))
} else {
// Missing key
return Box()
@@ -544,10 +658,11 @@ extension NSObject : MustacheBoxable {
}
-/**
- */
-
+/// Support for Mustache rendering of ReferenceConvertible types.
extension ReferenceConvertible where Self: MustacheBoxable {
+ /// Returns a MustacheBox that behaves like the equivalent NSObject.
+ ///
+ /// See NSObject.mustacheBox
public var mustacheBox: MustacheBox {
if let object = self as? ReferenceType {
return object.mustacheBox
@@ -558,15 +673,34 @@ extension ReferenceConvertible where Self: MustacheBoxable {
}
}
+/// Data can feed Mustache templates.
extension Data : MustacheBoxable { }
+
+/// Date can feed Mustache templates.
extension Date : MustacheBoxable { }
+
+/// DateComponents can feed Mustache templates.
extension DateComponents : MustacheBoxable { }
+
+/// IndexPath can feed Mustache templates.
extension IndexPath : MustacheBoxable { }
+
+/// IndexSet can feed Mustache templates.
extension IndexSet : MustacheBoxable { }
+
+/// URL can feed Mustache templates.
extension URL : MustacheBoxable { }
+
+/// URLComponents can feed Mustache templates.
extension URLComponents : MustacheBoxable { }
+
+/// URLQueryItem can feed Mustache templates.
extension URLQueryItem : MustacheBoxable { }
+
+/// URLRequest can feed Mustache templates.
extension URLRequest : MustacheBoxable { }
+
+/// UUID can feed Mustache templates.
extension UUID : MustacheBoxable { }
/**
@@ -623,7 +757,8 @@ extension NSNumber {
### Rendering
NSNumber renders exactly like Swift numbers: depending on its internal
- objCType, an NSNumber is rendered as a Swift Bool, Int, UInt, or Double.
+ objCType, an NSNumber is rendered as a Swift Bool, Int, UInt, Int64, UInt64,
+ or Double.
- `{{number}}` is rendered with built-in Swift String Interpolation.
Custom formatting can be explicitly required with NSNumberFormatter, as in
@@ -636,13 +771,6 @@ extension NSNumber {
*/
public override var mustacheBox: MustacheBox {
- // IMPLEMENTATION NOTE
- //
- // Don't event think about wrapping unsigned values in an Int, even if
- // Int is large enough to store these values without information loss.
- // This would make template rendering depend on the size of Int, and
- // yield very weird platform-related issues. So keep it simple, stupid.
-
let objCType = String(cString: self.objCType)
switch objCType {
case "c":
@@ -662,9 +790,9 @@ extension NSNumber {
case "L":
return Box(UInt(uintValue))
case "q":
- return Box(Int(int64Value)) // May fail on 32-bits architectures, right?
+ return Box(Int64(int64Value))
case "Q":
- return Box(UInt(uint64Value)) // May fail on 32-bits architectures, right?
+ return Box(UInt64(uint64Value))
case "f":
return Box(Double(floatValue))
case "d":
@@ -738,42 +866,9 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
}
-//// IMPLEMENTATION NOTE
-////
-//// Why is there a Box(NSObject?) function, when Box(MustacheBoxable?) should be
-//// enough, given NSObject adopts MustacheBoxable?
-////
-//// Well, this is another Swift oddity.
-////
-//// Without this explicit NSObject support, many compound values like the ones
-//// below could not be boxed:
-////
-//// - ["cats": ["Kitty", "Pussy", "Melba"]]
-//// - [[0,1],[2,3]]
-////
-//// It looks like Box(object: NSObject?) triggers the silent conversion of those
-//// values to NSArray and NSDictionary.
-////
-//// It's an extra commodity we want to keep, in order to prevent the user to
-//// rewrite them as:
-////
-//// - ["cats": Box([Box("Kitty"), Box("Pussy"), Box("Melba")])]
-//// - [Box([0,1]), Box([2,3])]
-//
-///**
-//See the documentation of `NSObject.mustacheBox`.
-//
-//- parameter object: An NSObject.
-//- returns: A MustacheBox that wraps *object*.
-//*/
-//public func Box(_ object: NSObject?) -> MustacheBox {
-// return object?.mustacheBox ?? Box()
-//}
-
-
// IMPLEMENTATION NOTE
//
-// Why is there a BoxAnyObject(AnyObject?) function, but no Box(AnyObject?)
+// Why is there a BoxAny(Any?) function, but no Box(Any?)
//
// GRMustache aims at having a single boxing function: Box(), with many
// overloaded variants. This lets the user box anything, standard Swift types
@@ -788,10 +883,10 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
// Sometimes values come out of Foundation objects:
//
// class NSDictionary {
-// subscript (key: NSCopying) -> AnyObject? { get }
+// subscript (key: NSCopying) -> Any? { get }
// }
//
-// So we need a Box(AnyObject?) function, right?
+// So we need a Box(Any?) function, right?
//
// Unfortunately, this will not work:
//
@@ -799,7 +894,7 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
// class Thing: MustacheBoxable {}
//
// func Box(x: MustacheBoxable?) -> String { return "MustacheBoxable" }
-// func Box(x: AnyObject?) -> String { return "AnyObject" }
+// func Box(x: Any?) -> String { return "Any" }
//
// // error: ambiguous use of 'Box'
// Box(Thing())
@@ -811,9 +906,9 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
// class Thing: MustacheBoxable {}
//
// func Box(x: T?) -> String { return "MustacheBoxable" }
-// func Box(x: AnyObject?) -> String { return "AnyObject" }
+// func Box(x: Any?) -> String { return "Any" }
//
-// // Wrong: uses the AnyObject variant
+// // Wrong: uses the Any variant
// Box(Thing())
//
// // Error: cannot find an overload for 'Box' that accepts an argument list of type '(MustacheBoxable)'
@@ -822,14 +917,14 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
// // Error: Crash the compiler
// Box(Thing() as MustacheBoxable?)
//
-// And if we turn the func Box(x: AnyObject) into a generic one? Well, it gets
+// And if we turn the func Box(x: Any) into a generic one? Well, it gets
// better:
//
// protocol MustacheBoxable {}
// class Thing: MustacheBoxable {}
//
// func Box(x: MustacheBoxable?) -> String { return "MustacheBoxable" }
-// func Box(object: T?) -> String { return "AnyObject" }
+// func Box(object: T?) -> String { return "Any" }
//
// // OK: uses the MustacheBox variant
// Box(Thing())
@@ -840,11 +935,11 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
// // OK: uses the MustacheBox variant
// Box(Thing() as MustacheBoxable?)
//
-// // OK: uses the AnyObject variant
-// Box(Thing() as AnyObject)
+// // OK: uses the Any variant
+// Box(Thing() as Any)
//
-// // OK: uses the AnyObject variant
-// Box(Thing() as AnyObject?)
+// // OK: uses the Any variant
+// Box(Thing() as Any?)
//
// This looks OK, doesn't it? Well, it's not satisfying yet.
//
@@ -859,18 +954,18 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
// resolves overloaded functions.
//
// So let's avoid having any Box(AnyObject?) variant in the public API, and
-// let's expose the BoxAnyObject(object: AnyObject?) instead.
+// let's expose the BoxAny(object: AnyObject?) instead.
// IMPLEMENTATION NOTE 2
//
-// BoxAnyObject has been made private. Now users get a compiler error when they
+// BoxAny has been made private. Now users get a compiler error when they
// try to box AnyObject.
//
// Reasons for this removal from the public API:
//
// - Users will try Box() first, which will fail. Since they may not know
-// anything BoxAnyObject, BoxAnyObject is of little value anyway.
-// - BoxAnyObject is error-prone, since it accepts anything and fails at
+// anything BoxAny, BoxAny is of little value anyway.
+// - BoxAny is error-prone, since it accepts anything and fails at
// runtime.
//
// It still exists because we need it to box Foundation collections like
@@ -880,11 +975,11 @@ public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
`AnyObject` can feed Mustache templates.
Yet, due to constraints in the Swift language, there is no `Box(AnyObject)`
-function. Instead, you use `BoxAnyObject`:
+function. Instead, you use `BoxAny`:
let set = NSSet(object: "Mario")
let object: AnyObject = set.anyObject()
- let box = BoxAnyObject(object)
+ let box = BoxAny(object)
box.value as String // "Mario"
The object is tested at runtime whether it adopts the `MustacheBoxable`
@@ -895,11 +990,11 @@ Otherwise, GRMustache logs a warning, and returns the empty box.
- parameter object: An object.
- returns: A MustacheBox that wraps *object*.
*/
-private func BoxAnyObject(_ object: Any?) -> MustacheBox {
+private func BoxAny(_ object: Any?) -> MustacheBox {
if let boxable = object as? MustacheBoxable {
return boxable.mustacheBox
} else if let object = object {
- NSLog("Mustache.BoxAnyObject(): value `\(object)` does not conform to MustacheBoxable: it is discarded.")
+ NSLog("Mustache: value `\(object)` does not conform to MustacheBoxable: it is discarded.")
return Box()
} else {
return Box()
@@ -1106,7 +1201,7 @@ extension BidirectionalCollection where IndexDistance == Int {
} else {
return Box()
}
- case "last": // C.Index: BidirectionalIndexType
+ case "last": // C: BidirectionalCollection
if let last = self.last {
return box(last)
} else {
@@ -1195,7 +1290,7 @@ extension NSSet {
//
// So turn NSSet into a Swift Array of MustacheBoxes, and ask the array
// to return a set-like box:
- let array = IteratorSequence(NSFastEnumerationIterator(self)).map(BoxAnyObject)
+ let array = IteratorSequence(NSFastEnumerationIterator(self)).map(BoxAny)
return array.mustacheBoxWithSetValue(self, box: { $0 })
}
}
@@ -1556,14 +1651,14 @@ extension NSDictionary {
dictionaryValue: IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
var boxDictionary = boxDictionary
if let key = key as? String {
- boxDictionary[key] = BoxAnyObject(self[key])
+ boxDictionary[key] = BoxAny(self[key])
} else {
NSLog("GRMustache found a non-string key in NSDictionary (\(key)): value is discarded.")
}
return boxDictionary
})),
value: self,
- keyedSubscript: { BoxAnyObject(self[$0])
+ keyedSubscript: { BoxAny(self[$0])
})
}
}
diff --git a/TODO.md b/TODO.md
index c6cb98df..f948fdb3 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,5 +1,6 @@
TODO
+- [ ] Swift3: Formatter / NSFormatter
- [ ] Use in examples: Freddy Mercury, Salvador Dali, Tom Selleck, Charles Bronson, Clark Gable, Albert Einstein, Charlie Chaplin, Errol Flynn, Groucho Marx, Hulk Hogan, Mario, Luigi, Zorro, Frank Zappa, Lionel Richie
- [ ] Rewrite GRMustacheKeyAccess.m in pure Swift
- [ ] Think about migration from ObjC GRMustache, and list incompatibilities. Fix the most cruel ones.
From 9bbb2c455375be383f47a08409f23c51229c52ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Thu, 29 Sep 2016 14:04:02 +0200
Subject: [PATCH 05/46] #32 WIP: more failing tests for expected valid calls to
Box()
---
Tests/Public/BoxTests.swift | 887 +++++++++++++++++------
Tests/Public/BoxValueTests.swift | 145 ++++
Xcode/Mustache.xcodeproj/project.pbxproj | 8 +
3 files changed, 815 insertions(+), 225 deletions(-)
create mode 100644 Tests/Public/BoxValueTests.swift
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index 944c91da..fd623e2e 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -36,272 +36,701 @@ func ==(lhs: HashableBoxable, rhs: HashableBoxable) -> Bool {
class BoxTests: XCTestCase {
- func testCustomValueExtraction() {
- // Test that one can extract a custom value from MustacheBox.
-
- struct BoxableStruct {
- let name: String
- func mustacheBox() -> MustacheBox {
- return MustacheBox(value: self)
- }
+ func testInt() {
+ do {
+ // Explicit type
+ let value: Int = 0
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
}
-
- struct Struct {
- let name: String
+ do {
+ // As MustacheBoxable
+ let value: MustacheBoxable = 0
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
}
-
- class BoxableClass {
- let name: String
- init(name: String) {
- self.name = name
- }
- func mustacheBox() -> MustacheBox {
- return MustacheBox(value: self)
- }
+ do {
+ // Infered type
+ let value = 0
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
}
-
- class Class {
- let name: String
- init(name: String) {
- self.name = name
- }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{.}}")
+ let box = Box(0)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ }
+
+ func testOptionalInt() {
+ do {
+ // Explicit type
+ let value: Int? = 0
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: MustacheBoxable? = 0
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = Optional.some(0)
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{.}}")
+ let box = Box(Optional.some(0))
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
}
-
- let boxableStruct = BoxableStruct(name: "BoxableStruct")
- let boxableClass = BoxableClass(name: "BoxableClass")
- let optionalBoxableClass: BoxableClass? = BoxableClass(name: "BoxableClass")
- let nsObject = NSDate()
- let referenceConvertible = Date()
-
- let boxedBoxableStruct = boxableStruct.mustacheBox()
- let boxedStruct = MustacheBox(value: Struct(name: "Struct"))
- let boxedBoxableClass = boxableClass.mustacheBox()
- let boxedOptionalBoxableClass = optionalBoxableClass!.mustacheBox()
- let boxedClass = MustacheBox(value: Class(name: "Class"))
- let boxedNSObject = Box(nsObject)
- let boxedReferenceConvertible = Box(referenceConvertible)
-
- let extractedBoxableStruct = boxedBoxableStruct.value as! BoxableStruct
- let extractedStruct = boxedStruct.value as! Struct
- let extractedBoxableClass = boxedBoxableClass.value as! BoxableClass
- let extractedOptionalBoxableClass = boxedOptionalBoxableClass.value as? BoxableClass
- let extractedClass = boxedClass.value as! Class
- let extractedNSObject = boxedNSObject.value as! NSDate
- let extractedReferenceConvertible = boxedReferenceConvertible.value as! Date
-
- XCTAssertEqual(extractedBoxableStruct.name, "BoxableStruct")
- XCTAssertEqual(extractedStruct.name, "Struct")
- XCTAssertEqual(extractedBoxableClass.name, "BoxableClass")
- XCTAssertEqual(extractedOptionalBoxableClass!.name, "BoxableClass")
- XCTAssertEqual(extractedClass.name, "Class")
- XCTAssertEqual(extractedNSObject, nsObject)
- XCTAssertEqual(extractedReferenceConvertible, referenceConvertible)
}
- // TODO: why is this test commented out?
-// func testCustomValueFilter() {
-// // Test that one can define a filter taking a CustomValue as an argument.
-//
-// struct Boxable : MustacheBoxable {
-// let name: String
-// var mustacheBox: MustacheBox {
-// return Box(value: self)
-// }
-// }
-//
-// let filter1 = { (value: Boxable?) -> MustacheBox in
-// if let value = value {
-// return Box(value.name)
-// } else {
-// return Box("other")
-// }
-// }
-//
-// let filter2 = { (value: Boxable?) -> MustacheBox in
-// if let value = value {
-// return Box(value.name)
-// } else {
-// return Box("other")
-// }
-// }
-//
-// let filter3 = { (value: NSDate?) -> MustacheBox in
-// if let value = value {
-// return Box("custom3")
-// } else {
-// return Box("other")
-// }
-// }
-//
-// let template = Template(string:"{{f(custom)}},{{f(string)}}")!
-//
-// let value1 = Box([
-// "string": Box("success"),
-// "custom": Box(Boxable(name: "custom1")),
-// "f": Box(Filter(filter1))
-// ])
-// let rendering1 = template.render(value1)!
-// XCTAssertEqual(rendering1, "custom1,other")
-//
-// let value2 = Box([
-// "string": Box("success"),
-// "custom": Box(value: Boxable(name: "custom2")),
-// "f": Box(Filter(filter2))])
-// let rendering2 = template.render(value2)!
-// XCTAssertEqual(rendering2, "custom2,other")
-//
-// let value3 = Box([
-// "string": Box("success"),
-// "custom": Box(NSDate()),
-// "f": Box(Filter(filter3))])
-// let rendering3 = template.render(value3)!
-// XCTAssertEqual(rendering3, "custom3,other")
-// }
+ func testMustacheBoxable() {
+ do {
+ // Explicit type
+ let value: HashableBoxable = HashableBoxable(int:0)
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable
+ let value: MustacheBoxable = HashableBoxable(int:0)
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = HashableBoxable(int:0)
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{.}}")
+ let box = Box(HashableBoxable(int:0))
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ }
+
+ func testOptionalMustacheBoxable() {
+ do {
+ // Explicit type
+ let value: HashableBoxable? = HashableBoxable(int:0)
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: MustacheBoxable? = HashableBoxable(int:0)
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = Optional.some(HashableBoxable(int:0))
+ let template = try! Template(string: "{{.}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{.}}")
+ let box = Box(Optional.some(HashableBoxable(int:0)))
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ }
func testSetOfInt() {
- let value: Set = [0,1,2]
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ do {
+ // Explicit type
+ let value: Set = [0,1,2]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
+ do {
+ // Infered element type
+ let value: Set = [0,1,2]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
}
func testSetOfMustacheBoxable() {
- let value: Set = [HashableBoxable(int:0),HashableBoxable(int:1),HashableBoxable(int:2)]
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ do {
+ // Explicit type
+ let value: Set = [HashableBoxable(int:0),HashableBoxable(int:1),HashableBoxable(int:2)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
+ do {
+ // Infered element type
+ let value: Set = [HashableBoxable(int:0),HashableBoxable(int:1),HashableBoxable(int:2)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
}
func testDictionaryOfInt() {
- let value: Dictionary = ["name": 1]
- let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "1")
+ do {
+ // Explicit type
+ let value: Dictionary = ["name": 0]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable
+ let value: Dictionary = ["name": 0]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered element type
+ let value: Dictionary = ["name": 0]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = ["name": 0]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{name}}")
+ let box = Box(["name": 0])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
func testDictionaryOfOptionalInt() {
- let value: Dictionary = ["name": 1]
- let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "1")
+ do {
+ // Explicit type
+ let value: Dictionary = ["name": 0]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: Dictionary = ["name": Optional.some(0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered element type
+ let value: Dictionary = ["name": Optional.some(0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = ["name": Optional.some(0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{name}}")
+ let box = Box(["name": Optional.some(0)])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
- func testArrayOfInt() {
- let value: Array = [0,1,2,3]
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "0123")
+ func testDictionaryOfMustacheBoxable() {
+ do {
+ // Explicit type
+ let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable
+ let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered element type
+ let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{name}}")
+ let box = Box(["name": HashableBoxable(int:0)])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
- func testArrayOfOptionalInt() {
- let value: Array = [0,1,2,3, nil]
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "0123")
+ func testDictionaryOfOptionalMustacheBoxable() {
+ do {
+ // Explicit type
+ let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered element type
+ let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Infered type
+ let value = ["name": HashableBoxable(int:0)]
+ let template = try! Template(string: "{{name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{name}}")
+ let box = Box(["name": HashableBoxable(int:0)])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
- func testNSArrayOfArrayOfInt() {
- let value: NSArray = [[0,1],[2,3]]
- let template = try! Template(string: "{{#.}}[{{#.}}{{.}},{{/}}],{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "[0,1,],[2,3,],")
+ func testDictionaryOfAny() {
+ do {
+ // Explicit type
+ let value: Dictionary = ["int": 1, "string": "foo"]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // As MustacheBoxable
+ let value: Dictionary = ["int": 1, "string": "foo"]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered element type
+ let value: Dictionary = ["int": 1, "string": "foo"]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type
+ let value = ["int": 1, "string": "foo"]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(["int": 1, "string": "foo"])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
}
- func testNSArrayOfArrayOfArrayOfInt() {
- let value: NSArray = [[[0,1],[2,3]], [[4,5],[6,7]]]
- let template = try! Template(string: "{{#.}}[{{#.}}[{{#.}}{{.}},{{/}}],{{/}}],{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "[[0,1,],[2,3,],],[[4,5,],[6,7,],],")
+ func testDictionaryOfOptionalAny() {
+ do {
+ // Explicit type
+ let value: Dictionary = ["int": 1, "string": "foo", "missing": nil]
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: Dictionary = ["int": 1, "string": "foo", "missing": nil]
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // Infered element type
+ let value: Dictionary = ["int": Optional.some(1), "string": Optional.some("foo"), "missing": nil]
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // Infered type
+ let value = ["int": Optional.some(1), "string": Optional.some("foo"), "missing": nil]
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // Infered type
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(["int": Optional.some(1), "string": Optional.some("foo"), "missing": nil])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
}
- func testNSArrayOfArrayOfArrayOfDictionaryOfInt() {
- let value: NSArray = [[[["a":0],["a":1]],[["a":2],["a":3]]], [[["a":4],["a":5]],[["a":6],["a":7]]]]
- let template = try! Template(string: "{{#.}}[{{#.}}[{{#.}}{{a}},{{/}}],{{/}}],{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "[[0,1,],[2,3,],],[[4,5,],[6,7,],],")
- }
-
- func testNSDictionaryOfArrayOfArrayOfArrayOfDictionaryOfInt() {
- let value: NSDictionary = ["a": [[[["1": 1], ["2": 2]], [["3": 3], ["4": 4]]], [[["5": 5], ["6": 6]], [["7": 7], ["8": 8]]]]]
- let template = try! Template(string: "{{#a}}[{{#.}}[{{#.}}[{{#each(.)}}{{@key}}:{{.}}{{/}}]{{/}}]{{/}}]{{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "[[[1:1][2:2]][[3:3][4:4]]][[[5:5][6:6]][[7:7][8:8]]]")
+ func testArrayOfInt() {
+ do {
+ // Explicit type
+ let value: Array = [0,1,2,3]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // As MustacheBoxable
+ let value: Array = [0,1,2,3]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered element type
+ let value: Array = [0,1,2,3]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered type
+ let value = [0,1,2,3]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([0,1,2,3])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
- func testRange() {
- let value = 0..<10
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "0123456789")
+ func testArrayOfOptionalInt() {
+ do {
+ // Explicit type
+ let value: Array = [0,1,2,3,nil]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: Array = [0,1,2,3,nil]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered element type
+ let value: Array = [Optional.some(0),Optional.some(1),Optional.some(2),Optional.some(3),Optional.none]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered type: won't compile
+ let value = [Optional.some(0),Optional.some(1),Optional.some(2),Optional.some(3),Optional.none]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([0,1,2,3,nil])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
- func testArrayValueForArray() {
- let originalValue = [1,2,3]
- let box = Box(originalValue)
- let extractedValue = box.value as! [Int]
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
+ func testArrayOfMustacheBoxable() {
+ do {
+ // Explicit type
+ let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // As MustacheBoxable
+ let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered element type
+ let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered type
+ let value = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
-
- func testArrayValueForNSArray() {
- let originalValue = NSArray(object: "foo")
- let box = Box(originalValue)
- let extractedValue = box.value as! NSArray
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
+
+ func testArrayOfOptionalMustacheBoxable() {
+ do {
+ // Explicit type
+ let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered element type
+ let value: Array = [Optional.some(HashableBoxable(int:0)), Optional.some(HashableBoxable(int:1)), Optional.some(HashableBoxable(int:2)), Optional.some(HashableBoxable(int:3))]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Infered type
+ let value = [Optional.some(HashableBoxable(int:0)), Optional.some(HashableBoxable(int:1)), Optional.some(HashableBoxable(int:2)), Optional.some(HashableBoxable(int:3))]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([Optional.some(HashableBoxable(int:0)), Optional.some(HashableBoxable(int:1)), Optional.some(HashableBoxable(int:2)), Optional.some(HashableBoxable(int:3))])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
- func testArrayValueForNSOrderedSet() {
- let originalValue = NSOrderedSet(object: "foo")
- let box = Box(originalValue)
- let extractedValue = box.value as! NSOrderedSet
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
+ func testArrayOfAny() {
+ do {
+ // Explicit type
+ let value: Array = [0,"foo"]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // As MustacheBoxable
+ let value: Array = [0,"foo"]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Infered element type
+ let value: Array = [0,"foo"]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Infered type
+ let value = [0,"foo"]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([0,"foo"])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
}
- func testArrayValueForCollectionOfOne() {
- let originalValue = CollectionOfOne(123)
- let box = Box(originalValue)
- let extractedValue = box.value as! CollectionOfOne
- XCTAssertEqual(extractedValue[extractedValue.startIndex], originalValue[originalValue.startIndex])
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! Int }, [123])
+ func testArrayOfOptionalAny() {
+ do {
+ // Explicit type
+ let value: Array = [0,nil,"foo"]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: Array = [0,nil,"foo"]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Infered element type
+ let value: Array = [Optional.some(0),nil,Optional.some("foo")]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Infered type
+ let value = [Optional.some(0),nil,Optional.some("foo")]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([Optional.some(0),nil,Optional.some("foo")])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
}
- func testArrayValueForRange() {
- let originalValue = 1...3
- let box = Box(originalValue)
- let extractedValue = box.value as! CountableClosedRange
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
+ func testArrayOfNonMustacheBoxable() {
+ class Class { }
+ let array: Array = [Class()]
+ let context = Context(Box(array))
+ let box = context.mustacheBoxForKey("first")
+ XCTAssertTrue(box.value == nil)
}
- func testDictionaryValueForNSDictionary() {
- let originalValue = NSDictionary(object: "value", forKey: "key" as NSCopying)
- let box = Box(originalValue)
- let extractedValue = box.value as! NSDictionary
- XCTAssertEqual(extractedValue, originalValue)
- let extractedDictionary: [String: MustacheBox] = box.dictionaryValue!
- XCTAssertEqual((extractedDictionary["key"]!.value as! String), "value")
+ func testNSArrayOfInt() {
+ let value: NSArray = [0,1,2,3]
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
}
- func testBoxNSArrayOfMustacheBoxable() {
+ func testNSArrayOfMustacheBoxable() {
class Class: MustacheBoxable {
var mustacheBox: MustacheBox {
return MustacheBox(keyedSubscript: { (key: String) in
@@ -316,7 +745,7 @@ class BoxTests: XCTestCase {
XCTAssertEqual((box.value as! String), "foo")
}
- func testBoxNSArrayOfNonMustacheBoxable() {
+ func testNSArrayOfNonMustacheBoxable() {
class Class {
}
@@ -325,4 +754,12 @@ class BoxTests: XCTestCase {
let box = context.mustacheBoxForKey("first")
XCTAssertTrue(box.value == nil)
}
+
+ func testRange() {
+ let value = 0..<10
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123456789")
+ }
}
diff --git a/Tests/Public/BoxValueTests.swift b/Tests/Public/BoxValueTests.swift
new file mode 100644
index 00000000..22279efa
--- /dev/null
+++ b/Tests/Public/BoxValueTests.swift
@@ -0,0 +1,145 @@
+// The MIT License
+//
+// Copyright (c) 2015 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+import XCTest
+import Mustache
+
+class BoxValueTests: XCTestCase {
+
+ func testCustomValueExtraction() {
+ // Test that one can extract a custom value from MustacheBox.
+
+ struct BoxableStruct {
+ let name: String
+ func mustacheBox() -> MustacheBox {
+ return MustacheBox(value: self)
+ }
+ }
+
+ struct Struct {
+ let name: String
+ }
+
+ class BoxableClass {
+ let name: String
+ init(name: String) {
+ self.name = name
+ }
+ func mustacheBox() -> MustacheBox {
+ return MustacheBox(value: self)
+ }
+ }
+
+ class Class {
+ let name: String
+ init(name: String) {
+ self.name = name
+ }
+ }
+
+ let boxableStruct = BoxableStruct(name: "BoxableStruct")
+ let boxableClass = BoxableClass(name: "BoxableClass")
+ let optionalBoxableClass: BoxableClass? = BoxableClass(name: "BoxableClass")
+ let nsObject = NSDate()
+ let referenceConvertible = Date()
+
+ let boxedBoxableStruct = boxableStruct.mustacheBox()
+ let boxedStruct = MustacheBox(value: Struct(name: "Struct"))
+ let boxedBoxableClass = boxableClass.mustacheBox()
+ let boxedOptionalBoxableClass = optionalBoxableClass!.mustacheBox()
+ let boxedClass = MustacheBox(value: Class(name: "Class"))
+ let boxedNSObject = Box(nsObject)
+ let boxedReferenceConvertible = Box(referenceConvertible)
+
+ let extractedBoxableStruct = boxedBoxableStruct.value as! BoxableStruct
+ let extractedStruct = boxedStruct.value as! Struct
+ let extractedBoxableClass = boxedBoxableClass.value as! BoxableClass
+ let extractedOptionalBoxableClass = boxedOptionalBoxableClass.value as? BoxableClass
+ let extractedClass = boxedClass.value as! Class
+ let extractedNSObject = boxedNSObject.value as! NSDate
+ let extractedReferenceConvertible = boxedReferenceConvertible.value as! Date
+
+ XCTAssertEqual(extractedBoxableStruct.name, "BoxableStruct")
+ XCTAssertEqual(extractedStruct.name, "Struct")
+ XCTAssertEqual(extractedBoxableClass.name, "BoxableClass")
+ XCTAssertEqual(extractedOptionalBoxableClass!.name, "BoxableClass")
+ XCTAssertEqual(extractedClass.name, "Class")
+ XCTAssertEqual(extractedNSObject, nsObject)
+ XCTAssertEqual(extractedReferenceConvertible, referenceConvertible)
+ }
+
+ func testArrayValueForArray() {
+ let originalValue = [1,2,3]
+ let box = Box(originalValue)
+ let extractedValue = box.value as! [Int]
+ XCTAssertEqual(extractedValue, originalValue)
+ let extractedArray: [MustacheBox] = box.arrayValue!
+ XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
+ }
+
+ func testArrayValueForNSArray() {
+ let originalValue = NSArray(object: "foo")
+ let box = Box(originalValue)
+ let extractedValue = box.value as! NSArray
+ XCTAssertEqual(extractedValue, originalValue)
+ let extractedArray: [MustacheBox] = box.arrayValue!
+ XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
+ }
+
+ func testArrayValueForNSOrderedSet() {
+ let originalValue = NSOrderedSet(object: "foo")
+ let box = Box(originalValue)
+ let extractedValue = box.value as! NSOrderedSet
+ XCTAssertEqual(extractedValue, originalValue)
+ let extractedArray: [MustacheBox] = box.arrayValue!
+ XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
+ }
+
+ func testArrayValueForCollectionOfOne() {
+ let originalValue = CollectionOfOne(123)
+ let box = Box(originalValue)
+ let extractedValue = box.value as! CollectionOfOne
+ XCTAssertEqual(extractedValue[extractedValue.startIndex], originalValue[originalValue.startIndex])
+ let extractedArray: [MustacheBox] = box.arrayValue!
+ XCTAssertEqual(extractedArray.map { $0.value as! Int }, [123])
+ }
+
+ func testArrayValueForRange() {
+ let originalValue = 1...3
+ let box = Box(originalValue)
+ let extractedValue = box.value as! CountableClosedRange
+ XCTAssertEqual(extractedValue, originalValue)
+ let extractedArray: [MustacheBox] = box.arrayValue!
+ XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
+ }
+
+ func testDictionaryValueForNSDictionary() {
+ let originalValue = NSDictionary(object: "value", forKey: "key" as NSCopying)
+ let box = Box(originalValue)
+ let extractedValue = box.value as! NSDictionary
+ XCTAssertEqual(extractedValue, originalValue)
+ let extractedDictionary: [String: MustacheBox] = box.dictionaryValue!
+ XCTAssertEqual((extractedDictionary["key"]!.value as! String), "value")
+ }
+
+}
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index 04792a0f..0191227e 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -222,6 +222,9 @@
5685DAB41A1A2029003E583B /* LocalizerTestsBundle in Resources */ = {isa = PBXBuildFile; fileRef = 5685DAB31A1A2029003E583B /* LocalizerTestsBundle */; };
5685DAB61A1A2074003E583B /* LocalizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685DAB51A1A2074003E583B /* LocalizerTests.swift */; };
569517021A8646CF00FF7D86 /* ObjcKeyAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569517011A8646CF00FF7D86 /* ObjcKeyAccessTests.swift */; };
+ 5698AC1B1D9D31C30056AF8C /* BoxValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698AC1A1D9D31C30056AF8C /* BoxValueTests.swift */; };
+ 5698AC1C1D9D31C30056AF8C /* BoxValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698AC1A1D9D31C30056AF8C /* BoxValueTests.swift */; };
+ 5698AC1D1D9D31C30056AF8C /* BoxValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698AC1A1D9D31C30056AF8C /* BoxValueTests.swift */; };
5698D0271A1626240039D564 /* KeyedSubscriptFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698D0261A1626240039D564 /* KeyedSubscriptFunctionTests.swift */; };
569C42381A86824800748E98 /* ZipFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C42371A86824800748E98 /* ZipFilter.swift */; };
569C423B1A87D03800748E98 /* ContextRegisteredKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C423A1A87D03800748E98 /* ContextRegisteredKeyTests.swift */; };
@@ -361,6 +364,7 @@
5685DAB31A1A2029003E583B /* LocalizerTestsBundle */ = {isa = PBXFileReference; lastKnownFileType = folder; path = LocalizerTestsBundle; sourceTree = ""; };
5685DAB51A1A2074003E583B /* LocalizerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizerTests.swift; sourceTree = ""; };
569517011A8646CF00FF7D86 /* ObjcKeyAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjcKeyAccessTests.swift; sourceTree = ""; };
+ 5698AC1A1D9D31C30056AF8C /* BoxValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxValueTests.swift; sourceTree = ""; };
5698D0261A1626240039D564 /* KeyedSubscriptFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyedSubscriptFunctionTests.swift; sourceTree = ""; };
569C42371A86824800748E98 /* ZipFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipFilter.swift; sourceTree = ""; };
569C423A1A87D03800748E98 /* ContextRegisteredKeyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextRegisteredKeyTests.swift; sourceTree = ""; };
@@ -766,6 +770,7 @@
isa = PBXGroup;
children = (
56C8D6E01A1F1A4700F106F8 /* BoxTests.swift */,
+ 5698AC1A1D9D31C30056AF8C /* BoxValueTests.swift */,
56FC9C831A17CD0C0020AAF8 /* ConfigurationTests */,
56FC9C881A17CD0C0020AAF8 /* ContextTests */,
56C8D6E21A1F1AD800F106F8 /* DocumentationTests */,
@@ -1184,6 +1189,7 @@
09A89E151BF5870E003A695E /* ContextTests.swift in Sources */,
09A89E081BF58702003A695E /* BoxTests.swift in Sources */,
09A89E1F1BF58720003A695E /* VariadicFilterTests.swift in Sources */,
+ 5698AC1D1D9D31C30056AF8C /* BoxValueTests.swift in Sources */,
09A89E0B1BF58702003A695E /* KeyedSubscriptFunctionTests.swift in Sources */,
09A89E111BF58709003A695E /* ConfigurationContentTypeTests.swift in Sources */,
09A89E181BF58719003A695E /* MustacheBoxDocumentationTests.swift in Sources */,
@@ -1243,6 +1249,7 @@
561E72AB1A8BDC6A004ED48B /* RenderFunctionTests.swift in Sources */,
561E72B71A8BDC6A004ED48B /* TemplateRepositoryPathTests.swift in Sources */,
561E72A51A8BDC6A004ED48B /* FilterTests.swift in Sources */,
+ 5698AC1C1D9D31C30056AF8C /* BoxValueTests.swift in Sources */,
561E72A31A8BDC6A004ED48B /* ReadMeTests.swift in Sources */,
566244941AF1638F008BAD41 /* GRMustacheSpecTests.swift in Sources */,
5611BF991B333E1F005FA874 /* LoggerTests.swift in Sources */,
@@ -1327,6 +1334,7 @@
564717311A18EAE600E383F9 /* FilterTests.swift in Sources */,
56FC9C911A189FA70020AAF8 /* VariadicFilterTests.swift in Sources */,
56B4B21C1A27022C00BD4F8F /* TemplateRepositoryDataSourceTests.swift in Sources */,
+ 5698AC1B1D9D31C30056AF8C /* BoxValueTests.swift in Sources */,
56BA49201A221ECB0029FD45 /* MustacheRenderableGuideTests.swift in Sources */,
566244931AF1638F008BAD41 /* GRMustacheSpecTests.swift in Sources */,
5611BF981B333E1F005FA874 /* LoggerTests.swift in Sources */,
From 247aa670ee8035b1c5a1d3b47a4558e3ab092469 Mon Sep 17 00:00:00 2001
From: Antonin Biret
Date: Thu, 6 Oct 2016 21:57:55 +0200
Subject: [PATCH 06/46] Added open acces level on MustacheBoxable property for
NSObject
---
Sources/Box.swift | 12 ++++++------
Sources/NSFormatter.swift | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index 21a6a34c..348b06a9 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -627,7 +627,7 @@ extension NSObject : MustacheBoxable {
- `{{^object}}...{{/object}}` does not render.
*/
- public var mustacheBox: MustacheBox {
+ open var mustacheBox: MustacheBox {
if let enumerable = self as? NSFastEnumeration {
// Enumerable
@@ -728,7 +728,7 @@ extension NSNull {
- `{{^null}}...{{/null}}` does render (NSNull is falsey).
*/
- public override var mustacheBox: MustacheBox {
+ open override var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
boolValue: false,
@@ -769,7 +769,7 @@ extension NSNumber {
- `{{^number}}...{{/number}}` renders if and only if `number` is 0 (zero).
*/
- public override var mustacheBox: MustacheBox {
+ open override var mustacheBox: MustacheBox {
let objCType = String(cString: self.objCType)
switch objCType {
@@ -846,7 +846,7 @@ extension NSString {
- `length`: the number of characters in the string (using Swift method).
*/
- public override var mustacheBox: MustacheBox {
+ open override var mustacheBox: MustacheBox {
return Box(self as String)
}
}
@@ -1280,7 +1280,7 @@ extension NSSet {
`arrayValue` property: it reliably returns an Array of MustacheBox, whatever
the actual type of the raw boxed value (Set, Array, NSArray, NSSet, ...)
*/
- public override var mustacheBox: MustacheBox {
+ open override var mustacheBox: MustacheBox {
// DRY principle won't let us provide all the code for boxing NSSet when
// we already have it for Set.
//
@@ -1645,7 +1645,7 @@ extension NSDictionary {
`dictionaryValue` property: it reliably returns an `[String: MustacheBox]`
dictionary, whatever the actual type of the raw boxed value.
*/
- public override var mustacheBox: MustacheBox {
+ open override var mustacheBox: MustacheBox {
return MustacheBox(
converter: MustacheBox.Converter(
dictionaryValue: IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
diff --git a/Sources/NSFormatter.swift b/Sources/NSFormatter.swift
index 64eaaf1b..6e84f652 100644
--- a/Sources/NSFormatter.swift
+++ b/Sources/NSFormatter.swift
@@ -85,7 +85,7 @@ extension Formatter {
dates: you can safely mix various data types in a section controlled by
those well-behaved formatters.
*/
- public override var mustacheBox: MustacheBox {
+ open override var mustacheBox: MustacheBox {
// Return a multi-facetted box, because NSFormatter interacts in
// various ways with Mustache rendering.
From 59ef66660b33eb8d34c30d7eb4ac7338390a5045 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 9 Oct 2016 16:05:00 +0200
Subject: [PATCH 07/46] #32 Boxing of collections: done
---
Sources/Box.swift | 156 ++++---
Sources/MustacheBox.swift | 8 +-
Tests/Public/BoxTests.swift | 514 ++++++++++++++++++-----
Tests/Public/BoxValueTests.swift | 18 -
Xcode/Mustache.xcodeproj/project.pbxproj | 4 -
5 files changed, 492 insertions(+), 208 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index 21a6a34c..dcaa47a3 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -991,13 +991,19 @@ Otherwise, GRMustache logs a warning, and returns the empty box.
- returns: A MustacheBox that wraps *object*.
*/
private func BoxAny(_ object: Any?) -> MustacheBox {
- if let boxable = object as? MustacheBoxable {
+ guard let object = object else {
+ return Box()
+ }
+ switch object {
+ case let boxable as MustacheBoxable:
return boxable.mustacheBox
- } else if let object = object {
+ case let array as [Any?]:
+ return Box(array)
+ case let dictionary as [AnyHashable: Any?]:
+ return Box(dictionary)
+ default:
NSLog("Mustache: value `\(object)` does not conform to MustacheBoxable: it is discarded.")
return Box()
- } else {
- return Box()
}
}
@@ -1140,7 +1146,7 @@ extension Collection where IndexDistance == Int {
*/
fileprivate func mustacheBoxWithSetValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
return MustacheBox(
- converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),
+ converter: MustacheBox.Converter(arrayValue: { self.map({ box($0) }) }),
value: value,
boolValue: !isEmpty,
keyedSubscript: { (key) in
@@ -1190,7 +1196,7 @@ extension BidirectionalCollection where IndexDistance == Int {
*/
fileprivate func mustacheBoxWithArrayValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
return MustacheBox(
- converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),
+ converter: MustacheBox.Converter(arrayValue: { self.map({ box($0) }) }),
value: value,
boolValue: !isEmpty,
keyedSubscript: { (key) in
@@ -1338,12 +1344,12 @@ type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
- returns: A MustacheBox that wraps *array*.
*/
-public func Box(_ set: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
- if let set = set {
- return set.mustacheBoxWithSetValue(set, box: { Box($0) })
- } else {
+public func Box(_ set: Set?) -> MustacheBox {
+//public func Box(_ set: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
+ guard let set = set else {
return Box()
}
+ return set.mustacheBoxWithSetValue(set, box: { BoxAny($0) })
}
/**
@@ -1389,12 +1395,12 @@ type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
- returns: A MustacheBox that wraps *array*.
*/
-public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
- if let array = array {
- return array.mustacheBoxWithArrayValue(array, box: { Box($0) })
- } else {
+public func Box(_ array: [Any?]?) -> MustacheBox {
+//public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
+ guard let array = array else {
return Box()
}
+ return array.mustacheBoxWithArrayValue(array, box: { BoxAny($0) })
}
/**
@@ -1440,13 +1446,20 @@ type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
- returns: A MustacheBox that wraps *array*.
*/
-public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element == Optional, T: MustacheBoxable, C.IndexDistance == Int {
- if let array = array {
- return array.mustacheBoxWithArrayValue(array, box: { Box($0) })
- } else {
- return Box()
- }
-}
+//public func Box(_ array: [T?]?) -> MustacheBox {
+// //public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
+// guard let array = array else {
+// return Box()
+// }
+// return array.mustacheBoxWithArrayValue(array, box: { BoxAny($0) })
+//}
+//public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element == Optional, T: MustacheBoxable, C.IndexDistance == Int {
+// if let array = array {
+// return array.mustacheBoxWithArrayValue(array, box: { Box($0) })
+// } else {
+// return Box()
+// }
+//}
// =============================================================================
@@ -1501,22 +1514,30 @@ dictionary, whatever the actual type of the raw boxed value.
- returns: A MustacheBox that wraps *dictionary*.
*/
-public func Box(_ dictionary: [String: MustacheBoxable]?) -> MustacheBox {
- if let dictionary = dictionary {
- return MustacheBox(
- converter: MustacheBox.Converter(
- dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: MustacheBoxable)) in
- var boxDictionary = boxDictionary
- boxDictionary[item.key] = Box(item.value)
- return boxDictionary
- })),
- value: dictionary,
- keyedSubscript: { (key: String) in
- return Box(dictionary[key])
- })
- } else {
+public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
+ guard let dictionary = dictionary else {
return Box()
}
+ return MustacheBox(
+ converter: MustacheBox.Converter(dictionaryValue: {
+ var boxDictionary: [String: MustacheBox] = [:]
+ for (key, value) in dictionary {
+ if let key = key as? String {
+ boxDictionary[key] = BoxAny(value)
+ } else {
+ NSLog("GRMustache found a non-string key in dictionary (\(key)): value is discarded.")
+ }
+ }
+ return boxDictionary
+ }),
+ value: dictionary,
+ keyedSubscript: { (key: String) in
+ if let value = dictionary[key] {
+ return BoxAny(value)
+ } else {
+ return Box()
+ }
+ })
}
/**
@@ -1567,27 +1588,31 @@ dictionary, whatever the actual type of the raw boxed value.
- returns: A MustacheBox that wraps *dictionary*.
*/
-public func Box(_ dictionary: [String: MustacheBoxable?]?) -> MustacheBox {
- if let dictionary = dictionary {
- return MustacheBox(
- converter: MustacheBox.Converter(
- dictionaryValue: dictionary.reduce([String: MustacheBox](), { (boxDictionary, item: (key: String, value: MustacheBoxable?)) in
- var boxDictionary = boxDictionary
- boxDictionary[item.key] = Box(item.value)
- return boxDictionary
- })),
- value: dictionary,
- keyedSubscript: { (key: String) in
- if let value = dictionary[key] {
- return Box(value)
- } else {
- return Box()
- }
- })
- } else {
- return Box()
- }
-}
+//public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
+// guard let dictionary = dictionary else {
+// return Box()
+// }
+// return MustacheBox(
+// converter: MustacheBox.Converter(dictionaryValue: {
+// var boxDictionary: [String: MustacheBox] = [:]
+// for (key, value) in dictionary {
+// if let key = key as? String {
+// boxDictionary[key] = BoxAny(value)
+// } else {
+// NSLog("GRMustache found a non-string key in dictionary (\(key)): value is discarded.")
+// }
+// }
+// return boxDictionary
+// }),
+// value: dictionary,
+// keyedSubscript: { (key: String) in
+// if let value = dictionary[key] {
+// return BoxAny(value)
+// } else {
+// return Box()
+// }
+// })
+//}
/**
@@ -1647,16 +1672,15 @@ extension NSDictionary {
*/
public override var mustacheBox: MustacheBox {
return MustacheBox(
- converter: MustacheBox.Converter(
- dictionaryValue: IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
- var boxDictionary = boxDictionary
- if let key = key as? String {
- boxDictionary[key] = BoxAny(self[key])
- } else {
- NSLog("GRMustache found a non-string key in NSDictionary (\(key)): value is discarded.")
- }
- return boxDictionary
- })),
+ converter: MustacheBox.Converter(dictionaryValue: { IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
+ var boxDictionary = boxDictionary
+ if let key = key as? String {
+ boxDictionary[key] = BoxAny(self[key])
+ } else {
+ NSLog("GRMustache found a non-string key in NSDictionary (\(key)): value is discarded.")
+ }
+ return boxDictionary
+ })}),
value: self,
keyedSubscript: { BoxAny(self[$0])
})
diff --git a/Sources/MustacheBox.swift b/Sources/MustacheBox.swift
index cd14ece2..e237db55 100644
--- a/Sources/MustacheBox.swift
+++ b/Sources/MustacheBox.swift
@@ -512,11 +512,11 @@ final public class MustacheBox : NSObject {
let dictionaryValue: (() -> [String: MustacheBox]?)
init(
- arrayValue: @autoclosure @escaping () -> [MustacheBox]? = nil,
- dictionaryValue: @autoclosure @escaping () -> [String: MustacheBox]? = nil)
+ arrayValue: (() -> [MustacheBox])? = nil,
+ dictionaryValue: (() -> [String: MustacheBox]?)? = nil)
{
- self.arrayValue = arrayValue
- self.dictionaryValue = dictionaryValue
+ self.arrayValue = arrayValue ?? { nil }
+ self.dictionaryValue = dictionaryValue ?? { nil }
}
}
}
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index fd623e2e..30eaa4dd 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -24,22 +24,33 @@
import XCTest
import Mustache
-struct HashableBoxable : MustacheBoxable, Hashable {
+private struct CustomHashableBoxable : MustacheBoxable, Hashable {
let int: Int
+ init(_ int: Int) { self.int = int }
var hashValue: Int { return int }
var mustacheBox: MustacheBox { return Box(int) }
+
+ static func ==(lhs: CustomHashableBoxable, rhs: CustomHashableBoxable) -> Bool {
+ return lhs.int == rhs.int
+ }
}
-func ==(lhs: HashableBoxable, rhs: HashableBoxable) -> Bool {
- return lhs.int == rhs.int
+private struct CustomBoxable : MustacheBoxable {
+ let int: Int
+ init(_ int: Int) { self.int = int }
+ var hashValue: Int { return int }
+ var mustacheBox: MustacheBox { return Box(int) }
}
class BoxTests: XCTestCase {
+
+ // MARK: - Box(MustacheBoxable)
+
func testInt() {
do {
// Explicit type
- let value: Int = 0
+ let value: Int = 1
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -47,7 +58,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable
- let value: MustacheBoxable = 0
+ let value: MustacheBoxable = 1
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -55,7 +66,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = 0
+ let value = 1
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -64,24 +75,24 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{.}}")
- let box = Box(0)
+ let box = Box(1)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
- func testOptionalInt() {
+ func testCustomBoxable() {
do {
// Explicit type
- let value: Int? = 0
+ let value: CustomBoxable = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
do {
- // As MustacheBoxable?
- let value: MustacheBoxable? = 0
+ // As MustacheBoxable
+ let value: MustacheBoxable = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -89,7 +100,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = Optional.some(0)
+ let value = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -98,24 +109,27 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{.}}")
- let box = Box(Optional.some(0))
+ let box = Box(CustomBoxable(1))
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
- func testMustacheBoxable() {
+
+ // MARK: - Box(MustacheBoxable?)
+
+ func testOptionalInt() {
do {
// Explicit type
- let value: HashableBoxable = HashableBoxable(int:0)
+ let value: Int? = 1
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
do {
- // As MustacheBoxable
- let value: MustacheBoxable = HashableBoxable(int:0)
+ // As MustacheBoxable?
+ let value: MustacheBoxable? = 1
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -123,7 +137,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = HashableBoxable(int:0)
+ let value = 1 as Int?
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -132,16 +146,16 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{.}}")
- let box = Box(HashableBoxable(int:0))
+ let box = Box(1 as Int?)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
-
+
func testOptionalMustacheBoxable() {
do {
// Explicit type
- let value: HashableBoxable? = HashableBoxable(int:0)
+ let value: CustomBoxable? = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -149,7 +163,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable?
- let value: MustacheBoxable? = HashableBoxable(int:0)
+ let value: MustacheBoxable? = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -157,7 +171,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = Optional.some(HashableBoxable(int:0))
+ let value = CustomBoxable(1) as CustomBoxable?
let template = try! Template(string: "{{.}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -166,12 +180,15 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{.}}")
- let box = Box(Optional.some(HashableBoxable(int:0)))
+ let box = Box(CustomBoxable(1) as CustomBoxable?)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
+
+ // MARK: - Box(Set)
+
func testSetOfInt() {
do {
// Explicit type
@@ -189,12 +206,19 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([0,1,2] as Set)
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
}
- func testSetOfMustacheBoxable() {
+ func testSetOfCustomHashableBoxable() {
do {
// Explicit type
- let value: Set = [HashableBoxable(int:0),HashableBoxable(int:1),HashableBoxable(int:2)]
+ let value: Set = [CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -202,18 +226,28 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Set = [HashableBoxable(int:0),HashableBoxable(int:1),HashableBoxable(int:2)]
+ let value: Set = [CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)] as Set)
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
}
- func testDictionaryOfInt() {
+
+ // MARK: - Box([String: MustacheBoxable])
+
+ func testDictionaryOfStringInt() {
do {
// Explicit type
- let value: Dictionary = ["name": 0]
+ let value: [String: Int] = ["name": 1]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -221,7 +255,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable
- let value: Dictionary = ["name": 0]
+ let value: [String: MustacheBoxable] = ["name": 1]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -229,7 +263,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Dictionary = ["name": 0]
+ let value: Dictionary = ["name": 1]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -237,7 +271,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = ["name": 0]
+ let value = ["name": 1]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -246,24 +280,24 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": 0])
+ let box = Box(["name": 1])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
- func testDictionaryOfOptionalInt() {
+ func testDictionaryOfStringCustomBoxable() {
do {
// Explicit type
- let value: Dictionary = ["name": 0]
+ let value: [String: CustomBoxable] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
do {
- // As MustacheBoxable?
- let value: Dictionary = ["name": Optional.some(0)]
+ // As MustacheBoxable
+ let value: [String: MustacheBoxable] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -271,7 +305,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Dictionary = ["name": Optional.some(0)]
+ let value: Dictionary = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -279,7 +313,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = ["name": Optional.some(0)]
+ let value = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -288,24 +322,27 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": Optional.some(0)])
+ let box = Box(["name": CustomBoxable(1)])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
- func testDictionaryOfMustacheBoxable() {
+
+ // MARK: - Box([String: MustacheBoxable?])
+
+ func testDictionaryOfStringOptionalInt() {
do {
// Explicit type
- let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let value: [String: Int?] = ["name": 1]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
do {
- // As MustacheBoxable
- let value: Dictionary = ["name": HashableBoxable(int:0)]
+ // As MustacheBoxable?
+ let value: [String: MustacheBoxable?] = ["name": 1 as Int?]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -313,7 +350,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let value: Dictionary = ["name": 1 as Int?]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -321,7 +358,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = ["name": HashableBoxable(int:0)]
+ let value = ["name": 1 as Int?]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -330,16 +367,16 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": HashableBoxable(int:0)])
+ let box = Box(["name": 1 as Int?])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
- func testDictionaryOfOptionalMustacheBoxable() {
+ func testDictionaryOfStringOptionalCustomBoxable() {
do {
// Explicit type
- let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let value: [String: CustomBoxable?] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -347,7 +384,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable?
- let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let value: [String: MustacheBoxable?] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -355,7 +392,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Dictionary = ["name": HashableBoxable(int:0)]
+ let value: Dictionary = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -363,7 +400,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = ["name": HashableBoxable(int:0)]
+ let value = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -372,58 +409,222 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": HashableBoxable(int:0)])
+ let box = Box(["name": CustomBoxable(1)])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
}
- func testDictionaryOfAny() {
+
+ // MARK: - Box([String: Any])
+
+ func testDictionaryOfStringAny() {
do {
// Explicit type
- let value: Dictionary = ["int": 1, "string": "foo"]
+ let value: [String: Any] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
do {
- // As MustacheBoxable
- let value: Dictionary = ["int": 1, "string": "foo"]
+ // As MustacheBoxable (won't compile)
+// let value: [String: MustacheBoxable & Hashable] = ["int": 1, "string": "foo"]
+// let template = try! Template(string: "{{int}}, {{string}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered element type (won't compile)
+// let value: Dictionary = ["int": 1, "string": "foo"]
+// let template = try! Template(string: "{{int}}, {{string}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type (won't compile)
+// let value = ["int": 1, "string": "foo"]
+// let template = try! Template(string: "{{int}}, {{string}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(["int": 1, "string": "foo"])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ }
+
+
+ // MARK: - Box([String: Any?])
+
+ func testDictionaryOfStringOptionalAny() {
+ do {
+ // Explicit type
+ let value: [String: Any?] = ["int": 1, "string": "foo", "missing": nil]
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // As MustacheBoxable?
+ let value: [String: MustacheBoxable?] = ["int": 1, "string": "foo", "missing": nil]
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // Infered element type (won't compile)
+// let value: Dictionary = ["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?]
+// let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // Infered type (won't compile)
+// let value = ["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?]
+// let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo, ")
+ }
+ do {
+ // Infered type
+ let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+ let box = Box(["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
+ }
+
+
+ // MARK: - Box([AnyHashable: Any])
+
+ func testDictionaryOfAnyHashableAny() {
+ do {
+ // Explicit type
+ let value: [AnyHashable: Any] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
do {
- // Infered element type
- let value: Dictionary = ["int": 1, "string": "foo"]
+ // As MustacheBoxable
+ let value: [AnyHashable: MustacheBoxable] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
+ do {
+ // Infered element type (won't compile)
+// let value: Dictionary = [AnyHashable("int"): 1, AnyHashable("string"): "foo"]
+// let template = try! Template(string: "{{int}}, {{string}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type (won't compile)
+// let value = [AnyHashable("int"): 1, AnyHashable("string"): "foo"]
+// let template = try! Template(string: "{{int}}, {{string}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo")
+ }
do {
// Infered type
- let value = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
+ let box = Box([AnyHashable("int"): 1, AnyHashable("string"): "foo"])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
+ }
+
+ func testDictionaryOfArray() {
+ do {
+ // Explicit type
+ let value: [AnyHashable: Any] = ["int": [1, 2], "string": ["foo", "bar"]]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "12, foobar")
+ }
+ do {
+ // Infered element type (won't compile)
+ let value: Dictionary = ["int": [1, 2], "string": ["foo", "bar"]]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "12, foobar")
+ }
+ do {
+ // Infered type (won't compile)
+ let value = ["int": [1, 2], "string": ["foo", "bar"]]
+ let template = try! Template(string: "{{int}}, {{string}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "12, foobar")
+ }
do {
// Infered type
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(["int": 1, "string": "foo"])
+ let box = Box(["int": [1, 2], "string": ["foo", "bar"]])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "12, foobar")
+ }
+ }
+
+ func testDictionaryOfDictionary() {
+ do {
+ // Explicit type
+ let value: [AnyHashable: Any] = ["int": ["name": 1], "string": ["name": "foo"]]
+ let template = try! Template(string: "{{int.name}}, {{string.name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered element type (won't compile)
+ let value: Dictionary = ["int": ["name": 1], "string": ["name": "foo"]]
+ let template = try! Template(string: "{{int.name}}, {{string.name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type (won't compile)
+ let value = ["int": ["name": 1], "string": ["name": "foo"]]
+ let template = try! Template(string: "{{int.name}}, {{string.name}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
+ do {
+ // Infered type
+ let template = try! Template(string: "{{int.name}}, {{string.name}}")
+ let box = Box(["int": ["name": 1], "string": ["name": "foo"]])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
}
- func testDictionaryOfOptionalAny() {
+
+ // MARK: - Box([AnyHashable: Any?])
+
+ func testDictionaryOfAnyHashableOptionalAny() {
do {
// Explicit type
- let value: Dictionary = ["int": 1, "string": "foo", "missing": nil]
+ let value: [AnyHashable: Any?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -431,7 +632,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable?
- let value: Dictionary = ["int": 1, "string": "foo", "missing": nil]
+ let value: [AnyHashable: MustacheBoxable?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -439,7 +640,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Dictionary = ["int": Optional.some(1), "string": Optional.some("foo"), "missing": nil]
+ let value: Dictionary = [AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -447,25 +648,28 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = ["int": Optional.some(1), "string": Optional.some("foo"), "missing": nil]
+ let value = [AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
- // Infered type
- let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(["int": Optional.some(1), "string": Optional.some("foo"), "missing": nil])
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "1, foo, ")
+ // Infered type (won't compile)
+// let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
+// let box = Box([AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil])
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "1, foo, ")
}
}
+
+ // MARK: - Box([MustacheBoxable])
+
func testArrayOfInt() {
do {
// Explicit type
- let value: Array = [0,1,2,3]
+ let value: [Int] = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -473,7 +677,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable
- let value: Array = [0,1,2,3]
+ let value: [MustacheBoxable] = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -504,18 +708,18 @@ class BoxTests: XCTestCase {
}
}
- func testArrayOfOptionalInt() {
+ func testArrayOfCustomBoxable() {
do {
// Explicit type
- let value: Array = [0,1,2,3,nil]
+ let value: [CustomBoxable] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
do {
- // As MustacheBoxable?
- let value: Array = [0,1,2,3,nil]
+ // As MustacheBoxable
+ let value: [MustacheBoxable] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -523,15 +727,15 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Array = [Optional.some(0),Optional.some(1),Optional.some(2),Optional.some(3),Optional.none]
+ let value: Array = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
do {
- // Infered type: won't compile
- let value = [Optional.some(0),Optional.some(1),Optional.some(2),Optional.some(3),Optional.none]
+ // Infered type
+ let value = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -540,24 +744,27 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0,1,2,3,nil])
+ let box = Box([CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
}
- func testArrayOfMustacheBoxable() {
+
+ // MARK: - Box([MustacheBoxable?])
+
+ func testArrayOfOptionalInt() {
do {
// Explicit type
- let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let value: [Int?] = [0,1,2,3,nil]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
do {
- // As MustacheBoxable
- let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ // As MustacheBoxable?
+ let value: [MustacheBoxable?] = [0,1,2,3,nil]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -565,7 +772,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let value: Array = [0 as Int?, 1 as Int?, 2 as Int?,3 as Int?, nil as Int?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -573,7 +780,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let value = [0 as Int?, 1 as Int?, 2 as Int?, 3 as Int?, nil as Int?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -582,16 +789,16 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)])
+ let box = Box([0 as Int?, 1 as Int?, 2 as Int?, 3 as Int?, nil as Int?])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
}
- func testArrayOfOptionalMustacheBoxable() {
+ func testArrayOfOptionalCustomBoxable() {
do {
// Explicit type
- let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let value: [CustomBoxable?] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -599,7 +806,7 @@ class BoxTests: XCTestCase {
}
do {
// As MustacheBoxable?
- let value: Array = [HashableBoxable(int:0), HashableBoxable(int:1), HashableBoxable(int:2), HashableBoxable(int:3)]
+ let value: [MustacheBoxable?] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -607,7 +814,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Array = [Optional.some(HashableBoxable(int:0)), Optional.some(HashableBoxable(int:1)), Optional.some(HashableBoxable(int:2)), Optional.some(HashableBoxable(int:3))]
+ let value: Array = [CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -615,7 +822,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = [Optional.some(HashableBoxable(int:0)), Optional.some(HashableBoxable(int:1)), Optional.some(HashableBoxable(int:2)), Optional.some(HashableBoxable(int:3))]
+ let value = [CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -624,24 +831,53 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([Optional.some(HashableBoxable(int:0)), Optional.some(HashableBoxable(int:1)), Optional.some(HashableBoxable(int:2)), Optional.some(HashableBoxable(int:3))])
+ let box = Box([CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
}
+
+ // MARK: - Box([Any])
+
func testArrayOfAny() {
do {
// Explicit type
- let value: Array = [0,"foo"]
+ let value: [Any] = [0,"foo"]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
do {
- // As MustacheBoxable
- let value: Array = [0,"foo"]
+ // Infered element type (won't compile)
+// let value: Array = [0,"foo"]
+// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Infered type (won't compile)
+// let value = [0,"foo"]
+// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let box = Box([0,"foo"])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
+ }
+
+ func testArrayOfArray() {
+ do {
+ // Explicit type
+ let value: [Any] = [[0,"foo"]]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -649,7 +885,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered element type
- let value: Array = [0,"foo"]
+ let value: Array = [[0,"foo"]]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -657,7 +893,7 @@ class BoxTests: XCTestCase {
}
do {
// Infered type
- let value = [0,"foo"]
+ let value = [[0,"foo"]]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
@@ -666,62 +902,105 @@ class BoxTests: XCTestCase {
do {
// Direct Box argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0,"foo"])
+ let box = Box([[0,"foo"]])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
}
- func testArrayOfOptionalAny() {
+ func testArrayOfDictionary() {
do {
// Explicit type
- let value: Array = [0,nil,"foo"]
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ let value: [Any] = [["name": 1]]
+ let template = try! Template(string: "{{#.}}{{name}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "0foo")
+ XCTAssertEqual(rendering, "1")
}
do {
- // As MustacheBoxable?
- let value: Array = [0,nil,"foo"]
- let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+ // Infered element type (won't compile)
+ let value: Array = [["name": 1]]
+ let template = try! Template(string: "{{#.}}{{name}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "0foo")
+ XCTAssertEqual(rendering, "1")
}
do {
- // Infered element type
- let value: Array = [Optional.some(0),nil,Optional.some("foo")]
+ // Infered type
+ let value = [["name": 1]]
+ let template = try! Template(string: "{{#.}}{{name}}{{/}}")
+ let box = Box(value)
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ do {
+ // Direct Box argument
+ let template = try! Template(string: "{{#.}}{{name}}{{/}}")
+ let box = Box([["name": 1]])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
+ }
+
+
+ // MARK: - Box([Any?])
+
+ func testArrayOfOptionalAny() {
+ do {
+ // Explicit type
+ let value: [Any?] = [0,nil,"foo"]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
do {
- // Infered type
- let value = [Optional.some(0),nil,Optional.some("foo")]
+ // As MustacheBoxable?
+ let value: [MustacheBoxable?] = [0,nil,"foo"]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
let box = Box(value)
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
+ do {
+ // Infered element type (won't compile)
+// let value: Array = [0 as Int?, nil as CustomBoxable?, "foo" as String?]
+// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "0foo")
+ }
+ do {
+ // Infered type (won't compile)
+// let value = [0 as Int?, nil as CustomBoxable?, "foo" as String?]
+// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
+// let box = Box(value)
+// let rendering = try! template.render(box)
+// XCTAssertEqual(rendering, "0foo")
+ }
do {
// Direct Box argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([Optional.some(0),nil,Optional.some("foo")])
+ let box = Box([0 as Int?, nil as CustomBoxable?, "foo" as String?])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
}
+
+ // MARK: - Box([non boxable])
+
func testArrayOfNonMustacheBoxable() {
class Class { }
- let array: Array = [Class()]
+ let array: [Any] = [Class()]
let context = Context(Box(array))
let box = context.mustacheBoxForKey("first")
XCTAssertTrue(box.value == nil)
}
+
+ // MARK: - Box(NSArray)
+
func testNSArrayOfInt() {
let value: NSArray = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
@@ -755,10 +1034,13 @@ class BoxTests: XCTestCase {
XCTAssertTrue(box.value == nil)
}
+
+ // MARK: - Box(Range)
+
func testRange() {
let value = 0..<10
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
+ let box = Box(Array(value))
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123456789")
}
diff --git a/Tests/Public/BoxValueTests.swift b/Tests/Public/BoxValueTests.swift
index 22279efa..494e4173 100644
--- a/Tests/Public/BoxValueTests.swift
+++ b/Tests/Public/BoxValueTests.swift
@@ -115,24 +115,6 @@ class BoxValueTests: XCTestCase {
XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
}
- func testArrayValueForCollectionOfOne() {
- let originalValue = CollectionOfOne(123)
- let box = Box(originalValue)
- let extractedValue = box.value as! CollectionOfOne
- XCTAssertEqual(extractedValue[extractedValue.startIndex], originalValue[originalValue.startIndex])
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! Int }, [123])
- }
-
- func testArrayValueForRange() {
- let originalValue = 1...3
- let box = Box(originalValue)
- let extractedValue = box.value as! CountableClosedRange
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
- }
-
func testDictionaryValueForNSDictionary() {
let originalValue = NSDictionary(object: "value", forKey: "key" as NSCopying)
let box = Box(originalValue)
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index 0191227e..b39d696e 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -1489,7 +1489,6 @@
PRODUCT_NAME = Mustache;
SDKROOT = macosx;
SKIP_INSTALL = YES;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
};
name = Debug;
};
@@ -1512,7 +1511,6 @@
PRODUCT_NAME = Mustache;
SDKROOT = macosx;
SKIP_INSTALL = YES;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
};
name = Release;
};
@@ -1669,7 +1667,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Mustache;
SKIP_INSTALL = YES;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -1692,7 +1689,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Mustache;
SKIP_INSTALL = YES;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
From 7d6219f30f0540d7cbb84469039c6096ed4a7ec1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Fri, 21 Oct 2016 13:49:19 +0200
Subject: [PATCH 08/46] #32: All targets use Swift 3
---
Xcode/Mustache.xcodeproj/project.pbxproj | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index b39d696e..be57c5f0 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -1599,7 +1599,7 @@
OTHER_SWIFT_FLAGS = "-DOBJC";
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 2.3;
+ SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@@ -1643,7 +1643,7 @@
OTHER_SWIFT_FLAGS = "-DOBJC";
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
- SWIFT_VERSION = 2.3;
+ SWIFT_VERSION = 3.0;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -1667,7 +1667,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Mustache;
SKIP_INSTALL = YES;
- SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -1689,7 +1688,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Mustache;
SKIP_INSTALL = YES;
- SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@@ -1710,7 +1708,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -1727,7 +1724,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.github.groue.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
From 3b20aff109ed9a03b37295e0424e0a0fac769e55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Fri, 21 Oct 2016 13:51:08 +0200
Subject: [PATCH 09/46] Box() cleanup
---
Sources/Box.swift | 1856 ++++------------------
Sources/CoreGraphics.swift | 75 +
Sources/Foundation.swift | 407 +++++
Sources/SwiftStandardLibrary.swift | 416 +++++
Xcode/Mustache.xcodeproj/project.pbxproj | 32 +
5 files changed, 1237 insertions(+), 1549 deletions(-)
create mode 100644 Sources/CoreGraphics.swift
create mode 100644 Sources/Foundation.swift
create mode 100644 Sources/SwiftStandardLibrary.swift
diff --git a/Sources/Box.swift b/Sources/Box.swift
index dcaa47a3..dc6267ce 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -23,978 +23,92 @@
import Foundation
-// "It's all boxes all the way down."
-//
-// Mustache templates don't eat raw values: they eat boxed values.
-//
-// To box something, you use the `Box()` function. It comes in several variants
-// so that nearly anything can be boxed and feed templates:
-//
-// let value = ...
-// template.render(Box(value))
-//
-// This file is organized in five sections with many examples. You can use the
-// Playground included in `Mustache.xcworkspace` to run those examples.
-//
-//
-// - MustacheBoxable and the Boxing of Value Types
-//
-// The `MustacheBoxable` protocol lets any type describe how it interacts with
-// the Mustache rendering engine.
-//
-// It is adopted by the standard types Bool, Int, UInt, Double, String, and
-// NSObject.
-//
-//
-// - Boxing of Collections
-//
-// Learn how Array and Set are rendered.
-//
-//
-// - Boxing of Dictionaries
-//
-// Learn how Dictionary and NSDictionary are rendered.
-//
-//
-// - Boxing of Core Mustache functions
-//
-// The "core Mustache functions" are raw filters, Mustache lambdas, etc. Those
-// can be boxed as well so that you can feed templates with them.
-//
-//
-// - Boxing of multi-facetted values
-//
-// Describes the most advanced `Box()` function.
-
-
-// =============================================================================
-// MARK: - MustacheBoxable and the Boxing of Value Types
-
-/**
-The MustacheBoxable protocol gives any type the ability to feed Mustache
-templates.
-
-It is adopted by the standard types Bool, Int, UInt, Double, String, and
-NSObject.
-
-Your own types can conform to it as well, so that they can feed templates:
-
- extension Profile: MustacheBoxable { ... }
-
- let profile = ...
- let template = try! Template(named: "Profile")
- let rendering = try! template.render(Box(profile))
-*/
+/// The MustacheBoxable protocol gives any type the ability to feed Mustache
+/// templates.
+///
+/// It is adopted by the standard types Bool, Int, UInt, Double, String, and
+/// NSObject.
+///
+/// Your own types can conform to it as well, so that they can feed templates:
+///
+/// extension Profile: MustacheBoxable { ... }
+///
+/// let profile = ...
+/// let template = try! Template(named: "Profile")
+/// let rendering = try! template.render(Box(profile))
public protocol MustacheBoxable {
- /**
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- value.mustacheBox // Valid, but discouraged
- Box(value) // Preferred
-
- Return a `MustacheBox` that describes how your type interacts with the
- rendering engine.
-
- You can for example box another value that is already boxable, such as
- dictionaries:
-
- struct Person {
- let firstName: String
- let lastName: String
- }
-
- extension Person : MustacheBoxable {
- // Expose the `firstName`, `lastName` and `fullName` keys to
- // Mustache templates:
- var mustacheBox: MustacheBox {
- return Box([
- "firstName": firstName,
- "lastName": lastName,
- "fullName": "\(self.firstName) \(self.lastName)",
- ])
- }
- }
-
- let person = Person(firstName: "Tom", lastName: "Selleck")
-
- // Renders "Tom Selleck"
- let template = try! Template(string: "{{person.fullName}}")
- try! template.render(Box(["person": Box(person)]))
-
- However, there are multiple ways to build a box, several `Box()` functions.
- See their documentations.
- */
- var mustacheBox: MustacheBox { get }
-}
-
-// IMPLEMENTATION NOTE
-//
-// This protocol conformance is not only a matter of consistency. It is also
-// a convenience for the library implementation: it makes arrays
-// [MustacheBox] boxable via Box(collection: C?)
-// and dictionaries [String:MustacheBox] boxable via Box(dictionary: [String: T]?)
-
-extension MustacheBox {
-
- /**
- `MustacheBox` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates. Its mustacheBox property returns itself.
- */
- public override var mustacheBox: MustacheBox {
- return self
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `Bool`.
-*/
-
-extension Bool : MustacheBoxable {
-
- /**
- `Bool` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- true.mustacheBox // Valid, but discouraged
- Box(true) // Preferred
-
-
- ### Rendering
-
- - `{{bool}}` renders as `0` or `1`.
-
- - `{{#bool}}...{{/bool}}` renders if and only if `bool` is true.
-
- - `{{^bool}}...{{/bool}}` renders if and only if `bool` is false.
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: self,
- render: { (info: RenderingInfo) in
- switch info.tag.type {
- case .variable:
- // {{ bool }}
- return Rendering("\(self ? 1 : 0)") // Behave like [NSNumber numberWithBool:]
- case .section:
- if info.enumerationItem {
- // {{# bools }}...{{/ bools }}
- return try info.tag.render(info.context.extendedContext(Box(self)))
- } else {
- // {{# bool }}...{{/ bool }}
- //
- // Bools do not enter the context stack when used in a
- // boolean section.
- //
- // This behavior must not change:
- // https://github.com/groue/GRMustache/issues/83
- return try info.tag.render(info.context)
- }
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `Int64`.
-*/
-
-extension Int64 : MustacheBoxable {
-
- /**
- `Int64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{int}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
-
- - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: (self != 0),
- render: { (info: RenderingInfo) in
- switch info.tag.type {
- case .variable:
- // {{ int }}
- return Rendering("\(self)")
- case .section:
- if info.enumerationItem {
- // {{# ints }}...{{/ ints }}
- return try info.tag.render(info.context.extendedContext(Box(self)))
- } else {
- // {{# int }}...{{/ int }}
- //
- // Ints do not enter the context stack when used in a
- // boolean section.
- //
- // This behavior must not change:
- // https://github.com/groue/GRMustache/issues/83
- return try info.tag.render(info.context)
- }
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `Int`.
-*/
-
-extension Int : MustacheBoxable {
-
- /**
- `Int` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{int}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
-
- - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: (self != 0),
- render: { (info: RenderingInfo) in
- switch info.tag.type {
- case .variable:
- // {{ int }}
- return Rendering("\(self)")
- case .section:
- if info.enumerationItem {
- // {{# ints }}...{{/ ints }}
- return try info.tag.render(info.context.extendedContext(Box(self)))
- } else {
- // {{# int }}...{{/ int }}
- //
- // Ints do not enter the context stack when used in a
- // boolean section.
- //
- // This behavior must not change:
- // https://github.com/groue/GRMustache/issues/83
- return try info.tag.render(info.context)
- }
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `UInt64`.
-*/
-
-extension UInt64 : MustacheBoxable {
-
- /**
- `UInt64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{uint}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
-
- - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: (self != 0),
- render: { (info: RenderingInfo) in
- switch info.tag.type {
- case .variable:
- // {{ uint }}
- return Rendering("\(self)")
- case .section:
- if info.enumerationItem {
- // {{# uints }}...{{/ uints }}
- return try info.tag.render(info.context.extendedContext(Box(self)))
- } else {
- // {{# uint }}...{{/ uint }}
- //
- // Uints do not enter the context stack when used in a
- // boolean section.
- //
- // This behavior must not change:
- // https://github.com/groue/GRMustache/issues/83
- return try info.tag.render(info.context)
- }
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `UInt`.
-*/
-
-extension UInt : MustacheBoxable {
-
- /**
- `UInt` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{uint}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
-
- - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: (self != 0),
- render: { (info: RenderingInfo) in
- switch info.tag.type {
- case .variable:
- // {{ uint }}
- return Rendering("\(self)")
- case .section:
- if info.enumerationItem {
- // {{# uints }}...{{/ uints }}
- return try info.tag.render(info.context.extendedContext(Box(self)))
- } else {
- // {{# uint }}...{{/ uint }}
- //
- // Uints do not enter the context stack when used in a
- // boolean section.
- //
- // This behavior must not change:
- // https://github.com/groue/GRMustache/issues/83
- return try info.tag.render(info.context)
- }
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `Double`.
-*/
-
-extension Double : MustacheBoxable {
-
- /**
- `Double` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 3.14.mustacheBox // Valid, but discouraged
- Box(3.14) // Preferred
-
-
- ### Rendering
-
- - `{{double}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#double}}...{{/double}}` renders if and only if `double` is not 0 (zero).
-
- - `{{^double}}...{{/double}}` renders if and only if `double` is 0 (zero).
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: (self != 0.0),
- render: { (info: RenderingInfo) in
- switch info.tag.type {
- case .variable:
- // {{ double }}
- return Rendering("\(self)")
- case .section:
- if info.enumerationItem {
- // {{# doubles }}...{{/ doubles }}
- return try info.tag.render(info.context.extendedContext(Box(self)))
- } else {
- // {{# double }}...{{/ double }}
- //
- // Doubles do not enter the context stack when used in a
- // boolean section.
- //
- // This behavior must not change:
- // https://github.com/groue/GRMustache/issues/83
- return try info.tag.render(info.context)
- }
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `String`.
-*/
-
-extension String : MustacheBoxable {
-
- /**
- `String` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- "foo".mustacheBox // Valid, but discouraged
- Box("foo") // Preferred
-
-
- ### Rendering
-
- - `{{string}}` renders the string, HTML-escaped.
-
- - `{{{string}}}` renders the string, *not* HTML-escaped.
-
- - `{{#string}}...{{/string}}` renders if and only if `string` is not empty.
-
- - `{{^string}}...{{/string}}` renders if and only if `string` is empty.
-
- HTML-escaping of `{{string}}` tags is disabled for Text templates: see
- `Configuration.contentType` for a full discussion of the content type of
- templates.
-
-
- ### Keys exposed to templates
-
- A string can be queried for the following keys:
-
- - `length`: the number of characters in the string.
-
- */
- public var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: (self.characters.count > 0),
- keyedSubscript: { (key: String) in
- switch key {
- case "length":
- return Box(self.characters.count)
- default:
- return Box()
- }
- })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `NSObject`.
-*/
-
-extension NSObject : MustacheBoxable {
-
- /**
- `NSObject` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- object.mustacheBox // Valid, but discouraged
- Box(object) // Preferred
-
-
- NSObject's default implementation handles two general cases:
-
- - Enumerable objects that conform to the `NSFastEnumeration` protocol, such
- as `NSArray` and `NSOrderedSet`.
- - All other objects
-
- GRMustache ships with a few specific classes that escape the general cases
- and provide their own rendering behavior: `NSDictionary`, `NSFormatter`,
- `NSNull`, `NSNumber`, `NSString`, and `NSSet` (see the documentation for
- those classes).
-
- Your own subclasses of NSObject can also override the `mustacheBox` method
- and provide their own custom behavior.
-
-
- ## Arrays
-
- An object is treated as an array if it conforms to `NSFastEnumeration`. This
- is the case of `NSArray` and `NSOrderedSet`, for example. `NSDictionary` and
- `NSSet` have their own custom Mustache rendering: see their documentation
- for more information.
-
-
- ### Rendering
-
- - `{{array}}` renders the concatenation of the renderings of the array items.
-
- - `{{#array}}...{{/array}}` renders as many times as there are items in
- `array`, pushing each item on its turn on the top of the context stack.
-
- - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
-
-
- ### Keys exposed to templates
-
- An array can be queried for the following keys:
-
- - `count`: number of elements in the array
- - `first`: the first object in the array
- - `last`: the last object in the array
-
- Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders
- once, if and only if `array` is not empty.
-
-
- ## Other objects
-
- Other objects fall in the general case.
-
- Their keys are extracted with the `valueForKey:` method, as long as the key
- is a property name, a custom property getter, or the name of a
- `NSManagedObject` attribute.
-
-
- ### Rendering
-
- - `{{object}}` renders the result of the `description` method, HTML-escaped.
-
- - `{{{object}}}` renders the result of the `description` method, *not*
- HTML-escaped.
-
- - `{{#object}}...{{/object}}` renders once, pushing `object` on the top of
- the context stack.
-
- - `{{^object}}...{{/object}}` does not render.
-
- */
- public var mustacheBox: MustacheBox {
- if let enumerable = self as? NSFastEnumeration {
- // Enumerable
-
- // Turn enumerable into a Swift array of MustacheBoxes that we know how to box
- let array = IteratorSequence(NSFastEnumerationIterator(enumerable)).map(BoxAny)
- return array.mustacheBoxWithArrayValue(self, box: { $0 })
-
- } else {
- // Generic NSObject
-
- #if OBJC
- return MustacheBox(
- value: self,
- keyedSubscript: { (key: String) in
- if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
- // Use valueForKey: for safe keys
- return BoxAny(self.value(forKey: key))
- } else {
- // Missing key
- return Box()
- }
- })
- #else
- return MustacheBox(value: self)
- #endif
- }
- }
-}
-
-
-/// Support for Mustache rendering of ReferenceConvertible types.
-extension ReferenceConvertible where Self: MustacheBoxable {
- /// Returns a MustacheBox that behaves like the equivalent NSObject.
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
///
- /// See NSObject.mustacheBox
- public var mustacheBox: MustacheBox {
- if let object = self as? ReferenceType {
- return object.mustacheBox
- } else {
- NSLog("Value `\(self)` can not feed Mustache templates: it is discarded.")
- return Box()
- }
- }
-}
-
-/// Data can feed Mustache templates.
-extension Data : MustacheBoxable { }
-
-/// Date can feed Mustache templates.
-extension Date : MustacheBoxable { }
-
-/// DateComponents can feed Mustache templates.
-extension DateComponents : MustacheBoxable { }
-
-/// IndexPath can feed Mustache templates.
-extension IndexPath : MustacheBoxable { }
-
-/// IndexSet can feed Mustache templates.
-extension IndexSet : MustacheBoxable { }
-
-/// URL can feed Mustache templates.
-extension URL : MustacheBoxable { }
-
-/// URLComponents can feed Mustache templates.
-extension URLComponents : MustacheBoxable { }
-
-/// URLQueryItem can feed Mustache templates.
-extension URLQueryItem : MustacheBoxable { }
-
-/// URLRequest can feed Mustache templates.
-extension URLRequest : MustacheBoxable { }
-
-/// UUID can feed Mustache templates.
-extension UUID : MustacheBoxable { }
-
-/**
-GRMustache provides built-in support for rendering `NSNull`.
-*/
-
-extension NSNull {
-
- /**
- `NSNull` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- NSNull().mustacheBox // Valid, but discouraged
- Box(NSNull()) // Preferred
-
-
- ### Rendering
-
- - `{{null}}` does not render.
-
- - `{{#null}}...{{/null}}` does not render (NSNull is falsey).
-
- - `{{^null}}...{{/null}}` does render (NSNull is falsey).
- */
- public override var mustacheBox: MustacheBox {
- return MustacheBox(
- value: self,
- boolValue: false,
- render: { (info: RenderingInfo) in return Rendering("") })
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `NSNumber`.
-*/
-
-extension NSNumber {
-
- /**
- `NSNumber` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- NSNumber(integer: 1).mustacheBox // Valid, but discouraged
- Box(NSNumber(integer: 1)) // Preferred
-
-
- ### Rendering
-
- NSNumber renders exactly like Swift numbers: depending on its internal
- objCType, an NSNumber is rendered as a Swift Bool, Int, UInt, Int64, UInt64,
- or Double.
-
- - `{{number}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#number}}...{{/number}}` renders if and only if `number` is not 0 (zero).
-
- - `{{^number}}...{{/number}}` renders if and only if `number` is 0 (zero).
-
- */
- public override var mustacheBox: MustacheBox {
-
- let objCType = String(cString: self.objCType)
- switch objCType {
- case "c":
- return Box(Int(int8Value))
- case "C":
- return Box(UInt(uint8Value))
- case "s":
- return Box(Int(int16Value))
- case "S":
- return Box(UInt(uint16Value))
- case "i":
- return Box(Int(int32Value))
- case "I":
- return Box(UInt(uint32Value))
- case "l":
- return Box(Int(intValue))
- case "L":
- return Box(UInt(uintValue))
- case "q":
- return Box(Int64(int64Value))
- case "Q":
- return Box(UInt64(uint64Value))
- case "f":
- return Box(Double(floatValue))
- case "d":
- return Box(doubleValue)
- case "B":
- return Box(boolValue)
- default:
- NSLog("GRMustache support for NSNumber of type \(objCType) is not implemented: value is discarded.")
- return Box()
- }
- }
-}
-
-
-/**
-GRMustache provides built-in support for rendering `NSString`.
-*/
-
-extension NSString {
-
- /**
- `NSString` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- "foo".mustacheBox // Valid, but discouraged
- Box("foo") // Preferred
-
-
- ### Rendering
-
- - `{{string}}` renders the string, HTML-escaped.
-
- - `{{{string}}}` renders the string, *not* HTML-escaped.
-
- - `{{#string}}...{{/string}}` renders if and only if `string` is not empty.
-
- - `{{^string}}...{{/string}}` renders if and only if `string` is empty.
-
- HTML-escaping of `{{string}}` tags is disabled for Text templates: see
- `Configuration.contentType` for a full discussion of the content type of
- templates.
-
-
- ### Keys exposed to templates
+ /// value.mustacheBox // Valid, but discouraged
+ /// Box(value) // Preferred
+ ///
+ /// Returns a `MustacheBox` that describes how your type interacts with the
+ /// rendering engine.
+ ///
+ /// You can for example box another value that is already boxable, such as
+ /// dictionaries:
+ ///
+ /// struct Person {
+ /// let firstName: String
+ /// let lastName: String
+ /// }
+ ///
+ /// extension Person : MustacheBoxable {
+ /// // Expose the `firstName`, `lastName` and `fullName` keys to
+ /// // Mustache templates:
+ /// var mustacheBox: MustacheBox {
+ /// return Box([
+ /// "firstName": firstName,
+ /// "lastName": lastName,
+ /// "fullName": "\(self.firstName) \(self.lastName)",
+ /// ])
+ /// }
+ /// }
+ ///
+ /// let person = Person(firstName: "Tom", lastName: "Selleck")
+ ///
+ /// // Renders "Tom Selleck"
+ /// let template = try! Template(string: "{{person.fullName}}")
+ /// try! template.render(Box(["person": Box(person)]))
+ ///
+ /// However, there are multiple ways to build a box, several `Box()`
+ /// functions. See their documentations.
+ var mustacheBox: MustacheBox { get }
+}
- A string can be queried for the following keys:
-
- - `length`: the number of characters in the string (using Swift method).
+extension MustacheBox {
- */
+ /// `MustacheBox` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates. Its mustacheBox property returns itself.
public override var mustacheBox: MustacheBox {
- return Box(self as String)
+ return self
}
}
-/**
-Values that conform to the `MustacheBoxable` protocol can feed Mustache
-templates.
-
-- parameter boxable: An optional value that conform to the `MustacheBoxable`
- protocol.
-
-- returns: A MustacheBox that wraps *boxable*.
-*/
+/// Values that conform to the `MustacheBoxable` protocol can feed Mustache
+/// templates.
+///
+/// - parameter boxable: An optional value that conform to the `MustacheBoxable`
+/// protocol.
+///
+/// - returns: A MustacheBox that wraps *boxable*.
public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
return boxable?.mustacheBox ?? Box()
}
-// IMPLEMENTATION NOTE
-//
-// Why is there a BoxAny(Any?) function, but no Box(Any?)
-//
-// GRMustache aims at having a single boxing function: Box(), with many
-// overloaded variants. This lets the user box anything, standard Swift types
-// (Bool, String, etc.), custom types, as well as opaque types (such as
-// StandardLibrary.javascriptEscape).
-//
-// For example:
-//
-// public func Box(boxable: MustacheBoxable?) -> MustacheBox
-// public func Box(filter: FilterFunction) -> MustacheBox
-//
-// Sometimes values come out of Foundation objects:
-//
-// class NSDictionary {
-// subscript (key: NSCopying) -> Any? { get }
-// }
-//
-// So we need a Box(Any?) function, right?
-//
-// Unfortunately, this will not work:
-//
-// protocol MustacheBoxable {}
-// class Thing: MustacheBoxable {}
-//
-// func Box(x: MustacheBoxable?) -> String { return "MustacheBoxable" }
-// func Box(x: Any?) -> String { return "Any" }
-//
-// // error: ambiguous use of 'Box'
-// Box(Thing())
-//
-// Maybe if we turn the func Box(x: MustacheBoxable?) into a generic one? Well,
-// it does not make the job either:
-//
-// protocol MustacheBoxable {}
-// class Thing: MustacheBoxable {}
-//
-// func Box(x: T?) -> String { return "MustacheBoxable" }
-// func Box(x: Any?) -> String { return "Any" }
-//
-// // Wrong: uses the Any variant
-// Box(Thing())
-//
-// // Error: cannot find an overload for 'Box' that accepts an argument list of type '(MustacheBoxable)'
-// Box(Thing() as MustacheBoxable)
-//
-// // Error: Crash the compiler
-// Box(Thing() as MustacheBoxable?)
-//
-// And if we turn the func Box(x: Any) into a generic one? Well, it gets
-// better:
-//
-// protocol MustacheBoxable {}
-// class Thing: MustacheBoxable {}
-//
-// func Box(x: MustacheBoxable?) -> String { return "MustacheBoxable" }
-// func Box(object: T?) -> String { return "Any" }
-//
-// // OK: uses the MustacheBox variant
-// Box(Thing())
-//
-// // OK: uses the MustacheBox variant
-// Box(Thing() as MustacheBoxable)
-//
-// // OK: uses the MustacheBox variant
-// Box(Thing() as MustacheBoxable?)
-//
-// // OK: uses the Any variant
-// Box(Thing() as Any)
-//
-// // OK: uses the Any variant
-// Box(Thing() as Any?)
-//
-// This looks OK, doesn't it? Well, it's not satisfying yet.
-//
-// According to http://airspeedvelocity.net/2015/03/26/protocols-and-generics-2/
-// there are reasons for preferring func Box(x: T?) over
-// func Box(x: MustacheBoxable?). The example above have shown that the boxing
-// of AnyObject with an overloaded version of Box() would make this choice for
-// us.
-//
-// It's better not to make any choice right now, until we have a better
-// knowledge of Swift performances and optimization, and of the way Swift
-// resolves overloaded functions.
-//
-// So let's avoid having any Box(AnyObject?) variant in the public API, and
-// let's expose the BoxAny(object: AnyObject?) instead.
-
-// IMPLEMENTATION NOTE 2
-//
-// BoxAny has been made private. Now users get a compiler error when they
-// try to box AnyObject.
-//
-// Reasons for this removal from the public API:
-//
-// - Users will try Box() first, which will fail. Since they may not know
-// anything BoxAny, BoxAny is of little value anyway.
-// - BoxAny is error-prone, since it accepts anything and fails at
-// runtime.
-//
-// It still exists because we need it to box Foundation collections like
-// NSArray, NSSet, NSDictionary.
-
-/**
-`AnyObject` can feed Mustache templates.
-
-Yet, due to constraints in the Swift language, there is no `Box(AnyObject)`
-function. Instead, you use `BoxAny`:
-
- let set = NSSet(object: "Mario")
- let object: AnyObject = set.anyObject()
- let box = BoxAny(object)
- box.value as String // "Mario"
-
-The object is tested at runtime whether it adopts the `MustacheBoxable`
-protocol. In this case, this function behaves just like `Box(MustacheBoxable)`.
-
-Otherwise, GRMustache logs a warning, and returns the empty box.
-
-- parameter object: An object.
-- returns: A MustacheBox that wraps *object*.
-*/
-private func BoxAny(_ object: Any?) -> MustacheBox {
- guard let object = object else {
+/// Attempt to turn value into a box.
+///
+/// - parameter object: An object.
+/// - returns: A MustacheBox that wraps *object*.
+func BoxAny(_ value: Any?) -> MustacheBox {
+ guard let value = value else {
return Box()
}
- switch object {
+ switch value {
case let boxable as MustacheBoxable:
return boxable.mustacheBox
case let array as [Any?]:
@@ -1002,57 +116,35 @@ private func BoxAny(_ object: Any?) -> MustacheBox {
case let dictionary as [AnyHashable: Any?]:
return Box(dictionary)
default:
- NSLog("Mustache: value `\(object)` does not conform to MustacheBoxable: it is discarded.")
+ NSLog("Mustache: value `\(value)` does not conform to MustacheBoxable: it is discarded.")
return Box()
}
}
-// =============================================================================
-// MARK: - Boxing of Collections
-
-
-// IMPLEMENTATION NOTE
-//
-// We don't provide any boxing function for `SequenceType`, because this type
-// makes no requirement on conforming types regarding whether they will be
-// destructively "consumed" by iteration (as stated by documentation).
-//
-// Now we need to consume a sequence several times:
-//
-// - for converting it to an array for the arrayValue property.
-// - for consuming the first element to know if the sequence is empty or not.
-// - for rendering it.
-//
-// So we don't support boxing of sequences.
-
-// Support for all collections
extension Collection {
- /**
- Concatenates the rendering of the collection items.
-
- There are two tricks when rendering collections:
-
- 1. Items can render as Text or HTML, and our collection should render with
- the same type. It is an error to mix content types.
-
- 2. We have to tell items that they are rendered as an enumeration item.
- This allows collections to avoid enumerating their items when they are
- part of another collections:
-
- {{# arrays }} // Each array renders as an enumeration item, and has itself enter the context stack.
- {{#.}} // Each array renders "normally", and enumerates its items
- ...
- {{/.}}
- {{/ arrays }}
-
- - parameter info: A RenderingInfo
- - parameter box: A closure that turns collection items into a MustacheBox.
- It makes us able to provide a single implementation
- whatever the type of the collection items.
- - returns: A Rendering
- */
+ /// Concatenates the rendering of the collection items.
+ ///
+ /// There are two tricks when rendering collections:
+ ///
+ /// 1. Items can render as Text or HTML, and our collection should render with
+ /// the same type. It is an error to mix content types.
+ ///
+ /// 2. We have to tell items that they are rendered as an enumeration item.
+ /// This allows collections to avoid enumerating their items when they are
+ /// part of another collections:
+ ///
+ /// {{# arrays }} // Each array renders as an enumeration item, and has itself enter the context stack.
+ /// {{#.}} // Each array renders "normally", and enumerates its items
+ /// ...
+ /// {{/.}}
+ /// {{/ arrays }}
+ ///
+ /// - parameter info: A RenderingInfo
+ /// - parameter box: A closure that turns collection items into a
+ /// MustacheBox.
+ /// - returns: A Rendering
fileprivate func renderItems(_ info: RenderingInfo, box: (Iterator.Element) -> MustacheBox) throws -> Rendering {
// Prepare the rendering. We don't known the contentType yet: it depends on items
var buffer = ""
@@ -1128,25 +220,21 @@ extension Collection {
}
}
-// Support for Set
extension Collection where IndexDistance == Int {
- /**
- This function returns a MustacheBox that wraps a set-like collection.
-
- The returned box can be queried for the following keys:
-
- - `first`: the first object in the collection
- - `count`: number of elements in the collection
-
- - parameter value: the value of the returned box.
- - parameter box: A closure that turns collection items into a MustacheBox.
- It makes us able to provide a single implementation
- whatever the type of the collection items.
- - returns: A MustacheBox that wraps the collection.
- */
- fileprivate func mustacheBoxWithSetValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
+ /// This function returns a MustacheBox that wraps a set-like collection.
+ ///
+ /// The returned box can be queried for the following keys:
+ ///
+ /// - `first`: the first object in the collection
+ /// - `count`: number of elements in the collection
+ ///
+ /// - parameter value: the value of the returned box.
+ /// - parameter box: A closure that turns collection items into a
+ /// MustacheBox.
+ /// - returns: A MustacheBox that wraps the collection.
+ func mustacheBoxWithSetValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
return MustacheBox(
- converter: MustacheBox.Converter(arrayValue: { self.map({ box($0) }) }),
+ converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),
value: value,
boolValue: !isEmpty,
keyedSubscript: { (key) in
@@ -1177,26 +265,22 @@ extension Collection where IndexDistance == Int {
}
}
-// Support for Array
extension BidirectionalCollection where IndexDistance == Int {
- /**
- This function returns a MustacheBox that wraps an array-like collection.
-
- The returned box can be queried for the following keys:
-
- - `first`: the first object in the collection
- - `count`: number of elements in the collection
- - `last`: the last object in the collection
-
- - parameter value: the value of the returned box.
- - parameter box: A closure that turns collection items into a MustacheBox.
- It makes us able to provide a single implementation
- whatever the type of the collection items.
- - returns: A MustacheBox that wraps the collection.
- */
- fileprivate func mustacheBoxWithArrayValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
+ /// This function returns a MustacheBox that wraps an array-like collection.
+ ///
+ /// The returned box can be queried for the following keys:
+ ///
+ /// - `first`: the first object in the collection
+ /// - `count`: number of elements in the collection
+ /// - `last`: the last object in the collection
+ ///
+ /// - parameter value: the value of the returned box.
+ /// - parameter box: A closure that turns collection items into a
+ /// MustacheBox.
+ /// - returns: A MustacheBox that wraps the collection.
+ func mustacheBoxWithArrayValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
return MustacheBox(
- converter: MustacheBox.Converter(arrayValue: { self.map({ box($0) }) }),
+ converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),
value: value,
boolValue: !isEmpty,
keyedSubscript: { (key) in
@@ -1207,7 +291,7 @@ extension BidirectionalCollection where IndexDistance == Int {
} else {
return Box()
}
- case "last": // C: BidirectionalCollection
+ case "last": // C.Index: BidirectionalIndexType
if let last = self.last {
return box(last)
} else {
@@ -1234,286 +318,121 @@ extension BidirectionalCollection where IndexDistance == Int {
}
-/**
-GRMustache provides built-in support for rendering `NSSet`.
-*/
-
-extension NSSet {
-
- /**
- `NSSet` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- let set: NSSet = [1,2,3]
-
- // Renders "213"
- let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
- try! template.render(Box(["set": Box(set)]))
-
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- set.mustacheBox // Valid, but discouraged
- Box(set) // Preferred
-
-
- ### Rendering
-
- - `{{set}}` renders the concatenation of the renderings of the set items, in
- any order.
-
- - `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
- pushing each item on its turn on the top of the context stack.
-
- - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
-
-
- ### Keys exposed to templates
-
- A set can be queried for the following keys:
-
- - `count`: number of elements in the set
- - `first`: the first object in the set
-
- Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once,
- if and only if `set` is not empty.
-
-
- ### Unwrapping from MustacheBox
-
- Whenever you want to extract a collection of a MustacheBox, use the
- `arrayValue` property: it reliably returns an Array of MustacheBox, whatever
- the actual type of the raw boxed value (Set, Array, NSArray, NSSet, ...)
- */
- public override var mustacheBox: MustacheBox {
- // DRY principle won't let us provide all the code for boxing NSSet when
- // we already have it for Set.
- //
- // However, we can't turn NSSet into Set, because the only type we could
- // build is Set, which we can't do because MustacheBox is
- // not Hashable.
- //
- // So turn NSSet into a Swift Array of MustacheBoxes, and ask the array
- // to return a set-like box:
- let array = IteratorSequence(NSFastEnumerationIterator(self)).map(BoxAny)
- return array.mustacheBoxWithSetValue(self, box: { $0 })
- }
-}
-
-
-/**
-Sets of `MustacheBoxable` can feed Mustache templates.
-
- let set:Set = [1,2,3]
-
- // Renders "132", or "231", etc.
- let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
- try! template.render(Box(["set": Box(set)]))
-
-
-### Rendering
-
-- `{{set}}` renders the concatenation of the set items.
-
-- `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
- pushing each item on its turn on the top of the context stack.
-
-- `{{^set}}...{{/set}}` renders if and only if `set` is empty.
-
-
-### Keys exposed to templates
-
-A set can be queried for the following keys:
-
-- `count`: number of elements in the set
-- `first`: the first object in the set
-
-Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once, if
-and only if `set` is not empty.
-
-
-### Unwrapping from MustacheBox
-
-Whenever you want to extract a collection of a MustacheBox, use the `arrayValue`
-property: it returns an Array of MustacheBox, whatever the actual
-type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
-
-
-- parameter array: An array of boxable values.
-
-- returns: A MustacheBox that wraps *array*.
-*/
+/// Sets can feed Mustache templates.
+///
+/// let set:Set = [1,2,3]
+///
+/// // Renders "132", or "231", etc.
+/// let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
+/// try! template.render(Box(["set": Box(set)]))
+///
+///
+/// ### Rendering
+///
+/// - `{{set}}` renders the concatenation of the set items.
+///
+/// - `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
+/// pushing each item on its turn on the top of the context stack.
+///
+/// - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
+///
+///
+/// ### Keys exposed to templates
+///
+/// A set can be queried for the following keys:
+///
+/// - `count`: number of elements in the set
+/// - `first`: the first object in the set
+///
+/// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once,
+/// if and only if `set` is not empty.
+///
+///
+/// - parameter set: A set.
+/// - returns: A MustacheBox that wraps *set*.
public func Box(_ set: Set?) -> MustacheBox {
-//public func Box(_ set: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
guard let set = set else {
return Box()
}
return set.mustacheBoxWithSetValue(set, box: { BoxAny($0) })
}
-/**
-Arrays of `MustacheBoxable` can feed Mustache templates.
-
- let array = [1,2,3]
-
- // Renders "123"
- let template = try! Template(string: "{{#array}}{{.}}{{/array}}")
- try! template.render(Box(["array": Box(array)]))
-
-
-### Rendering
-
-- `{{array}}` renders the concatenation of the array items.
-
-- `{{#array}}...{{/array}}` renders as many times as there are items in `array`,
- pushing each item on its turn on the top of the context stack.
-
-- `{{^array}}...{{/array}}` renders if and only if `array` is empty.
-
-
-### Keys exposed to templates
-
-An array can be queried for the following keys:
-
-- `count`: number of elements in the array
-- `first`: the first object in the array
-- `last`: the last object in the array
-
-Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders once,
-if and only if `array` is not empty.
-
-
-### Unwrapping from MustacheBox
-
-Whenever you want to extract a collection of a MustacheBox, use the `arrayValue`
-property: it returns an Array of MustacheBox, whatever the actual
-type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
-
-
-- parameter array: An array of boxable values.
-
-- returns: A MustacheBox that wraps *array*.
-*/
+/// Arrays can feed Mustache templates.
+///
+/// let array = [1,2,3]
+///
+/// // Renders "123"
+/// let template = try! Template(string: "{{#array}}{{.}}{{/array}}")
+/// try! template.render(Box(["array": Box(array)]))
+///
+///
+/// ### Rendering
+///
+/// - `{{array}}` renders the concatenation of the array items.
+///
+/// - `{{#array}}...{{/array}}` renders as many times as there are items in
+/// `array`, pushing each item on its turn on the top of the context stack.
+///
+/// - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
+///
+///
+/// ### Keys exposed to templates
+///
+/// An array can be queried for the following keys:
+///
+/// - `count`: number of elements in the array
+/// - `first`: the first object in the array
+/// - `last`: the last object in the array
+///
+/// Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders
+/// once, if and only if `array` is not empty.
+///
+///
+/// - parameter array: An array of boxable values.
+/// - returns: A MustacheBox that wraps *array*.
public func Box(_ array: [Any?]?) -> MustacheBox {
-//public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
guard let array = array else {
return Box()
}
return array.mustacheBoxWithArrayValue(array, box: { BoxAny($0) })
}
-/**
-Arrays of `MustacheBoxable?` can feed Mustache templates.
-
- let array = [1,2,nil]
-
- // Renders "<1><2><>"
- let template = try! Template(string: "{{#array}}<{{.}}>{{/array}}")
- try! template.render(Box(["array": Box(array)]))
-
-
-### Rendering
-
-- `{{array}}` renders the concatenation of the array items.
-
-- `{{#array}}...{{/array}}` renders as many times as there are items in `array`,
- pushing each item on its turn on the top of the context stack.
-
-- `{{^array}}...{{/array}}` renders if and only if `array` is empty.
-
-
-### Keys exposed to templates
-
-An array can be queried for the following keys:
-
-- `count`: number of elements in the array
-- `first`: the first object in the array
-- `last`: the last object in the array
-
-Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders once,
-if and only if `array` is not empty.
-
-
-### Unwrapping from MustacheBox
-
-Whenever you want to extract a collection of a MustacheBox, use the `arrayValue`
-property: it returns an Array of MustacheBox, whatever the actual
-type of the raw boxed value (Array, Set, NSArray, NSSet, ...).
-
-- parameter array: An array of optional boxable values.
-
-- returns: A MustacheBox that wraps *array*.
-*/
-//public func Box(_ array: [T?]?) -> MustacheBox {
-// //public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element: MustacheBoxable, C.IndexDistance == Int {
-// guard let array = array else {
-// return Box()
-// }
-// return array.mustacheBoxWithArrayValue(array, box: { BoxAny($0) })
-//}
-//public func Box(_ array: C?) -> MustacheBox where C.Iterator.Element == Optional, T: MustacheBoxable, C.IndexDistance == Int {
-// if let array = array {
-// return array.mustacheBoxWithArrayValue(array, box: { Box($0) })
-// } else {
-// return Box()
-// }
-//}
-
-
-// =============================================================================
-// MARK: - Boxing of Dictionaries
-
-
-/**
-A dictionary of values that conform to the `MustacheBoxable` protocol can feed
-Mustache templates. It behaves exactly like Objective-C `NSDictionary`.
-
- let dictionary: [String: String] = [
- "firstName": "Freddy",
- "lastName": "Mercury"]
-
- // Renders "Freddy Mercury"
- let template = try! Template(string: "{{firstName}} {{lastName}}")
- let rendering = try! template.render(Box(dictionary))
-
-
-### Rendering
-
-- `{{dictionary}}` renders the built-in Swift String Interpolation of the
- dictionary.
-
-- `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
- context stack, and renders the section once.
-
-- `{{^dictionary}}...{{/dictionary}}` does not render.
-
-
-In order to iterate over the key/value pairs of a dictionary, use the `each`
-filter from the Standard Library:
-
- // Register StandardLibrary.each for the key "each":
- let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
-
- // Renders ""
- let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"]
- let rendering = try! template.render(Box(["dictionary": dictionary]))
-
-
-### Unwrapping from MustacheBox
-
-Whenever you want to extract a dictionary of a MustacheBox, use the
-`dictionaryValue` property: it reliably returns an `[String: MustacheBox]`
-dictionary, whatever the actual type of the raw boxed value.
-
-
-- parameter dictionary: A dictionary of values that conform to the
- `MustacheBoxable` protocol.
-
-- returns: A MustacheBox that wraps *dictionary*.
-*/
+/// A dictionary can feed Mustache templates.
+///
+/// let dictionary: [String: String] = [
+/// "firstName": "Freddy",
+/// "lastName": "Mercury"]
+///
+/// // Renders "Freddy Mercury"
+/// let template = try! Template(string: "{{firstName}} {{lastName}}")
+/// let rendering = try! template.render(Box(dictionary))
+///
+///
+/// ### Rendering
+///
+/// - `{{dictionary}}` renders the built-in Swift String Interpolation of the
+/// dictionary.
+///
+/// - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
+/// context stack, and renders the section once.
+///
+/// - `{{^dictionary}}...{{/dictionary}}` does not render.
+///
+///
+/// In order to iterate over the key/value pairs of a dictionary, use the `each`
+/// filter from the Standard Library:
+///
+/// // Register StandardLibrary.each for the key "each":
+/// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
+/// template.registerInBaseContext("each", Box(StandardLibrary.each))
+///
+/// // Renders ""
+/// let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"]
+/// let rendering = try! template.render(Box(["dictionary": dictionary]))
+///
+/// - parameter dictionary: A dictionary
+/// - returns: A MustacheBox that wraps *dictionary*.
public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
guard let dictionary = dictionary else {
return Box()
@@ -1540,271 +459,110 @@ public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
})
}
-/**
-A dictionary of optional values that conform to the `MustacheBoxable` protocol
-can feed Mustache templates. It behaves exactly like Objective-C `NSDictionary`.
-
- let dictionary: [String: String?] = [
- "firstName": nil,
- "lastName": "Zappa"]
-
- // Renders " Zappa"
- let template = try! Template(string: "{{firstName}} {{lastName}}")
- let rendering = try! template.render(Box(dictionary))
-
-
-### Rendering
-
-- `{{dictionary}}` renders the built-in Swift String Interpolation of the
- dictionary.
-
-- `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
- context stack, and renders the section once.
-
-- `{{^dictionary}}...{{/dictionary}}` does not render.
-
-
-In order to iterate over the key/value pairs of a dictionary, use the `each`
-filter from the Standard Library:
-
- // Register StandardLibrary.each for the key "each":
- let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
-
- // Renders ""
- let dictionary: [String: String?] = ["firstName": "Freddy", "lastName": "Mercury"]
- let rendering = try! template.render(Box(["dictionary": dictionary]))
-
-
-### Unwrapping from MustacheBox
-
-Whenever you want to extract a dictionary of a MustacheBox, use the
-`dictionaryValue` property: it reliably returns an `[String: MustacheBox]`
-dictionary, whatever the actual type of the raw boxed value.
-
-
-- parameter dictionary: A dictionary of optional values that conform to the
- `MustacheBoxable` protocol.
-
-- returns: A MustacheBox that wraps *dictionary*.
-*/
-//public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
-// guard let dictionary = dictionary else {
-// return Box()
-// }
-// return MustacheBox(
-// converter: MustacheBox.Converter(dictionaryValue: {
-// var boxDictionary: [String: MustacheBox] = [:]
-// for (key, value) in dictionary {
-// if let key = key as? String {
-// boxDictionary[key] = BoxAny(value)
-// } else {
-// NSLog("GRMustache found a non-string key in dictionary (\(key)): value is discarded.")
-// }
-// }
-// return boxDictionary
-// }),
-// value: dictionary,
-// keyedSubscript: { (key: String) in
-// if let value = dictionary[key] {
-// return BoxAny(value)
-// } else {
-// return Box()
-// }
-// })
-//}
-
-
-/**
-GRMustache provides built-in support for rendering `NSDictionary`.
-*/
-
-extension NSDictionary {
-
- /**
- `NSDictionary` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- // Renders "Freddy Mercury"
- let dictionary: NSDictionary = [
- "firstName": "Freddy",
- "lastName": "Mercury"]
- let template = try! Template(string: "{{firstName}} {{lastName}}")
- let rendering = try! template.render(Box(dictionary))
-
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- dictionary.mustacheBox // Valid, but discouraged
- Box(dictionary) // Preferred
-
-
- ### Rendering
-
- - `{{dictionary}}` renders the result of the `description` method, HTML-escaped.
-
- - `{{{dictionary}}}` renders the result of the `description` method, *not* HTML-escaped.
-
- - `{{#dictionary}}...{{/dictionary}}` renders once, pushing `dictionary` on
- the top of the context stack.
-
- - `{{^dictionary}}...{{/dictionary}}` does not render.
-
-
- In order to iterate over the key/value pairs of a dictionary, use the `each`
- filter from the Standard Library:
-
- // Attach StandardLibrary.each to the key "each":
- let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
-
- // Renders ""
- let dictionary = ["name": "Arthur", "age": 36] as NSDictionary
- let rendering = try! template.render(Box(["dictionary": dictionary]))
-
-
- ### Unwrapping from MustacheBox
-
- Whenever you want to extract a dictionary of a MustacheBox, use the
- `dictionaryValue` property: it reliably returns an `[String: MustacheBox]`
- dictionary, whatever the actual type of the raw boxed value.
- */
- public override var mustacheBox: MustacheBox {
- return MustacheBox(
- converter: MustacheBox.Converter(dictionaryValue: { IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
- var boxDictionary = boxDictionary
- if let key = key as? String {
- boxDictionary[key] = BoxAny(self[key])
- } else {
- NSLog("GRMustache found a non-string key in NSDictionary (\(key)): value is discarded.")
- }
- return boxDictionary
- })}),
- value: self,
- keyedSubscript: { BoxAny(self[$0])
- })
- }
-}
-
-
-// =============================================================================
-// MARK: - Boxing of Core Mustache functions
-
-/**
-A function that wraps a `FilterFunction` into a `MustacheBox` so that it can
-feed template.
-
- let square: FilterFunction = Filter { (x: Int?) in
- return Box(x! * x!)
- }
-
- let template = try! Template(string: "{{ square(x) }}")
- template.registerInBaseContext("square", Box(square))
-
- // Renders "100"
- try! template.render(Box(["x": 10]))
-
-- parameter filter: A FilterFunction.
-- returns: A MustacheBox that wraps *filter*.
-
-See also:
-
-- FilterFunction
-*/
+/// A function that wraps a `FilterFunction` into a `MustacheBox` so that it can
+/// feed template.
+///
+/// let square: FilterFunction = Filter { (x: Int?) in
+/// return Box(x! * x!)
+/// }
+///
+/// let template = try! Template(string: "{{ square(x) }}")
+/// template.registerInBaseContext("square", Box(square))
+///
+/// // Renders "100"
+/// try! template.render(Box(["x": 10]))
+///
+/// - parameter filter: A FilterFunction.
+/// - returns: A MustacheBox that wraps *filter*.
+///
+/// See also:
+///
+/// - FilterFunction
public func Box(_ filter: @escaping FilterFunction) -> MustacheBox {
return MustacheBox(filter: filter)
}
-/**
-A function that wraps a `RenderFunction` into a `MustacheBox` so that it can
-feed template.
-
- let foo: RenderFunction = { (_) in Rendering("foo") }
-
- // Renders "foo"
- let template = try! Template(string: "{{ foo }}")
- try! template.render(Box(["foo": Box(foo)]))
-
-- parameter render: A RenderFunction.
-- returns: A MustacheBox that wraps *render*.
-
-See also:
-
-- RenderFunction
-*/
+/// A function that wraps a `RenderFunction` into a `MustacheBox` so that it can
+/// feed template.
+///
+/// let foo: RenderFunction = { (_) in Rendering("foo") }
+///
+/// // Renders "foo"
+/// let template = try! Template(string: "{{ foo }}")
+/// try! template.render(Box(["foo": Box(foo)]))
+///
+/// - parameter render: A RenderFunction.
+/// - returns: A MustacheBox that wraps *render*.
+///
+/// See also:
+///
+/// - RenderFunction
public func Box(_ render: @escaping RenderFunction) -> MustacheBox {
return MustacheBox(render: render)
}
-/**
-A function that wraps a `WillRenderFunction` into a `MustacheBox` so that it can
-feed template.
-
- let logTags: WillRenderFunction = { (tag: Tag, box: MustacheBox) in
- print("\(tag) will render \(box.value!)")
- return box
- }
-
- // By entering the base context of the template, the logTags function
- // will be notified of all tags.
- let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
- template.extendBaseContext(Box(logTags))
-
- // Prints:
- // {{# user }} at line 1 will render { firstName = Errol; lastName = Flynn; }
- // {{ firstName }} at line 1 will render Errol
- // {{ lastName }} at line 1 will render Flynn
- let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
- try! template.render(Box(data))
-
-- parameter willRender: A WillRenderFunction
-- returns: A MustacheBox that wraps *willRender*.
-
-See also:
-
-- WillRenderFunction
-*/
+/// A function that wraps a `WillRenderFunction` into a `MustacheBox` so that it
+/// can feed template.
+///
+/// let logTags: WillRenderFunction = { (tag: Tag, box: MustacheBox) in
+/// print("\(tag) will render \(box.value!)")
+/// return box
+/// }
+///
+/// // By entering the base context of the template, the logTags function
+/// // will be notified of all tags.
+/// let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
+/// template.extendBaseContext(Box(logTags))
+///
+/// // Prints:
+/// // {{# user }} at line 1 will render { firstName = Errol; lastName = Flynn; }
+/// // {{ firstName }} at line 1 will render Errol
+/// // {{ lastName }} at line 1 will render Flynn
+/// let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
+/// try! template.render(Box(data))
+///
+/// - parameter willRender: A WillRenderFunction
+/// - returns: A MustacheBox that wraps *willRender*.
+///
+/// See also:
+///
+/// - WillRenderFunction
public func Box(_ willRender: @escaping WillRenderFunction) -> MustacheBox {
return MustacheBox(willRender: willRender)
}
-/**
-A function that wraps a `DidRenderFunction` into a `MustacheBox` so that it can
-feed template.
-
- let logRenderings: DidRenderFunction = { (tag: Tag, box: MustacheBox, string: String?) in
- print("\(tag) did render \(box.value!) as `\(string!)`")
- }
-
- // By entering the base context of the template, the logRenderings function
- // will be notified of all tags.
- let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
- template.extendBaseContext(Box(logRenderings))
-
- // Renders "Errol Flynn"
- //
- // Prints:
- // {{ firstName }} at line 1 did render Errol as `Errol`
- // {{ lastName }} at line 1 did render Flynn as `Flynn`
- // {{# user }} at line 1 did render { firstName = Errol; lastName = Flynn; } as `Errol Flynn`
- let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
- try! template.render(Box(data))
-
-- parameter didRender: A DidRenderFunction/
-- returns: A MustacheBox that wraps *didRender*.
-
-See also:
-
-- DidRenderFunction
-*/
+/// A function that wraps a `DidRenderFunction` into a `MustacheBox` so that it
+/// can feed template.
+///
+/// let logRenderings: DidRenderFunction = { (tag: Tag, box: MustacheBox, string: String?) in
+/// print("\(tag) did render \(box.value!) as `\(string!)`")
+/// }
+///
+/// // By entering the base context of the template, the logRenderings function
+/// // will be notified of all tags.
+/// let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
+/// template.extendBaseContext(Box(logRenderings))
+///
+/// // Renders "Errol Flynn"
+/// //
+/// // Prints:
+/// // {{ firstName }} at line 1 did render Errol as `Errol`
+/// // {{ lastName }} at line 1 did render Flynn as `Flynn`
+/// // {{# user }} at line 1 did render { firstName = Errol; lastName = Flynn; } as `Errol Flynn`
+/// let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
+/// try! template.render(Box(data))
+///
+/// - parameter didRender: A DidRenderFunction/
+/// - returns: A MustacheBox that wraps *didRender*.
+///
+/// See also:
+///
+/// - DidRenderFunction
public func Box(_ didRender: @escaping DidRenderFunction) -> MustacheBox {
return MustacheBox(didRender: didRender)
}
-/**
-The empty box, the box that represents missing values.
-*/
+/// The empty box, the box that represents missing values.
public func Box() -> MustacheBox {
return EmptyBox
}
diff --git a/Sources/CoreGraphics.swift b/Sources/CoreGraphics.swift
new file mode 100644
index 00000000..e4b38d2e
--- /dev/null
+++ b/Sources/CoreGraphics.swift
@@ -0,0 +1,75 @@
+// The MIT License
+//
+// Copyright (c) 2016 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
+ import CoreGraphics
+
+ /// GRMustache provides built-in support for rendering `CGFloat`.
+ extension CGFloat : MustacheBoxable {
+
+ /// CGFloat adopts the MustacheBoxable protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use
+ /// the `Box()` function instead:
+ ///
+ /// CGFloat(3.14).mustacheBox // Valid, but discouraged
+ /// Box(CGFloat(3.14)) // Preferred
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{cgfloat}}` is rendered with built-in Swift String
+ /// Interpolation. Custom formatting can be explicitly required with
+ /// NSNumberFormatter, as in `{{format(a)}}` (see `NSFormatter`).
+ ///
+ /// - `{{#cgfloat}}...{{/cgfloat}}` renders if and only if `cgfloat` is not 0 (zero).
+ ///
+ /// - `{{^cgfloat}}...{{/cgfloat}}` renders if and only if `double` is 0 (zero).
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0.0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ cgfloat }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# cgfloats }}...{{/ cgfloats }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# cgfloat }}...{{/ cgfloat }}
+ //
+ // Doubles do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+ }
+
+#endif
diff --git a/Sources/Foundation.swift b/Sources/Foundation.swift
new file mode 100644
index 00000000..f1541787
--- /dev/null
+++ b/Sources/Foundation.swift
@@ -0,0 +1,407 @@
+// The MIT License
+//
+// Copyright (c) 2016 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import Foundation
+
+
+/// GRMustache provides built-in support for rendering `NSObject`.
+extension NSObject : MustacheBoxable {
+
+ /// `NSObject` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
+ ///
+ /// object.mustacheBox // Valid, but discouraged
+ /// Box(object) // Preferred
+ ///
+ ///
+ /// NSObject's default implementation handles two general cases:
+ ///
+ /// - Enumerable objects that conform to the `NSFastEnumeration` protocol, such
+ /// as `NSArray` and `NSOrderedSet`.
+ /// - All other objects
+ ///
+ /// GRMustache ships with a few specific classes that escape the general
+ /// cases and provide their own rendering behavior: `NSDictionary`,
+ /// `NSFormatter`, `NSNull`, `NSNumber`, `NSString`, and `NSSet` (see the
+ /// documentation for those classes).
+ ///
+ /// Your own subclasses of NSObject can also override the `mustacheBox`
+ /// method and provide their own custom behavior.
+ ///
+ ///
+ /// ## Arrays
+ ///
+ /// An object is treated as an array if it conforms to `NSFastEnumeration`.
+ /// This is the case of `NSArray` and `NSOrderedSet`, for example.
+ /// `NSDictionary` and `NSSet` have their own custom Mustache rendering: see
+ /// their documentation for more information.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{array}}` renders the concatenation of the renderings of the
+ /// array items.
+ ///
+ /// - `{{#array}}...{{/array}}` renders as many times as there are items in
+ /// `array`, pushing each item on its turn on the top of the
+ /// context stack.
+ ///
+ /// - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
+ ///
+ ///
+ /// ### Keys exposed to templates
+ ///
+ /// An array can be queried for the following keys:
+ ///
+ /// - `count`: number of elements in the array
+ /// - `first`: the first object in the array
+ /// - `last`: the last object in the array
+ ///
+ /// Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}`
+ /// renders once, if and only if `array` is not empty.
+ ///
+ ///
+ /// ## Other objects
+ ///
+ /// Other objects fall in the general case.
+ ///
+ /// Their keys are extracted with the `valueForKey:` method, as long as the
+ /// key is a property name, a custom property getter, or the name of a
+ /// `NSManagedObject` attribute.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{object}}` renders the result of the `description` method, HTML-escaped.
+ ///
+ /// - `{{{object}}}` renders the result of the `description` method, *not*
+ /// HTML-escaped.
+ ///
+ /// - `{{#object}}...{{/object}}` renders once, pushing `object` on the top
+ /// of the context stack.
+ ///
+ /// - `{{^object}}...{{/object}}` does not render.
+ ///
+ open var mustacheBox: MustacheBox {
+ if let enumerable = self as? NSFastEnumeration {
+ // Enumerable
+
+ // Turn enumerable into a Swift array of MustacheBoxes that we know how to box
+ let array = IteratorSequence(NSFastEnumerationIterator(enumerable)).map(BoxAny)
+ return array.mustacheBoxWithArrayValue(self, box: { $0 })
+
+ } else {
+ // Generic NSObject
+
+ #if OBJC
+ return MustacheBox(
+ value: self,
+ keyedSubscript: { (key: String) in
+ if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
+ // Use valueForKey: for safe keys
+ return BoxAny(self.value(forKey: key))
+ } else {
+ // Missing key
+ return Box()
+ }
+ })
+ #else
+ return MustacheBox(value: self)
+ #endif
+ }
+ }
+}
+
+
+/// GRMustache provides built-in support for rendering `NSNull`.
+extension NSNull {
+
+ /// `NSNull` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
+ ///
+ /// NSNull().mustacheBox // Valid, but discouraged
+ /// Box(NSNull()) // Preferred
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{null}}` does not render.
+ ///
+ /// - `{{#null}}...{{/null}}` does not render (NSNull is falsey).
+ ///
+ /// - `{{^null}}...{{/null}}` does render (NSNull is falsey).
+ open override var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: false,
+ render: { (info: RenderingInfo) in return Rendering("") })
+ }
+}
+
+
+/// GRMustache provides built-in support for rendering `NSNumber`.
+extension NSNumber {
+
+ /// `NSNumber` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
+ ///
+ /// NSNumber(integer: 1).mustacheBox // Valid, but discouraged
+ /// Box(NSNumber(integer: 1)) // Preferred
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// NSNumber renders exactly like Swift numbers: depending on its internal
+ /// objCType, an NSNumber is rendered as a Swift Bool, Int, UInt, Int64,
+ /// UInt64, or Double.
+ ///
+ /// - `{{number}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NSNumberFormatter,
+ /// as in `{{format(a)}}` (see `NSFormatter`).
+ ///
+ /// - `{{#number}}...{{/number}}` renders if and only if `number` is
+ /// not 0 (zero).
+ ///
+ /// - `{{^number}}...{{/number}}` renders if and only if `number` is 0 (zero).
+ ///
+ open override var mustacheBox: MustacheBox {
+
+ let objCType = String(cString: self.objCType)
+ switch objCType {
+ case "c":
+ return Box(Int(int8Value))
+ case "C":
+ return Box(UInt(uint8Value))
+ case "s":
+ return Box(Int(int16Value))
+ case "S":
+ return Box(UInt(uint16Value))
+ case "i":
+ return Box(Int(int32Value))
+ case "I":
+ return Box(UInt(uint32Value))
+ case "l":
+ return Box(intValue)
+ case "L":
+ return Box(uintValue)
+ case "q":
+ return Box(int64Value)
+ case "Q":
+ return Box(uint64Value)
+ case "f":
+ return Box(Double(floatValue))
+ case "d":
+ return Box(doubleValue)
+ case "B":
+ return Box(boolValue)
+ default:
+ NSLog("GRMustache support for NSNumber of type \(objCType) is not implemented: value is discarded.")
+ return Box()
+ }
+ }
+}
+
+
+/// GRMustache provides built-in support for rendering `NSString`.
+extension NSString {
+
+ /// `NSString` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
+ ///
+ /// "foo".mustacheBox // Valid, but discouraged
+ /// Box("foo") // Preferred
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{string}}` renders the string, HTML-escaped.
+ ///
+ /// - `{{{string}}}` renders the string, *not* HTML-escaped.
+ ///
+ /// - `{{#string}}...{{/string}}` renders if and only if `string` is
+ /// not empty.
+ ///
+ /// - `{{^string}}...{{/string}}` renders if and only if `string` is empty.
+ ///
+ /// HTML-escaping of `{{string}}` tags is disabled for Text templates: see
+ /// `Configuration.contentType` for a full discussion of the content type of
+ /// templates.
+ ///
+ ///
+ /// ### Keys exposed to templates
+ ///
+ /// A string can be queried for the following keys:
+ ///
+ /// - `length`: the number of characters in the string (using Swift method).
+ open override var mustacheBox: MustacheBox {
+ return Box(self as String)
+ }
+}
+
+
+/// GRMustache provides built-in support for rendering `NSSet`.
+extension NSSet {
+
+ /// `NSSet` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// let set: NSSet = [1,2,3]
+ ///
+ /// // Renders "213"
+ /// let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
+ /// try! template.render(Box(["set": Box(set)]))
+ ///
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
+ ///
+ /// set.mustacheBox // Valid, but discouraged
+ /// Box(set) // Preferred
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{set}}` renders the concatenation of the renderings of the set
+ /// items, in any order.
+ ///
+ /// - `{{#set}}...{{/set}}` renders as many times as there are items in
+ /// `set`, pushing each item on its turn on the top of the context stack.
+ ///
+ /// - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
+ ///
+ ///
+ /// ### Keys exposed to templates
+ ///
+ /// A set can be queried for the following keys:
+ ///
+ /// - `count`: number of elements in the set
+ /// - `first`: the first object in the set
+ ///
+ /// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders
+ /// once, if and only if `set` is not empty.
+ open override var mustacheBox: MustacheBox {
+ let array = IteratorSequence(NSFastEnumerationIterator(self)).map(BoxAny)
+ return array.mustacheBoxWithSetValue(self, box: { $0 })
+ }
+}
+
+
+/// GRMustache provides built-in support for rendering `NSDictionary`.
+extension NSDictionary {
+
+ /// `NSDictionary` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// // Renders "Freddy Mercury"
+ /// let dictionary: NSDictionary = [
+ /// "firstName": "Freddy",
+ /// "lastName": "Mercury"]
+ /// let template = try! Template(string: "{{firstName}} {{lastName}}")
+ /// let rendering = try! template.render(Box(dictionary))
+ ///
+ ///
+ /// You should not directly call the `mustacheBox` property. Always use the
+ /// `Box()` function instead:
+ ///
+ /// dictionary.mustacheBox // Valid, but discouraged
+ /// Box(dictionary) // Preferred
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{dictionary}}` renders the result of the `description` method,
+ /// HTML-escaped.
+ ///
+ /// - `{{{dictionary}}}` renders the result of the `description` method,
+ /// *not* HTML-escaped.
+ ///
+ /// - `{{#dictionary}}...{{/dictionary}}` renders once, pushing `dictionary`
+ /// on the top of the context stack.
+ ///
+ /// - `{{^dictionary}}...{{/dictionary}}` does not render.
+ ///
+ ///
+ /// In order to iterate over the key/value pairs of a dictionary, use the `each`
+ /// filter from the Standard Library:
+ ///
+ /// // Attach StandardLibrary.each to the key "each":
+ /// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
+ /// template.registerInBaseContext("each", Box(StandardLibrary.each))
+ ///
+ /// // Renders ""
+ /// let dictionary = ["name": "Arthur", "age": 36] as NSDictionary
+ /// let rendering = try! template.render(Box(["dictionary": dictionary]))
+ open override var mustacheBox: MustacheBox {
+ return MustacheBox(
+ converter: MustacheBox.Converter(dictionaryValue: { IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
+ var boxDictionary = boxDictionary
+ if let key = key as? String {
+ boxDictionary[key] = BoxAny(self[key])
+ } else {
+ NSLog("GRMustache found a non-string key in NSDictionary (\(key)): value is discarded.")
+ }
+ return boxDictionary
+ })}),
+ value: self,
+ keyedSubscript: { BoxAny(self[$0])
+ })
+ }
+}
+
+/// Support for Mustache rendering of ReferenceConvertible types.
+extension ReferenceConvertible where Self: MustacheBoxable {
+ /// Returns a MustacheBox that behaves like the equivalent NSObject.
+ ///
+ /// See NSObject.mustacheBox
+ public var mustacheBox: MustacheBox {
+ if let object = self as? ReferenceType {
+ return object.mustacheBox
+ } else {
+ NSLog("Value `\(self)` can not feed Mustache templates: it is discarded.")
+ return Box()
+ }
+ }
+}
+
+/// Data can feed Mustache templates.
+extension Data : MustacheBoxable { }
+
+/// Date can feed Mustache templates.
+extension Date : MustacheBoxable { }
+
+/// URL can feed Mustache templates.
+extension URL : MustacheBoxable { }
+
+/// UUID can feed Mustache templates.
+extension UUID : MustacheBoxable { }
diff --git a/Sources/SwiftStandardLibrary.swift b/Sources/SwiftStandardLibrary.swift
new file mode 100644
index 00000000..c016b4b4
--- /dev/null
+++ b/Sources/SwiftStandardLibrary.swift
@@ -0,0 +1,416 @@
+// The MIT License
+//
+// Copyright (c) 2016 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+/**
+ GRMustache provides built-in support for rendering `Double`.
+ */
+
+extension Double : MustacheBoxable {
+
+ /**
+ `Double` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 3.14.mustacheBox // Valid, but discouraged
+ Box(3.14) // Preferred
+
+
+ ### Rendering
+
+ - `{{double}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#double}}...{{/double}}` renders if and only if `double` is not 0 (zero).
+
+ - `{{^double}}...{{/double}}` renders if and only if `double` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0.0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ double }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# doubles }}...{{/ doubles }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# double }}...{{/ double }}
+ //
+ // Doubles do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
+/**
+ GRMustache provides built-in support for rendering `String`.
+ */
+
+extension String : MustacheBoxable {
+
+ /**
+ `String` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ "foo".mustacheBox // Valid, but discouraged
+ Box("foo") // Preferred
+
+
+ ### Rendering
+
+ - `{{string}}` renders the string, HTML-escaped.
+
+ - `{{{string}}}` renders the string, *not* HTML-escaped.
+
+ - `{{#string}}...{{/string}}` renders if and only if `string` is not empty.
+
+ - `{{^string}}...{{/string}}` renders if and only if `string` is empty.
+
+ HTML-escaping of `{{string}}` tags is disabled for Text templates: see
+ `Configuration.contentType` for a full discussion of the content type of
+ templates.
+
+
+ ### Keys exposed to templates
+
+ A string can be queried for the following keys:
+
+ - `length`: the number of characters in the string.
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self.characters.count > 0),
+ keyedSubscript: { (key: String) in
+ switch key {
+ case "length":
+ return Box(self.characters.count)
+ default:
+ return Box()
+ }
+ })
+ }
+}
+
+
+/**
+GRMustache provides built-in support for rendering `Bool`.
+*/
+
+extension Bool : MustacheBoxable {
+
+ /**
+ `Bool` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ true.mustacheBox // Valid, but discouraged
+ Box(true) // Preferred
+
+
+ ### Rendering
+
+ - `{{bool}}` renders as `0` or `1`.
+
+ - `{{#bool}}...{{/bool}}` renders if and only if `bool` is true.
+
+ - `{{^bool}}...{{/bool}}` renders if and only if `bool` is false.
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: self,
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ bool }}
+ return Rendering("\(self ? 1 : 0)") // Behave like [NSNumber numberWithBool:]
+ case .section:
+ if info.enumerationItem {
+ // {{# bools }}...{{/ bools }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# bool }}...{{/ bool }}
+ //
+ // Bools do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
+/**
+GRMustache provides built-in support for rendering `Int64`.
+*/
+
+extension Int64 : MustacheBoxable {
+
+ /**
+ `Int64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 1.mustacheBox // Valid, but discouraged
+ Box(1) // Preferred
+
+
+ ### Rendering
+
+ - `{{int}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
+
+ - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ int }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# ints }}...{{/ ints }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# int }}...{{/ int }}
+ //
+ // Ints do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
+/**
+GRMustache provides built-in support for rendering `Int`.
+*/
+
+extension Int : MustacheBoxable {
+
+ /**
+ `Int` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 1.mustacheBox // Valid, but discouraged
+ Box(1) // Preferred
+
+
+ ### Rendering
+
+ - `{{int}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
+
+ - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ int }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# ints }}...{{/ ints }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# int }}...{{/ int }}
+ //
+ // Ints do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
+/**
+GRMustache provides built-in support for rendering `UInt64`.
+*/
+
+extension UInt64 : MustacheBoxable {
+
+ /**
+ `UInt64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 1.mustacheBox // Valid, but discouraged
+ Box(1) // Preferred
+
+
+ ### Rendering
+
+ - `{{uint}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
+
+ - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ uint }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# uints }}...{{/ uints }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# uint }}...{{/ uint }}
+ //
+ // Uints do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
+/**
+GRMustache provides built-in support for rendering `UInt`.
+*/
+
+extension UInt : MustacheBoxable {
+
+ /**
+ `UInt` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 1.mustacheBox // Valid, but discouraged
+ Box(1) // Preferred
+
+
+ ### Rendering
+
+ - `{{uint}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
+
+ - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ uint }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# uints }}...{{/ uints }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# uint }}...{{/ uint }}
+ //
+ // Uints do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index be57c5f0..e65d7e46 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -231,6 +231,15 @@
569C423E1A88979600748E98 /* MustacheBoxDocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C423D1A88979600748E98 /* MustacheBoxDocumentationTests.swift */; };
56A0B4671A19D95A008A719F /* ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A0B4661A19D95A008A719F /* ContextTests.swift */; };
56A88EC81A066EC40055E40A /* RenderFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A88EC71A066EC40055E40A /* RenderFunctionTests.swift */; };
+ 56A928931DBA385D00F15955 /* CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928901DBA385D00F15955 /* CoreGraphics.swift */; };
+ 56A928941DBA385D00F15955 /* CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928901DBA385D00F15955 /* CoreGraphics.swift */; };
+ 56A928951DBA385D00F15955 /* CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928901DBA385D00F15955 /* CoreGraphics.swift */; };
+ 56A928961DBA385D00F15955 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928911DBA385D00F15955 /* Foundation.swift */; };
+ 56A928971DBA385D00F15955 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928911DBA385D00F15955 /* Foundation.swift */; };
+ 56A928981DBA385D00F15955 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928911DBA385D00F15955 /* Foundation.swift */; };
+ 56A928991DBA385D00F15955 /* SwiftStandardLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928921DBA385D00F15955 /* SwiftStandardLibrary.swift */; };
+ 56A9289A1DBA385D00F15955 /* SwiftStandardLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928921DBA385D00F15955 /* SwiftStandardLibrary.swift */; };
+ 56A9289B1DBA385D00F15955 /* SwiftStandardLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A928921DBA385D00F15955 /* SwiftStandardLibrary.swift */; };
56B401A01A1F643B002D5CD7 /* TagTests.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 56B4019E1A1F643B002D5CD7 /* TagTests.mustache */; };
56B401A11A1F643B002D5CD7 /* TagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B4019F1A1F643B002D5CD7 /* TagTests.swift */; };
56B4B2151A25BC5200BD4F8F /* TemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B4B2141A25BC5200BD4F8F /* TemplateTests.swift */; };
@@ -371,6 +380,9 @@
569C423D1A88979600748E98 /* MustacheBoxDocumentationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MustacheBoxDocumentationTests.swift; sourceTree = ""; };
56A0B4661A19D95A008A719F /* ContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextTests.swift; sourceTree = ""; };
56A88EC71A066EC40055E40A /* RenderFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RenderFunctionTests.swift; sourceTree = ""; };
+ 56A928901DBA385D00F15955 /* CoreGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreGraphics.swift; sourceTree = ""; };
+ 56A928911DBA385D00F15955 /* Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Foundation.swift; sourceTree = ""; };
+ 56A928921DBA385D00F15955 /* SwiftStandardLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftStandardLibrary.swift; sourceTree = ""; };
56B4019E1A1F643B002D5CD7 /* TagTests.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TagTests.mustache; sourceTree = ""; };
56B4019F1A1F643B002D5CD7 /* TagTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TagTests.swift; sourceTree = ""; };
56B4B2141A25BC5200BD4F8F /* TemplateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateTests.swift; sourceTree = ""; };
@@ -645,6 +657,7 @@
563AE20319FC643C0028C6C1 /* ExpressionInvocation.swift */,
56BD793F1A0E381900937A76 /* MustacheBox.swift */,
563AE1EB19FC55920028C6C1 /* RenderingEngine.swift */,
+ 56A9288F1DBA384800F15955 /* Support */,
);
name = Rendering;
sourceTree = "";
@@ -699,6 +712,16 @@
path = ServicesTests;
sourceTree = "";
};
+ 56A9288F1DBA384800F15955 /* Support */ = {
+ isa = PBXGroup;
+ children = (
+ 56A928901DBA385D00F15955 /* CoreGraphics.swift */,
+ 56A928911DBA385D00F15955 /* Foundation.swift */,
+ 56A928921DBA385D00F15955 /* SwiftStandardLibrary.swift */,
+ );
+ name = Support;
+ sourceTree = "";
+ };
56B4019D1A1F643B002D5CD7 /* TagTests */ = {
isa = PBXGroup;
children = (
@@ -1149,12 +1172,14 @@
5607A3411C160216002364C1 /* GRMustacheKeyAccess.m in Sources */,
09A89E001BF58560003A695E /* ExpressionInvocation.swift in Sources */,
09A89DF81BF58550003A695E /* NSFormatter.swift in Sources */,
+ 56A928981DBA385D00F15955 /* Foundation.swift in Sources */,
09A89DEF1BF58548003A695E /* TemplateGenerator.swift in Sources */,
09A89E031BF58569003A695E /* RenderingEngine.swift in Sources */,
09A89DF61BF58550003A695E /* StandardLibrary.swift in Sources */,
09A89DFB1BF58557003A695E /* TemplateParser.swift in Sources */,
09A89DF31BF58550003A695E /* JavascriptEscapeHelper.swift in Sources */,
09A89DF71BF58550003A695E /* URLEscapeHelper.swift in Sources */,
+ 56A9289B1DBA385D00F15955 /* SwiftStandardLibrary.swift in Sources */,
09A89DFC1BF58557003A695E /* TemplateToken.swift in Sources */,
09A89DE71BF58538003A695E /* LocatedTag.swift in Sources */,
09A89DF51BF58550003A695E /* Logger.swift in Sources */,
@@ -1167,6 +1192,7 @@
09A89DF01BF58548003A695E /* ExpressionGenerator.swift in Sources */,
09A89DF21BF58550003A695E /* HTMLEscapeHelper.swift in Sources */,
09A89DF41BF58550003A695E /* Localizer.swift in Sources */,
+ 56A928951DBA385D00F15955 /* CoreGraphics.swift in Sources */,
09A89DFA1BF58557003A695E /* ExpressionParser.swift in Sources */,
09A89DF91BF58550003A695E /* ZipFilter.swift in Sources */,
);
@@ -1217,12 +1243,14 @@
561E72901A8BDC4D004ED48B /* TemplateParser.swift in Sources */,
561E728B1A8BDC4D004ED48B /* StandardLibrary.swift in Sources */,
561E72931A8BDC4D004ED48B /* ExpressionInvocation.swift in Sources */,
+ 56A928971DBA385D00F15955 /* Foundation.swift in Sources */,
561E72941A8BDC4D004ED48B /* MustacheBox.swift in Sources */,
561E72911A8BDC4D004ED48B /* TemplateToken.swift in Sources */,
561E72881A8BDC4D004ED48B /* HTMLEscapeHelper.swift in Sources */,
561E72851A8BDC4D004ED48B /* TemplateCompiler.swift in Sources */,
561E728C1A8BDC4D004ED48B /* URLEscapeHelper.swift in Sources */,
561E728A1A8BDC4D004ED48B /* Localizer.swift in Sources */,
+ 56A9289A1DBA385D00F15955 /* SwiftStandardLibrary.swift in Sources */,
561E727F1A8BDC4D004ED48B /* Tag.swift in Sources */,
561E72861A8BDC4D004ED48B /* Configuration.swift in Sources */,
561E727E1A8BDC4D004ED48B /* SectionTag.swift in Sources */,
@@ -1235,6 +1263,7 @@
561E729A1A8BDC4D004ED48B /* TemplateRepository.swift in Sources */,
561E72991A8BDC4D004ED48B /* Template.swift in Sources */,
561336071B2174E2005BB0F7 /* Common.swift in Sources */,
+ 56A928941DBA385D00F15955 /* CoreGraphics.swift in Sources */,
56105B0A1B4E8618002CD224 /* LocatedTag.swift in Sources */,
566244581AEBC88C008BAD41 /* Expression.swift in Sources */,
);
@@ -1302,12 +1331,14 @@
5607A33F1C160216002364C1 /* GRMustacheKeyAccess.m in Sources */,
563AE1D819FC4A450028C6C1 /* Context.swift in Sources */,
56BF065B1A063DE600926BEB /* JavascriptEscapeHelper.swift in Sources */,
+ 56A928961DBA385D00F15955 /* Foundation.swift in Sources */,
563AE1BD19FC27040028C6C1 /* TemplateCompiler.swift in Sources */,
563AE1D319FC45400028C6C1 /* TemplateRepository.swift in Sources */,
563AE1D519FC48330028C6C1 /* Template.swift in Sources */,
5622F1FB1A0355EE00B5D3F8 /* EachFilter.swift in Sources */,
563AE1BA19FBE6F40028C6C1 /* Configuration.swift in Sources */,
563AE1B519FBE4FF0028C6C1 /* TemplateParser.swift in Sources */,
+ 56A928991DBA385D00F15955 /* SwiftStandardLibrary.swift in Sources */,
5622F1F51A02E98F00B5D3F8 /* StandardLibrary.swift in Sources */,
5622F1F81A03535700B5D3F8 /* Localizer.swift in Sources */,
563AE1EC19FC55920028C6C1 /* RenderingEngine.swift in Sources */,
@@ -1320,6 +1351,7 @@
569C42381A86824800748E98 /* ZipFilter.swift in Sources */,
563AE1C019FC29BF0028C6C1 /* TemplateAST.swift in Sources */,
566244571AEBC88C008BAD41 /* Expression.swift in Sources */,
+ 56A928931DBA385D00F15955 /* CoreGraphics.swift in Sources */,
56105B091B4E8618002CD224 /* LocatedTag.swift in Sources */,
563AE20419FC643C0028C6C1 /* ExpressionInvocation.swift in Sources */,
);
From f97c5fe7d420760b635b756fcd7ab54c91afdd16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 14:33:02 +0200
Subject: [PATCH 10/46] BoxAny: test for Set as runtime
---
Sources/Box.swift | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index bbbdc1ff..847f6151 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -113,10 +113,12 @@ func BoxAny(_ value: Any?) -> MustacheBox {
return boxable.mustacheBox
case let array as [Any?]:
return Box(array)
+ case let set as Set:
+ return Box(set)
case let dictionary as [AnyHashable: Any?]:
return Box(dictionary)
default:
- NSLog("Mustache: value `\(value)` does not conform to MustacheBoxable: it is discarded.")
+ NSLog("Mustache: value `\(value)` is discarded (not an array, not a set, not a dictionary, not a MustacheBoxable value.")
return Box()
}
}
@@ -397,7 +399,6 @@ public func Box(_ array: [Any?]?) -> MustacheBox {
return array.mustacheBoxWithArrayValue(array, box: { BoxAny($0) })
}
-
/// A dictionary can feed Mustache templates.
///
/// let dictionary: [String: String] = [
@@ -444,7 +445,7 @@ public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
if let key = key as? String {
boxDictionary[key] = BoxAny(value)
} else {
- NSLog("GRMustache found a non-string key in dictionary (\(key)): value is discarded.")
+ NSLog("Mustache: non-string key in dictionary (\(key)) is discarded.")
}
}
return boxDictionary
From 5c908cef4017d4a5cf401b8a8328d85e80b309a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 14:33:51 +0200
Subject: [PATCH 11/46] MustacheError.localizedDescription (just in case people
use it)
---
Sources/Common.swift | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Sources/Common.swift b/Sources/Common.swift
index 082509b2..9ddf89fb 100644
--- a/Sources/Common.swift
+++ b/Sources/Common.swift
@@ -55,7 +55,7 @@ public enum ContentType {
// MARK: - Errors
/// The errors thrown by Mustache.swift
-public struct MustacheError: Error {
+public struct MustacheError : Error {
/// MustacheError types
public enum Kind : Int {
@@ -79,6 +79,11 @@ public struct MustacheError: Error {
/// Eventual underlying error
public let underlyingError: Error?
+ /// Returns self.description
+ public var localizedDescription: String {
+ return description
+ }
+
// Not public
From db3fd6c01fc5958f81c025944dbea61c7d5b6cb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 14:37:31 +0200
Subject: [PATCH 12/46] Add tests for nested values and collections
---
Tests/Public/BoxTests.swift | 208 +++++++++++++++++++++++++++++++++++-
1 file changed, 206 insertions(+), 2 deletions(-)
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index 30eaa4dd..c2686caf 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -28,7 +28,13 @@ private struct CustomHashableBoxable : MustacheBoxable, Hashable {
let int: Int
init(_ int: Int) { self.int = int }
var hashValue: Int { return int }
- var mustacheBox: MustacheBox { return Box(int) }
+ var mustacheBox: MustacheBox {
+ // Don't inherit the boolean nature of int
+ return MustacheBox(
+ value: self,
+ boolValue: true,
+ render: { _ in Rendering("\(self.int)") })
+ }
static func ==(lhs: CustomHashableBoxable, rhs: CustomHashableBoxable) -> Bool {
return lhs.int == rhs.int
@@ -39,7 +45,13 @@ private struct CustomBoxable : MustacheBoxable {
let int: Int
init(_ int: Int) { self.int = int }
var hashValue: Int { return int }
- var mustacheBox: MustacheBox { return Box(int) }
+ var mustacheBox: MustacheBox {
+ // Don't inherit the boolean nature of int
+ return MustacheBox(
+ value: self,
+ boolValue: true,
+ render: { _ in Rendering("\(self.int)") })
+ }
}
class BoxTests: XCTestCase {
@@ -79,6 +91,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: Int = 1
+ let template = try! Template(string: "{{#nested}}1{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
func testCustomBoxable() {
@@ -113,6 +133,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: CustomBoxable = CustomBoxable(1)
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
@@ -150,6 +178,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: Int? = 1
+ let template = try! Template(string: "{{#nested}}1{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
func testOptionalMustacheBoxable() {
@@ -184,6 +220,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: CustomBoxable? = CustomBoxable(1)
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
@@ -213,6 +257,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
+ do {
+ // Nested
+ let value: Set = [0,1,2]
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
}
func testSetOfCustomHashableBoxable() {
@@ -239,6 +291,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
+ do {
+ // Nested
+ let value: Set = [CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)]
+ let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
+ }
}
@@ -284,6 +344,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: [String: Int] = ["name": 1]
+ let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
func testDictionaryOfStringCustomBoxable() {
@@ -326,6 +394,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: [String: CustomBoxable] = ["name": CustomBoxable(1)]
+ let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
@@ -371,6 +447,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: [String: Int?] = ["name": 1]
+ let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
func testDictionaryOfStringOptionalCustomBoxable() {
@@ -413,6 +497,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: [String: CustomBoxable?] = ["name": CustomBoxable(1)]
+ let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
@@ -458,6 +550,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
+ do {
+ // Nested
+ let value: [String: Any] = ["int": 1, "string": "foo"]
+ let template = try! Template(string: "{{#nested}}{{int}}, {{string}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
}
@@ -503,6 +603,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo, ")
}
+ do {
+ // Nested
+ let value: [String: Any?] = ["int": 1, "string": "foo", "missing": nil]
+ let template = try! Template(string: "{{#nested}}{{int}}, {{string}}, {{missing}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
}
@@ -548,6 +656,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
+ do {
+ // Nested
+ let value: [AnyHashable: Any] = ["int": 1, "string": "foo"]
+ let template = try! Template(string: "{{#nested}}{{int}}, {{string}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
}
func testDictionaryOfArray() {
@@ -582,6 +698,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "12, foobar")
}
+ do {
+ // Nested
+ let value: [AnyHashable: Any] = ["int": [1, 2], "string": ["foo", "bar"]]
+ let template = try! Template(string: "{{#nested}}{{int}}, {{string}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "12, foobar")
+ }
}
func testDictionaryOfDictionary() {
@@ -616,6 +740,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1, foo")
}
+ do {
+ // Nested
+ let value: [AnyHashable: Any] = ["int": ["name": 1], "string": ["name": "foo"]]
+ let template = try! Template(string: "{{#nested}}{{int.name}}, {{string.name}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo")
+ }
}
@@ -661,6 +793,14 @@ class BoxTests: XCTestCase {
// let rendering = try! template.render(box)
// XCTAssertEqual(rendering, "1, foo, ")
}
+ do {
+ // Nested
+ let value: [AnyHashable: Any?] = ["int": 1, "string": "foo", "missing": nil]
+ let template = try! Template(string: "{{#nested}}{{int}}, {{string}}, {{missing}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1, foo, ")
+ }
}
@@ -706,6 +846,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
+ do {
+ // Nested
+ let value: [Int] = [0,1,2,3]
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
func testArrayOfCustomBoxable() {
@@ -748,6 +896,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
+ do {
+ // Nested
+ let value: [CustomBoxable] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
+ let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
@@ -793,6 +949,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
+ do {
+ // Nested
+ let value: [Int?] = [0,1,2,3,nil]
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
func testArrayOfOptionalCustomBoxable() {
@@ -835,6 +999,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0123")
}
+ do {
+ // Nested
+ let value: [CustomBoxable?] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
+ let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0123")
+ }
}
@@ -872,6 +1044,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
+ do {
+ // Nested
+ let value: [Any] = [0,"foo"]
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
}
func testArrayOfArray() {
@@ -906,6 +1086,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
+ do {
+ // Nested
+ let value: [Any] = [[0,"foo"]]
+ let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
}
func testArrayOfDictionary() {
@@ -940,6 +1128,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "1")
}
+ do {
+ // Nested
+ let value: [Any] = [["name": 1]]
+ let template = try! Template(string: "{{#nested}}{{#.}}{{name}}{{/}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "1")
+ }
}
@@ -985,6 +1181,14 @@ class BoxTests: XCTestCase {
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "0foo")
}
+ do {
+ // Nested
+ let value: [Any?] = [0,nil,"foo"]
+ let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
+ let box = Box(["nested": value])
+ let rendering = try! template.render(box)
+ XCTAssertEqual(rendering, "0foo")
+ }
}
From 1b9d0a4babe9f072fda5f4654245c34f6e5dfb7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 14:38:32 +0200
Subject: [PATCH 13/46] Rename NSFormatter.swift -> Formatter.swift
---
Sources/{NSFormatter.swift => Formatter.swift} | 0
Xcode/Mustache.xcodeproj/project.pbxproj | 16 ++++++++--------
2 files changed, 8 insertions(+), 8 deletions(-)
rename Sources/{NSFormatter.swift => Formatter.swift} (100%)
diff --git a/Sources/NSFormatter.swift b/Sources/Formatter.swift
similarity index 100%
rename from Sources/NSFormatter.swift
rename to Sources/Formatter.swift
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index e65d7e46..3b09511f 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -27,7 +27,6 @@
09A89DF51BF58550003A695E /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D204331B32EBA50031FEE7 /* Logger.swift */; };
09A89DF61BF58550003A695E /* StandardLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5622F1F41A02E98F00B5D3F8 /* StandardLibrary.swift */; };
09A89DF71BF58550003A695E /* URLEscapeHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BF06571A063DC800926BEB /* URLEscapeHelper.swift */; };
- 09A89DF81BF58550003A695E /* NSFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3721A1CC417000CE8CB /* NSFormatter.swift */; };
09A89DF91BF58550003A695E /* ZipFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C42371A86824800748E98 /* ZipFilter.swift */; };
09A89DFA1BF58557003A695E /* ExpressionParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563AE1C819FC38B80028C6C1 /* ExpressionParser.swift */; };
09A89DFB1BF58557003A695E /* TemplateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563AE1B419FBE4FF0028C6C1 /* TemplateParser.swift */; };
@@ -110,7 +109,6 @@
561E728A1A8BDC4D004ED48B /* Localizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5622F1F71A03535700B5D3F8 /* Localizer.swift */; };
561E728B1A8BDC4D004ED48B /* StandardLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5622F1F41A02E98F00B5D3F8 /* StandardLibrary.swift */; };
561E728C1A8BDC4D004ED48B /* URLEscapeHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BF06571A063DC800926BEB /* URLEscapeHelper.swift */; };
- 561E728D1A8BDC4D004ED48B /* NSFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3721A1CC417000CE8CB /* NSFormatter.swift */; };
561E728E1A8BDC4D004ED48B /* ZipFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C42371A86824800748E98 /* ZipFilter.swift */; };
561E728F1A8BDC4D004ED48B /* ExpressionParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563AE1C819FC38B80028C6C1 /* ExpressionParser.swift */; };
561E72901A8BDC4D004ED48B /* TemplateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563AE1B419FBE4FF0028C6C1 /* TemplateParser.swift */; };
@@ -217,7 +215,6 @@
566244B21AF201E7008BAD41 /* TemplateGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566244B01AF201E7008BAD41 /* TemplateGenerator.swift */; };
56660D6E1A9378B100E53CCF /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56660D6D1A9378B100E53CCF /* CoreFunctions.swift */; };
56660D6F1A9378B100E53CCF /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56660D6D1A9378B100E53CCF /* CoreFunctions.swift */; };
- 5674F3731A1CC417000CE8CB /* NSFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3721A1CC417000CE8CB /* NSFormatter.swift */; };
5674F3751A1CC7B8000CE8CB /* NSFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3741A1CC7B8000CE8CB /* NSFormatterTests.swift */; };
5685DAB41A1A2029003E583B /* LocalizerTestsBundle in Resources */ = {isa = PBXBuildFile; fileRef = 5685DAB31A1A2029003E583B /* LocalizerTestsBundle */; };
5685DAB61A1A2074003E583B /* LocalizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685DAB51A1A2074003E583B /* LocalizerTests.swift */; };
@@ -273,6 +270,9 @@
56DFF1DF1C16029B000082A4 /* Mustache.h in Headers */ = {isa = PBXBuildFile; fileRef = 56DFF1DC1C16029B000082A4 /* Mustache.h */; settings = {ATTRIBUTES = (Public, ); }; };
56EB0BE81BD0CB0D00A3DC55 /* LambdaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EB0BE71BD0CB0D00A3DC55 /* LambdaTests.swift */; };
56EB0BE91BD0CB0D00A3DC55 /* LambdaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EB0BE71BD0CB0D00A3DC55 /* LambdaTests.swift */; };
+ 56EE412A1DBB788D00CCA895 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE41291DBB788D00CCA895 /* Formatter.swift */; };
+ 56EE412B1DBB788D00CCA895 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE41291DBB788D00CCA895 /* Formatter.swift */; };
+ 56EE412C1DBB788D00CCA895 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE41291DBB788D00CCA895 /* Formatter.swift */; };
56FC9C8A1A17CD0C0020AAF8 /* ConfigurationBaseContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC9C841A17CD0C0020AAF8 /* ConfigurationBaseContextTests.swift */; };
56FC9C8B1A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC9C851A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift */; };
56FC9C8C1A17CD0C0020AAF8 /* ConfigurationExtendBaseContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC9C861A17CD0C0020AAF8 /* ConfigurationExtendBaseContextTests.swift */; };
@@ -368,7 +368,6 @@
566244A61AF1653C008BAD41 /* HoganSuite */ = {isa = PBXFileReference; lastKnownFileType = folder; path = HoganSuite; sourceTree = ""; };
566244B01AF201E7008BAD41 /* TemplateGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateGenerator.swift; sourceTree = ""; };
56660D6D1A9378B100E53CCF /* CoreFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreFunctions.swift; sourceTree = ""; };
- 5674F3721A1CC417000CE8CB /* NSFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSFormatter.swift; sourceTree = ""; };
5674F3741A1CC7B8000CE8CB /* NSFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSFormatterTests.swift; sourceTree = ""; };
5685DAB31A1A2029003E583B /* LocalizerTestsBundle */ = {isa = PBXFileReference; lastKnownFileType = folder; path = LocalizerTestsBundle; sourceTree = ""; };
5685DAB51A1A2074003E583B /* LocalizerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizerTests.swift; sourceTree = ""; };
@@ -413,6 +412,7 @@
56D204331B32EBA50031FEE7 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; };
56DFF1DC1C16029B000082A4 /* Mustache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mustache.h; sourceTree = ""; };
56EB0BE71BD0CB0D00A3DC55 /* LambdaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LambdaTests.swift; sourceTree = ""; };
+ 56EE41291DBB788D00CCA895 /* Formatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = ""; };
56FC9C841A17CD0C0020AAF8 /* ConfigurationBaseContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationBaseContextTests.swift; sourceTree = ""; };
56FC9C851A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationContentTypeTests.swift; sourceTree = ""; };
56FC9C861A17CD0C0020AAF8 /* ConfigurationExtendBaseContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationExtendBaseContextTests.swift; sourceTree = ""; };
@@ -513,13 +513,13 @@
isa = PBXGroup;
children = (
5622F1FA1A0355EE00B5D3F8 /* EachFilter.swift */,
+ 56EE41291DBB788D00CCA895 /* Formatter.swift */,
5622F2001A054D5800B5D3F8 /* HTMLEscapeHelper.swift */,
56BF065A1A063DE600926BEB /* JavascriptEscapeHelper.swift */,
5622F1F71A03535700B5D3F8 /* Localizer.swift */,
56D204331B32EBA50031FEE7 /* Logger.swift */,
5622F1F41A02E98F00B5D3F8 /* StandardLibrary.swift */,
56BF06571A063DC800926BEB /* URLEscapeHelper.swift */,
- 5674F3721A1CC417000CE8CB /* NSFormatter.swift */,
569C42371A86824800748E98 /* ZipFilter.swift */,
);
name = Goodies;
@@ -1171,7 +1171,6 @@
09A89DE91BF58538003A695E /* Tag.swift in Sources */,
5607A3411C160216002364C1 /* GRMustacheKeyAccess.m in Sources */,
09A89E001BF58560003A695E /* ExpressionInvocation.swift in Sources */,
- 09A89DF81BF58550003A695E /* NSFormatter.swift in Sources */,
56A928981DBA385D00F15955 /* Foundation.swift in Sources */,
09A89DEF1BF58548003A695E /* TemplateGenerator.swift in Sources */,
09A89E031BF58569003A695E /* RenderingEngine.swift in Sources */,
@@ -1190,6 +1189,7 @@
09A89DED1BF58538003A695E /* TemplateCompiler.swift in Sources */,
09A89DEB1BF58538003A695E /* TemplateASTNode.swift in Sources */,
09A89DF01BF58548003A695E /* ExpressionGenerator.swift in Sources */,
+ 56EE412C1DBB788D00CCA895 /* Formatter.swift in Sources */,
09A89DF21BF58550003A695E /* HTMLEscapeHelper.swift in Sources */,
09A89DF41BF58550003A695E /* Localizer.swift in Sources */,
56A928951DBA385D00F15955 /* CoreGraphics.swift in Sources */,
@@ -1257,10 +1257,10 @@
566244B21AF201E7008BAD41 /* TemplateGenerator.swift in Sources */,
561E728E1A8BDC4D004ED48B /* ZipFilter.swift in Sources */,
56660D6F1A9378B100E53CCF /* CoreFunctions.swift in Sources */,
- 561E728D1A8BDC4D004ED48B /* NSFormatter.swift in Sources */,
561E72841A8BDC4D004ED48B /* VariableTag.swift in Sources */,
561E72871A8BDC4D004ED48B /* EachFilter.swift in Sources */,
561E729A1A8BDC4D004ED48B /* TemplateRepository.swift in Sources */,
+ 56EE412B1DBB788D00CCA895 /* Formatter.swift in Sources */,
561E72991A8BDC4D004ED48B /* Template.swift in Sources */,
561336071B2174E2005BB0F7 /* Common.swift in Sources */,
56A928941DBA385D00F15955 /* CoreGraphics.swift in Sources */,
@@ -1342,13 +1342,13 @@
5622F1F51A02E98F00B5D3F8 /* StandardLibrary.swift in Sources */,
5622F1F81A03535700B5D3F8 /* Localizer.swift in Sources */,
563AE1EC19FC55920028C6C1 /* RenderingEngine.swift in Sources */,
- 5674F3731A1CC417000CE8CB /* NSFormatter.swift in Sources */,
566244B11AF201E7008BAD41 /* TemplateGenerator.swift in Sources */,
5622F2011A054D5800B5D3F8 /* HTMLEscapeHelper.swift in Sources */,
563AE1B719FBE6220028C6C1 /* TemplateToken.swift in Sources */,
56660D6E1A9378B100E53CCF /* CoreFunctions.swift in Sources */,
563AE1C219FC29E70028C6C1 /* TemplateASTNode.swift in Sources */,
569C42381A86824800748E98 /* ZipFilter.swift in Sources */,
+ 56EE412A1DBB788D00CCA895 /* Formatter.swift in Sources */,
563AE1C019FC29BF0028C6C1 /* TemplateAST.swift in Sources */,
566244571AEBC88C008BAD41 /* Expression.swift in Sources */,
56A928931DBA385D00F15955 /* CoreGraphics.swift in Sources */,
From 4a9e00aa3fef61fa63aafa6dc59c7097a1e4e8ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 14:38:46 +0200
Subject: [PATCH 14/46] Update tests
---
Tests/Public/DocumentationTests/ReadMeTests.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Tests/Public/DocumentationTests/ReadMeTests.swift b/Tests/Public/DocumentationTests/ReadMeTests.swift
index 6d4256d8..99358d3b 100644
--- a/Tests/Public/DocumentationTests/ReadMeTests.swift
+++ b/Tests/Public/DocumentationTests/ReadMeTests.swift
@@ -54,7 +54,7 @@ class ReadMeTests: XCTestCase {
func testReadmeExample1() {
let testBundle = Bundle(for: type(of: self))
let template = try! Template(named: "ReadMeExample1", bundle: testBundle)
- let data: [String: MustacheBoxable?] = [
+ let data: [String: Any] = [
"name": "Chris",
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
From 8976fc6d2c8116a1048786947f77ff06ba8498a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 15:22:10 +0200
Subject: [PATCH 15/46] Float adopts MustacheBoxable
---
Sources/SwiftStandardLibrary.swift | 57 ++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/Sources/SwiftStandardLibrary.swift b/Sources/SwiftStandardLibrary.swift
index c016b4b4..98339700 100644
--- a/Sources/SwiftStandardLibrary.swift
+++ b/Sources/SwiftStandardLibrary.swift
@@ -78,6 +78,63 @@ extension Double : MustacheBoxable {
}
+/**
+ GRMustache provides built-in support for rendering `Float`.
+ */
+
+extension Float : MustacheBoxable {
+
+ /**
+ `Float` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ templates.
+
+ You should not directly call the `mustacheBox` property. Always use the
+ `Box()` function instead:
+
+ 3.14.mustacheBox // Valid, but discouraged
+ Box(3.14) // Preferred
+
+
+ ### Rendering
+
+ - `{{float}}` is rendered with built-in Swift String Interpolation.
+ Custom formatting can be explicitly required with NSNumberFormatter, as in
+ `{{format(a)}}` (see `NSFormatter`).
+
+ - `{{#float}}...{{/float}}` renders if and only if `float` is not 0 (zero).
+
+ - `{{^float}}...{{/float}}` renders if and only if `float` is 0 (zero).
+
+ */
+ public var mustacheBox: MustacheBox {
+ return MustacheBox(
+ value: self,
+ boolValue: (self != 0.0),
+ render: { (info: RenderingInfo) in
+ switch info.tag.type {
+ case .variable:
+ // {{ float }}
+ return Rendering("\(self)")
+ case .section:
+ if info.enumerationItem {
+ // {{# floats }}...{{/ floats }}
+ return try info.tag.render(info.context.extendedContext(Box(self)))
+ } else {
+ // {{# float }}...{{/ float }}
+ //
+ // Floats do not enter the context stack when used in a
+ // boolean section.
+ //
+ // This behavior must not change:
+ // https://github.com/groue/GRMustache/issues/83
+ return try info.tag.render(info.context)
+ }
+ }
+ })
+ }
+}
+
+
/**
GRMustache provides built-in support for rendering `String`.
*/
From c1bbc737e831049cff7bb7c205c2861b3d6b144d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 15:22:55 +0200
Subject: [PATCH 16/46] BoxAny() checks at runtime for FilterFunction,
RenderFunction, WillRenderFunction, DidRenderFunction, and
KeySubscriptFunction
---
Sources/Box.swift | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index 847f6151..42517fe2 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -117,6 +117,16 @@ func BoxAny(_ value: Any?) -> MustacheBox {
return Box(set)
case let dictionary as [AnyHashable: Any?]:
return Box(dictionary)
+ case let f as FilterFunction:
+ return Box(f)
+ case let f as RenderFunction:
+ return Box(f)
+ case let f as WillRenderFunction:
+ return Box(f)
+ case let f as DidRenderFunction:
+ return Box(f)
+ case let f as KeyedSubscriptFunction:
+ return MustacheBox(keyedSubscript: f)
default:
NSLog("Mustache: value `\(value)` is discarded (not an array, not a set, not a dictionary, not a MustacheBoxable value.")
return Box()
From 62cc476f4384e3db4958a18f68ae0cfbc4a58585 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 15:23:13 +0200
Subject: [PATCH 17/46] #32: Update documentation for Swift 3
---
Docs/Guides/goodies.md | 38 +++----
README.md | 249 +++++++++++------------------------------
2 files changed, 85 insertions(+), 202 deletions(-)
diff --git a/Docs/Guides/goodies.md b/Docs/Guides/goodies.md
index 49565579..85388f2a 100644
--- a/Docs/Guides/goodies.md
+++ b/Docs/Guides/goodies.md
@@ -3,7 +3,7 @@ Goodies
GRMustache ships with a library of built-in goodies available for your templates.
-- [NSFormatter](#nsformatter)
+- [Formatter](#formatter)
- [HTMLEscape](#htmlescape)
- [javascriptEscape](#javascriptescape)
- [URLEscape](#urlescape)
@@ -13,15 +13,15 @@ GRMustache ships with a library of built-in goodies available for your templates
- [Logger](#logger)
-### NSFormatter
+### Formatter
-GRMustache provides built-in support for NSFormatter and its subclasses such as NSNumberFormatter and NSDateFormatter.
+GRMustache provides built-in support for Foundation's Formatter and its subclasses such as NumberFormatter and DateFormatter.
#### Formatting a value
```swift
-let percentFormatter = NSNumberFormatter()
-percentFormatter.numberStyle = .PercentStyle
+let percentFormatter = NumberFormatter()
+percentFormatter.numberStyle = .percent
let template = try! Template(string: "{{ percent(x) }}")
template.registerInBaseContext("percent", Box(percentFormatter))
@@ -33,7 +33,7 @@ let rendering = try! template.render(Box(data))
#### Formatting all values in a section
-NSFormatters are able to *format all variable tags* inside the section:
+Formatters are able to *format all variable tags* inside the section:
`Document.mustache`:
@@ -46,8 +46,8 @@ NSFormatters are able to *format all variable tags* inside the section:
Rendering code:
```swift
-let percentFormatter = NSNumberFormatter()
-percentFormatter.numberStyle = .PercentStyle
+let percentFormatter = NumberFormatter()
+percentFormatter.numberStyle = .percent
let template = try! Template(named: "Document")
template.registerInBaseContext("percent", Box(percentFormatter))
@@ -58,11 +58,11 @@ template.registerInBaseContext("percent", Box(percentFormatter))
// daily: 150%
// weekly: 400%
-id data = [
+let data = [
"hourly": 0.1,
"daily": 1.5,
"weekly": 4,
-};
+]
let rendering = try! template.render(Box(data))
```
@@ -82,11 +82,11 @@ Would render:
- ham: 22%
- butter: 43%
-Precisely speaking, "values that can't be formatted" are the ones that have the `stringForObjectValue:` method return nil, as stated by [NSFormatter documentation](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSFormatter_Class/index.html#//apple_ref/occ/instm/NSFormatter/stringForObjectValue:).
+Precisely speaking, "values that can't be formatted" are the ones that have the `string(for:)` method return nil, as stated by [NSFormatter documentation](https://developer.apple.com/reference/foundation/formatter/1415993-string).
-Typically, NSNumberFormatter only formats numbers, and NSDateFormatter, dates: you can safely mix various data types in a section controlled by those well-behaved formatters.
+Typically, NumberFormatter only formats numbers, and DateFormatter, dates: you can safely mix various data types in a section controlled by those well-behaved formatters.
-Support for NSFormatter is written using public APIs. You can check the [source](../../Mustache/Goodies/NSFormatter.swift) for inspiration.
+Support for Formatter is written using public APIs. You can check the [source](../../Mustache/Formatter.swift) for inspiration.
### HTMLEscape
@@ -121,7 +121,7 @@ Variable tags buried inside inner sections are escaped as well, so that you can
{{/}}
{{/ HTMLEscape }}
-StandardLibrary.HTMLEscape is written using public APIs. You can check the [source](../../Mustache/Goodies/HTMLEscape.swift) for inspiration.
+StandardLibrary.HTMLEscape is written using public APIs. You can check the [source](../../Mustache/HTMLEscapeHelper.swift) for inspiration.
See also [javascriptEscape](#javascriptescape), [URLEscape](#urlescape)
@@ -165,7 +165,7 @@ Variable tags buried inside inner sections are escaped as well, so that you can
```
-StandardLibrary.javascriptEscape is written using public APIs. You can check the [source](../../Mustache/Goodies/JavascriptEscape.swift) for inspiration.
+StandardLibrary.javascriptEscape is written using public APIs. You can check the [source](../../Mustache/JavascriptEscapeHelper.swift) for inspiration.
See also [HTMLEscape](#htmlescape), [URLEscape](#urlescape)
@@ -201,7 +201,7 @@ Variable tags buried inside inner sections are escaped as well, so that you can
{{/ URLEscape }}
```
-StandardLibrary.URLEscape is written using public APIs. You can check the [source](../../Mustache/Goodies/URLEscape.swift) for inspiration.
+StandardLibrary.URLEscape is written using public APIs. You can check the [source](../../Mustache/URLEscapeHelper.swift) for inspiration.
See also [HTMLEscape](#htmlescape), [javascriptEscape](#javascriptescape)
@@ -260,7 +260,7 @@ When provided with a dictionary, `each` iterates each key/value pair of the dict
The other positional keys `@index`, `@first`, etc. are still available when iterating dictionaries.
-The `each` filter is written using public APIs. You can check the [source](../../Mustache/Goodies/EachFilter.swift) for inspiration.
+The `each` filter is written using public APIs. You can check the [source](../../Mustache/EachFilter.swift) for inspiration.
### zip
@@ -312,7 +312,7 @@ In the example above, the first step has consumed (Alice, iOS and 100), and the
The zip filter renders a section as many times as there are elements in the longest of its argument: exhausted lists simply do not add anything to the rendering context.
-The `zip` filter is written using public APIs. You can check the [source](../../Mustache/Goodies/ZipFilter.swift) for inspiration.
+The `zip` filter is written using public APIs. You can check the [source](../../Mustache/ZipFilter.swift) for inspiration.
### Localizer
@@ -358,7 +358,7 @@ You can embed conditional sections inside:
Depending on the name, this would render `Bonjour Arthur` or `Bonjour toi`, given French localizations for both `Hello %@` and `Hello you`.
-StandardLibrary.Localizer filter is written using public APIs. You can check the [source](../../Mustache/Goodies/Localizer.swift) for inspiration.
+StandardLibrary.Localizer filter is written using public APIs. You can check the [source](../../Mustache/Localizer.swift) for inspiration.
### Logger
diff --git a/README.md b/README.md
index 7f2f4ce3..be728a08 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-GRMustache.swift [![Swift](https://img.shields.io/badge/swift-2.3-orange.svg?style=flat)](https://developer.apple.com/swift/) [![Platforms](https://img.shields.io/cocoapods/p/GRMustache.swift.svg)](https://developer.apple.com/swift/) [![License](https://img.shields.io/github/license/groue/GRMustache.swift.svg?maxAge=2592000)](/LICENSE)
+GRMustache.swift [![Swift](https://img.shields.io/badge/swift-3-orange.svg?style=flat)](https://developer.apple.com/swift/) [![Platforms](https://img.shields.io/cocoapods/p/GRMustache.swift.svg)](https://developer.apple.com/swift/) [![License](https://img.shields.io/github/license/groue/GRMustache.swift.svg?maxAge=2592000)](/LICENSE)
================
### Mustache templates for Swift
@@ -17,7 +17,7 @@ Follow [@groue](http://twitter.com/groue) on Twitter for release announcements a
Features •
Usage •
Installation •
- Documentation •
+ Documentation
---
@@ -62,15 +62,15 @@ import Mustache
let template = try Template(named: "document")
// Let template format dates with `{{format(...)}}`
-let dateFormatter = NSDateFormatter()
-dateFormatter.dateStyle = .MediumStyle
+let dateFormatter = DateFormatter()
+dateFormatter.dateStyle = .medium
template.registerInBaseContext("format", Box(dateFormatter))
// The rendered data
-let data = [
+let data: [String: Any] = [
"name": "Arthur",
- "date": NSDate(),
- "realDate": NSDate().dateByAddingTimeInterval(60*60*24*3),
+ "date": Date(),
+ "realDate": Date().addingTimeInterval(60*60*24*3),
"late": true
]
@@ -120,7 +120,7 @@ let package = Package(
name: "MyPackage",
targets: [],
dependencies: [
- .Package(url: "https://github.com/groue/GRMustache.swift", majorVersion: 1, minor: 1),
+ .Package(url: "https://github.com/groue/GRMustache.swift", majorVersion: 2, minor: 0),
]
)
```
@@ -131,9 +131,19 @@ Check [groue/GRMustacheSPM](https://github.com/groue/GRMustacheSPM) for a sample
### Manually
1. Download a copy of GRMustache.swift.
-2. Embed the `Mustache.xcodeproj` project in your own project.
-3. Add the `MustacheOSX`, `MustacheiOS`, or `MustacheWatchOS` target in the **Target Dependencies** section of the **Build Phases** tab of your application target.
-4. Add the the `Mustache.framework` from the targetted platform to the **Embedded Binaries** section of the **General** tab of your target.
+
+2. Checkout the latest GRMustache.swift version:
+
+ ```sh
+ cd [GRMustache.swift directory]
+ git checkout 1.1.0
+ ````
+
+3. Embed the `Mustache.xcodeproj` project in your own project.
+
+4. Add the `MustacheOSX`, `MustacheiOS`, or `MustacheWatchOS` target in the **Target Dependencies** section of the **Build Phases** tab of your application target.
+
+5. Add the the `Mustache.framework` from the targetted platform to the **Embedded Binaries** section of the **General** tab of your target.
See [MustacheDemoiOS](Docs/DemoApps/MustacheDemoiOS) for an example of such integration.
@@ -213,19 +223,19 @@ Templates may come from various sources:
For more information, check:
-- [Template.swift](Sources/Template.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html))
-- [TemplateRepository.swift](Sources/TemplateRepository.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/TemplateRepository.html))
+- [Template.swift](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html)
+- [TemplateRepository.swift](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/TemplateRepository.html)
Errors
------
-Not funny, but they happen. Standard NSErrors of domain NSCocoaErrorDomain, etc. may be thrown whenever the library needs to access the file system or other system resource. Mustache-specific errors are of type `MustacheError`:
+Not funny, but they happen. Standard errors of domain NSCocoaErrorDomain, etc. may be thrown whenever the library needs to access the file system or other system resource. Mustache-specific errors are of type `MustacheError`:
```swift
do {
let template = try Template(named: "Document")
- let rendering = template.render(Box(data))
+ let rendering = try template.render(Box(data))
} catch let error as MustacheError {
// Parse error at line 2 of template /path/to/template.mustache:
// Unclosed Mustache tag.
@@ -314,7 +324,7 @@ try template.render(Box(["value": false])) // false boolean
#### Collections
-If the value is a *collection*, the section is rendered as many times as there are elements in the collection, and inner tags have direct access to the keys of elements:
+If the value is a *collection* (an array or a set), the section is rendered as many times as there are elements in the collection, and inner tags have direct access to the keys of elements:
Template:
@@ -344,8 +354,6 @@ Rendering:
- Tom Selleck
```
-Collections can be Swift arrays, ranges, sets, NSArray, NSSet, etc.
-
#### Other Values
@@ -540,7 +548,7 @@ Check [TemplateRepository.swift](Sources/TemplateRepository.swift) for more info
A tag `{{> partial }}` includes a template, the one that is named "partial". One can say it is **statically** determined, since that partial has already been loaded before the template is rendered:
```swift
-let repo = TemplateRepository(bundle: NSBundle.mainBundle())
+let repo = TemplateRepository(bundle: Bundle.main)
let template = try repo.template(string: "{{#user}}{{>partial}}{{/user}}")
// Now the `partial.mustache` resource has been loaded. It will be used when
@@ -563,10 +571,10 @@ let partial2 = try Template(string: "{{occupation}}")
// Two different renderings of the same template:
// "Georges Brassens"
-let data1 = ["user": Box(user), "partial": Box(partial1) ]
+let data1: [String: Any] = ["user": user, "partial": partial1]
try template.render(Box(data1))
// "Singer"
-let data2 = ["user": Box(user), "partial": Box(partial2) ]
+let data2: [String: Any] = ["user": user, "partial": partial2]
try template.render(Box(data2))
```
@@ -688,7 +696,7 @@ GRMustache.swift interprets two pragma tags that set the content type of the tem
In a **text template**, there is no HTML-escaping. Both `{{name}}` and `{{{name}}}` have the same rendering. Text templates are globally HTML-escaped when included in HTML templates.
-For a more complete discussion, see the documentation of `Configuration.contentType` in [Configuration.swift](Sources/Configuration.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Structs/Configuration.html)).
+For a more complete discussion, see the documentation of `Configuration.contentType` in [Configuration.swift](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Structs/Configuration.html).
The Context Stack and Expressions
@@ -802,83 +810,15 @@ template.render(Box(profile))
This boxing is required by the rendering engine. Some types can be boxed and rendered, some can not, and some require some help.
-- [Boxable Types](#boxable-types)
-- [Non Boxable Types](#non-boxable-types)
-- [Imprecise Types](#imprecise-types)
-- [Note to Library Developers](#note-to-library-developers)
-
-
-### Boxable Types
-
The types that can feed templates are:
-- `NSObject` and all its subclasses.
-- All types conforming to `MustacheBoxable` such as `String`, `Int`.
-- Collections and dictionaries of `MustacheBoxable`: `[Int]`, `[String: String]`, `Set`.
-- A few function types such as `FilterFunction` (that we will see later).
-
-Check the rendering of [Custom Types](#custom-types) below for more information about `MustacheBoxable`.
-
-**Caveat**: You may experience boxing issues with nested collections, such as dictionaries of arrays, arrays of dictionaries, etc. Even though they only contain granted boxable values, the compiler complains:
-
-```Swift
-Box(["numbers": 1..<10]) // Compiler error
-```
-
-Well, Swift won't help us here. When this happens, you have to box further:
-
-```Swift
-Box(["numbers": Box(1..<10)]) // Fine
-```
-
-
-### Non Boxable Types
-
-Some types can not be boxed, because the rendering engine does not know what do to with them. Think of tuples, and most function types. When you try to box them, you get a compiler error.
-
-
-### Imprecise Types
-
-`Any`, `AnyObject`, `[String: Any]`, `[AnyObject]` et al. can not be directly boxed. There is no way for GRMustache.swift to render values it does not know anything about. Worse: those types may hide values that are not boxable at all.
-
-You must turn them into a known boxable type before they can feed templates. And that should not be a problem for you, since you do not feed your templates with random data, do you?
-
-Pick the best of those three options:
-
-1. For boxable types and homogeneous collections of such types, perform a simple conversion with the `as!` operator: `Box(value as! [String:Int])`.
-
-2. For mixed collections of values that are compatible with Objective-C, a conversion to NSArray or NSDictionary will make it: `Box(value as! NSDictionary)`.
-
-3. For data soups, no conversions will work. We suggest you create a `MustacheBox`, `[MustacheBox]` or `[String:MustacheBox]` by hand. See [issue #8](https://github.com/groue/GRMustache.swift/issues/8) for some help.
-
-
-### Note to Library Developers
-
-If you intend to use GRMustache.swift as an internal rendering engine, and wrap it around your own APIs, you will be facing with a dilemna as soon as you'll want to let your own library users provide rendered data.
-
-Say you want to provide an API to build a HTTP Response while hiding the template house-keeping:
-
-```swift
-class HTTPController {
- func responseWithTemplateNamed(templateName: String, data: ???) -> HTTPResponse {
- let template = try templateRepository.template(named: templateName)
- let rendering = try template.render(Box(data))
- return HTTPResponse(string: rendering)
- }
-}
-```
-
-You'll wonder what type the `data` parameter should be.
-
-- `Any` or `AnyObject`? We have seen above that they may hide non-boxable values. GRMustache.swift won't help you, because Swift won't help either: it makes it impossible to write a general `func Box(Any?) -> MustacheBox?` function that covers everything (especially, Swift can't recognize collections of boxable values).
-
-- `MustacheBox`? This would sure work, but now your library has married GRMustache.swift for good, and your library users are exposed to an alien library.
-
-- `MustacheBoxable`? Again, we have strong coupling, but worse: No Swift collection adopts this protocol. No Swift collection of boxable values can adopt any protocol, actually, because Swift won't let any specialization of a generic type adopt a protocol. Forget Dictionary, Array, Set, etc.
-
-- `NSObject` or `NSDictionary`? This one would sure work. But now your users have no way to use the extra features of GRMustache.swift such as [filters](#filters) and [lambdas](#lambdas), etc. Consider that lambdas are part of the Mustache specification: your users may expect them to be available as soon as you claim to use a Mustache back end.
-
-There is no silver bullet here: marry me, or live a dull life. [Suggestions are welcome](https://github.com/groue/GRMustache.swift/issues).
+- All types conforming to `MustacheBoxable` such as `String`, `Int`, `NSObject` and its subclasses (see [Standard Swift Types Reference](#standard-swift-types-reference) and [Custom Types](#custom-types))
+
+- Arrays, sets, and dictionaries (`[Any?]`, `Set`, `[AnyHashable: Any?]`, NSArray, NSSet and NSDictionary). *This does not include other sequences and collections, such as Swift ranges.*
+
+- A few function types such as [filter functions](#filters), [lambdas](#lambdas), and other functions involved in [advanced boxes](#advanced-boxes).
+
+- [Goodies](Docs/Guides/goodies.md) such as Foundation's formatters.
Standard Swift Types Reference
@@ -887,13 +827,12 @@ Standard Swift Types Reference
GRMustache.swift comes with built-in support for the following standard Swift types:
- [Bool](#bool)
-- [Numeric Types](#numeric-types): Int, UInt, Int64, UInt64 and Double
+- [Numeric Types](#numeric-types): Int, UInt, Int64, UInt64, Float, Double and CGFloat.
- [String](#string)
-- [Set](#set) (and similar collections)
-- [Array](#array) (and similar collections)
+- [Set](#set)
+- [Array](#array)
- [Dictionary](#dictionary)
- [NSObject](#nsobject)
-- [AnyObject and Any](#anyobject-and-any)
### Bool
@@ -905,19 +844,19 @@ GRMustache.swift comes with built-in support for the following standard Swift ty
### Numeric Types
-GRMustache supports `Int`, `UInt`, `Int64`, `UInt64` and `Double`:
+GRMustache supports `Int`, `UInt`, `Int64`, `UInt64`, `Float` and `Double`:
- `{{number}}` renders the standard Swift string interpolation of *number*.
- `{{#number}}...{{/number}}` renders if and only if *number* is not 0 (zero).
- `{{^number}}...{{/number}}` renders if and only if *number* is 0 (zero).
-The Swift types `Float`, `Int8`, `UInt8`, etc. have no built-in support: turn them into one of the three general types before injecting them into templates.
+The Swift types `Int8`, `UInt8`, etc. have no built-in support: turn them into one of the three general types before injecting them into templates.
-To format numbers, use `NSNumberFormatter`:
+To format numbers, you can use `NumberFormatter`:
```swift
-let percentFormatter = NSNumberFormatter()
-percentFormatter.numberStyle = .PercentStyle
+let percentFormatter = NumberFormatter()
+percentFormatter.numberStyle = .percent
let template = try Template(string: "{{ percent(x) }}")
template.registerInBaseContext("percent", Box(percentFormatter))
@@ -927,7 +866,7 @@ let data = ["x": 0.5]
let rendering = try template.render(Box(data))
```
-[More info on NSFormatter](Docs/Guides/goodies.md#nsformatter).
+[More info on Formatter](Docs/Guides/goodies.md#formatter).
### String
@@ -953,10 +892,6 @@ Exposed keys:
- `set.first`: the first element.
- `set.count`: the number of elements in the set.
-GRMustache.swift renders as `Set` all types that provide iteration, access to the first element, and to the elements count. Precisely: `CollectionType where Index.Distance == Int`.
-
-Sets must contain boxable values. Check the [Boxing Values](#boxing-values) chapter for more information.
-
### Array
@@ -970,18 +905,6 @@ Exposed keys:
- `array.last`: the last element.
- `array.count`: the number of elements in the array.
-GRMustache.swift renders as `Array` all types that provide iteration, access to the first and last elements, and to the elements count. Precisely: `CollectionType where Index: BidirectionalIndexType, Index.Distance == Int`.
-
-`Range` is such a type:
-
-```swift
-// 123456789
-let template = try Template(string: "{{ numbers }}")
-let rendering = try template.render(Box(["numbers": Box(1..<10)]))
-```
-
-Arrays must contain boxable values. Check the [Boxing Values](#boxing-values) chapter for more information.
-
### Dictionary
@@ -989,8 +912,6 @@ Arrays must contain boxable values. Check the [Boxing Values](#boxing-values) ch
- `{{#dictionary}}...{{/dictionary}}` renders once, pushing the dictionary on top of the [context stack](#the-context-stack).
- `{{^dictionary}}...{{/dictionary}}` does not render.
-Dictionary keys must be String, and its values must be boxable. Check the [Boxing Values](#boxing-values) chapter for more information.
-
### NSObject
@@ -1000,7 +921,7 @@ The rendering of NSObject depends on the actual class:
When an object conforms to the NSFastEnumeration protocol, like **NSArray**, it renders just like Swift [Array](#array). **NSSet** is an exception, rendered as a Swift [Set](#set). **NSDictionary**, the other exception, renders as a Swift [Dictionary](#dictionary).
-- **NSNumber** is rendered as a Swift [Bool](#bool), [Int, UInt, Int64, UInt64 or Double](#numeric-types), depending on its value.
+- **NSNumber** is rendered as a Swift [Bool](#bool), [Int, UInt, Int64, UInt64, Float or Double](#numeric-types), depending on its value.
- **NSString** is rendered as [String](#string)
@@ -1022,43 +943,12 @@ The rendering of NSObject depends on the actual class:
Subclasses can alter this behavior by overriding the `mustacheBox` method of the `MustacheBoxable` protocol. For more information, check the rendering of [Custom Types](#custom-types) below.
-### AnyObject and Any
-
-When you try to render a value of type `Any` or `AnyObject`, you get a compiler error:
-
-```swift
-let any: Any = ...
-let anyObject: AnyObject = ...
-
-let rendering = try template.render(Box(any))
-// Compiler Error
-
-let rendering = try template.render(Box(anyObject))
-// Compiler Error
-```
-
-The solution is to convert the value to its actual type:
-
-```swift
-let json: AnyObject = ["name": "Lionel Richie"]
-
-// Convert to Dictionary:
-let dictionary = json as! [String: String]
-
-// Lionel Richie has a Mustache.
-let template = try Template(string: "{{ name }} has a Mustache.")
-let rendering = try template.render(Box(dictionary))
-```
-
-The same kind of boxing trouble happens for collections of general types like `[String: Any]` or `[AnyObject]`. For more information, check the [Boxing Values](#boxing-values) chapter.
-
-
Custom Types
------------
### NSObject subclasses
-With support for Objective-C runtime (on Apple platforms), your NSObject subclass can trivially feed your templates:
+NSObject subclasses can trivially feed your templates:
```swift
// An NSObject subclass
@@ -1132,11 +1022,11 @@ let wrapped = Lambda { (string) in "\(string)" }
// Frank Zappa is awesome.
let templateString = "{{#wrapped}}{{fullName}} is awesome.{{/wrapped}}"
let template = try Template(string: templateString)
-let data = [
- "firstName": Box("Frank"),
- "lastName": Box("Zappa"),
- "fullName": Box(fullName),
- "wrapped": Box(wrapped)]
+let data: [String: Any] = [
+ "firstName": "Frank",
+ "lastName": "Zappa",
+ "fullName": fullName,
+ "wrapped": wrapped]
let rendering = try template.render(Box(data))
```
@@ -1204,21 +1094,14 @@ Filters can accept a precisely typed argument as above. You may prefer managing
```swift
// Define the `abs` filter.
//
-// abs(x) evaluates to the absolute value of x (Int, UInt, Int64, UInt64 or Double):
+// abs(x) evaluates to the absolute value of x (Int or Double):
let absFilter = Filter { (box: MustacheBox) in
switch box.value {
case let int as Int:
return Box(abs(int))
- case let int64 as Int64:
- return Box(abs(int64))
- case let uint as UInt:
- return Box(uint)
- case let uint64 as UInt64:
- return Box(uint64)
case let double as Double:
return Box(abs(double))
default:
- // GRMustache does not support any other numeric types: give up.
return Box()
}
}
@@ -1233,8 +1116,8 @@ You can process collections and dictionaries as well, and return new ones:
// oneEveryTwoItems(collection) returns the array of even items in the input
// collection.
let oneEveryTwoItems = Filter { (box: MustacheBox) in
- // `box.arrayValue` returns a `[MustacheBox]` whatever the boxed Swift
- // or Foundation collection (Array, Set, NSOrderedSet, etc.).
+ // `box.arrayValue` returns a `[MustacheBox]` for all boxed collections
+ // (Array, Set, NSArray, etc.).
guard let boxes = box.arrayValue else {
// No value, or not a collection: return the empty box
return Box()
@@ -1242,7 +1125,7 @@ let oneEveryTwoItems = Filter { (box: MustacheBox) in
// Rebuild another array with even indexes:
var result: [MustacheBox] = []
- for (index, box) in boxes.enumerate() where index % 2 == 0 {
+ for (index, box) in boxes.enumerated() where index % 2 == 0 {
result.append(box)
}
@@ -1258,7 +1141,7 @@ let template = try Template(string: templateString)
template.registerInBaseContext("oneEveryTwoItems", Box(oneEveryTwoItems))
// <1><3><5><7><9>
-let rendering = try template.render(Box(["items": Box(1..<10)]))
+let rendering = try template.render(Box(["items": Array(1..<10)]))
```
@@ -1290,11 +1173,11 @@ Filters can chain and generally be part of more complex expressions:
Circle area is {{ format(product(PI, circle.radius, circle.radius)) }} cm².
-When you want to format values, just use NSNumberFormatter, NSDateFormatter, or any NSFormatter. They are ready-made filters:
+When you want to format values, just use NumberFormatter, DateFormatter, or generally any Foundation's Formatter. They are ready-made filters:
```swift
-let percentFormatter = NSNumberFormatter()
-percentFormatter.numberStyle = .PercentStyle
+let percentFormatter = NumberFormatter()
+percentFormatter.numberStyle = .percent
let template = try Template(string: "{{ percent(x) }}")
template.registerInBaseContext("percent", Box(percentFormatter))
@@ -1304,7 +1187,7 @@ let data = ["x": 0.5]
let rendering = try template.render(Box(data))
```
-[More info on NSFormatter](Docs/Guides/goodies.md#nsformatter).
+[More info on formatters](Docs/Guides/goodies.md#formatter).
### Pre-Rendering Filters
@@ -1318,7 +1201,7 @@ You can, for example, reverse a rendering:
//
// reverse(x) renders the reversed rendering of its argument:
let reverse = Filter { (rendering: Rendering) in
- let reversedString = String(rendering.string.characters.reverse())
+ let reversedString = String(rendering.string.characters.reversed())
return Rendering(reversedString, rendering.contentType)
}
@@ -1358,7 +1241,7 @@ let pluralize = Filter { (count: Int?, info: RenderingInfo) in
var string = info.tag.innerTemplateString
// Pluralize if needed:
- if count > 1 {
+ if let count = count, count > 1 {
string += "s" // naive
}
@@ -1424,7 +1307,7 @@ The `MustacheBox` type that feeds templates is able to wrap many different behav
{{/ user }}
```
-- `Box(NSFormatter)` returns a box that is able to format a single value, or all values inside a section ([more information](Docs/Guides/goodies.md#nsformatter)):
+- `Box(Formatter)` returns a box that is able to format a single value, or all values inside a section ([more information](Docs/Guides/goodies.md#formatter)):
```
{{ format(date) }}
@@ -1453,7 +1336,7 @@ This variety of behaviors is available through public APIs. Before we dig into t
3. The rendering engine then looks in the context stack for all boxes that have a customized `WillRenderFunction`. Those functions have an opportunity to process the Result box, and eventually return another one.
- This is how, for example, a boxed [NSDateFormatter](Docs/Guides/goodies.md#nsformatter) can format all dates in a section: its `WillRenderFunction` formats dates into strings.
+ This is how, for example, a boxed [DateFormatter](Docs/Guides/goodies.md#formatter) can format all dates in a section: its `WillRenderFunction` formats dates into strings.
4. The resulting box is ready to be rendered. For regular and inverted section tags, the rendering engine queries the customizable boolean value of the box, so that `{{# F(A) }}...{{/}}` and `{{^ F(A) }}...{{/}}` can't be both rendered.
From 322660b8f301663ef2e544a398c4ffb333ae0e29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sat, 22 Oct 2016 15:36:22 +0200
Subject: [PATCH 18/46] #32: I saw the light: we can get rid of Box()
---
.../ConfigurationExtendBaseContextTests.swift | 2 +-
...ntextValueForMustacheExpressionTests.swift | 2 +-
.../MustacheBoxDocumentationTests.swift | 4 +-
.../MustacheRenderableGuideTests.swift | 48 ++++++++--------
Tests/Public/FilterTests/FilterTests.swift | 56 +++++++++----------
Tests/Public/FoundationCollectionTests.swift | 4 +-
Tests/Public/HookFunctionTests.swift | 14 ++---
Tests/Public/LambdaTests.swift | 44 +++++++--------
Tests/Public/RenderFunctionTests.swift | 50 ++++++++---------
.../ServicesTests/EachFilterTests.swift | 12 ++--
.../Public/ServicesTests/LocalizerTests.swift | 28 +++++-----
.../ServicesTests/NSFormatterTests.swift | 40 ++++++-------
.../twitter/hogan.js/HoganSuite.swift | 4 +-
.../Public/TemplateTests/TemplateTests.swift | 2 +-
14 files changed, 155 insertions(+), 155 deletions(-)
diff --git a/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift b/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift
index d2c8114f..d303f66e 100644
--- a/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift
+++ b/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift
@@ -28,7 +28,7 @@ class ConfigurationExtendBaseContextTests: XCTestCase {
func testConfigurationExtendBaseContextWithValue() {
var configuration = Configuration()
- configuration.extendBaseContext(Box(["name": Box("Arthur")]))
+ configuration.extendBaseContext(Box(["name": "Arthur"]))
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{name}}")
diff --git a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
index 0d06413d..78bd36d3 100644
--- a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
+++ b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
@@ -51,7 +51,7 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
let filter = Filter({ (string: String?) -> MustacheBox in
return Box(string!.uppercased())
})
- let context = Context(Box(["name": Box("success"), "f": Box(filter)]))
+ let context = Context(Box(["name": "success", "f": filter]))
let box = try! context.mustacheBoxForExpression("f(name)")
let string = box.value as? String
XCTAssertEqual(string!, "SUCCESS")
diff --git a/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift b/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
index b95500a1..203a5036 100644
--- a/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
+++ b/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
@@ -31,7 +31,7 @@ class MustacheBoxDocumentationTests: XCTestCase {
return Rendering("foo")
}
let template = try! Template(string: "{{object}}")
- let data = ["object": Box(render)]
+ let data = ["object": render]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "foo")
}
@@ -52,7 +52,7 @@ class MustacheBoxDocumentationTests: XCTestCase {
return try info.tag.render(context)
}
}
- let data = ["object": Box(render)]
+ let data = ["object": render]
// Renders "variable"
let template1 = try! Template(string: "{{object}}")
diff --git a/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift b/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
index 1540688d..66df57d7 100644
--- a/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
+++ b/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
@@ -59,8 +59,8 @@ class MustacheRenderableGuideTests: XCTestCase {
}
let box = Box([
- "strong": Box(render),
- "name": Box("Arthur")])
+ "strong": render,
+ "name": "Arthur"])
let rendering = try! Template(string: "{{#strong}}{{name}}{{/strong}}").render(box)
XCTAssertEqual(rendering, "Arthur")
}
@@ -70,7 +70,7 @@ class MustacheRenderableGuideTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering(rendering.string + rendering.string, rendering.contentType)
}
- let box = Box(["twice": Box(render)])
+ let box = Box(["twice": render])
let rendering = try! Template(string: "{{#twice}}Success{{/twice}}").render(box)
XCTAssertEqual(rendering, "SuccessSuccess")
}
@@ -81,9 +81,9 @@ class MustacheRenderableGuideTests: XCTestCase {
return try template.render(info.context)
}
let box = Box([
- "link": Box(render),
- "name": Box("Arthur"),
- "url": Box("/people/123")])
+ "link": render,
+ "name": "Arthur",
+ "url": "/people/123"])
let rendering = try! Template(string: "{{# link }}{{ name }}{{/ link }}").render(box)
XCTAssertEqual(rendering, "Arthur")
}
@@ -92,17 +92,17 @@ class MustacheRenderableGuideTests: XCTestCase {
let repository = TemplateRepository(templates: [
"movieLink": "{{title}}",
"personLink": "{{name}}"])
- let link1 = Box(try! repository.template(named: "movieLink"))
- let item1 = Box([
- "title": Box("Citizen Kane"),
- "url": Box("/movies/321"),
- "link": link1])
- let link2 = Box(try! repository.template(named: "personLink"))
- let item2 = Box([
- "name": Box("Orson Welles"),
- "url": Box("/people/123"),
- "link": link2])
- let box = Box(["items": Box([item1, item2])])
+ let link1 = try! repository.template(named: "movieLink")
+ let item1: [String: Any] = [
+ "title": "Citizen Kane",
+ "url": "/movies/321",
+ "link": link1]
+ let link2 = try! repository.template(named: "personLink")
+ let item2: [String: Any] = [
+ "name": "Orson Welles",
+ "url": "/people/123",
+ "link": link2]
+ let box = Box(["items": [item1, item2]])
let rendering = try! Template(string: "{{#items}}{{link}}{{/items}}").render(box)
XCTAssertEqual(rendering, "Citizen KaneOrson Welles")
}
@@ -164,7 +164,7 @@ class MustacheRenderableGuideTests: XCTestCase {
let movie = Movie(title:"Citizen Kane", director: director)
let template = try! Template(string: "{{ movie }}")
- let rendering = try! template.render(Box(["movie": Box(movie)]))
+ let rendering = try! template.render(Box(["movie": movie]))
XCTAssertEqual(rendering, "Citizen Kane by Orson Welles")
}
@@ -185,15 +185,15 @@ class MustacheRenderableGuideTests: XCTestCase {
}
let template = try! Template(string: "{{#list(nav)}}{{title}}{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["list": Box(Filter(listFilter))]))
+ template.baseContext = template.baseContext.extendedContext(Box(["list": Filter(listFilter)]))
- let item1 = Box([
+ let item1 = [
"url": "http://mustache.github.io",
- "title": "Mustache"])
- let item2 = Box([
+ "title": "Mustache"]
+ let item2 = [
"url": "http://github.com/groue/GRMustache.swift",
- "title": "GRMustache.swift"])
- let box = Box(["nav": Box([item1, item2])])
+ "title": "GRMustache.swift"]
+ let box = Box(["nav": [item1, item2]])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "")
diff --git a/Tests/Public/FilterTests/FilterTests.swift b/Tests/Public/FilterTests/FilterTests.swift
index b1c67ca5..cdcaac86 100644
--- a/Tests/Public/FilterTests/FilterTests.swift
+++ b/Tests/Public/FilterTests/FilterTests.swift
@@ -32,13 +32,13 @@ class FilterTests: XCTestCase {
func testFilterCanChain() {
let box = Box([
- "name": Box("Name"),
- "uppercase": Box(Filter { (string: String?) -> MustacheBox in
+ "name": "Name",
+ "uppercase": Filter { (string: String?) -> MustacheBox in
return Box(string?.uppercased())
- }),
- "prefix": Box(Filter { (string: String?) -> MustacheBox in
+ },
+ "prefix": Filter { (string: String?) -> MustacheBox in
return Box("prefix\(string!)")
- })
+ }
])
let template = try! Template(string:"<{{name}}> <{{prefix(name)}}> <{{uppercase(name)}}> <{{prefix(uppercase(name))}}> <{{uppercase(prefix(name))}}>")
let rendering = try! template.render(box)
@@ -51,31 +51,31 @@ class FilterTests: XCTestCase {
var rendering: String
box = Box([
- "object": Box(["name": "objectName"]),
- "name": Box("rootName"),
- "f": Box(Filter { (box: MustacheBox) -> MustacheBox in
+ "object": ["name": "objectName"],
+ "name": "rootName",
+ "f": Filter { (box: MustacheBox) -> MustacheBox in
return box
- })
+ }
])
rendering = try! template.render(box)
XCTAssertEqual(rendering, " ")
box = Box([
- "object": Box(["name": "objectName"]),
- "name": Box("rootName"),
- "f": Box(Filter { (_: MustacheBox) -> MustacheBox in
+ "object": ["name": "objectName"],
+ "name": "rootName",
+ "f": Filter { (_: MustacheBox) -> MustacheBox in
return Box(["name": "filterName"])
- })
+ }
])
rendering = try! template.render(box)
XCTAssertEqual(rendering, " ")
box = Box([
- "object": Box(["name": "objectName"]),
- "name": Box("rootName"),
- "f": Box(Filter { (_: MustacheBox) -> MustacheBox in
+ "object": ["name": "objectName"],
+ "name": "rootName",
+ "f": Filter { (_: MustacheBox) -> MustacheBox in
return Box(true)
- })
+ }
])
rendering = try! template.render(box)
XCTAssertEqual(rendering, "<> ")
@@ -83,11 +83,11 @@ class FilterTests: XCTestCase {
func testFilterArgumentsDoNotEnterSectionContextStack() {
let box = Box([
- "test": Box("success"),
- "filtered": Box(["test": "failure"]),
- "filter": Box(Filter { (_: MustacheBox) -> MustacheBox in
+ "test": "success",
+ "filtered": ["test": "failure"],
+ "filter": Filter { (_: MustacheBox) -> MustacheBox in
return Box(true)
- })])
+ }])
let template = try! Template(string:"{{#filter(filtered)}}<{{test}} instead of {{#filtered}}{{test}}{{/filtered}}>{{/filter(filtered)}}")
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "")
@@ -98,8 +98,8 @@ class FilterTests: XCTestCase {
return Box((x ?? 0) * 2)
})
let box = Box([
- "x": Box(1),
- "math": Box(["double": doubleFilter])
+ "x": 1,
+ "math": ["double": doubleFilter]
])
let template = try! Template(string:"{{ math.double(x) }}")
let rendering = try! template.render(box)
@@ -113,8 +113,8 @@ class FilterTests: XCTestCase {
})
})
let box = Box([
- "prefix": Box("prefix"),
- "value": Box("value"),
+ "prefix": "prefix",
+ "value": "value",
"f": filterValue])
let template = try! Template(string:"{{f(prefix)(value)}}")
let rendering = try! template.render(box)
@@ -132,10 +132,10 @@ class FilterTests: XCTestCase {
func testMissingFilterError() {
let box = Box([
- "name": Box("Name"),
- "replace": Box(Filter { (_: MustacheBox) -> MustacheBox in
+ "name": "Name",
+ "replace": Filter { (_: MustacheBox) -> MustacheBox in
return Box("replace")
- })
+ }
])
var template = try! Template(string:"<{{missing(missing)}}>")
diff --git a/Tests/Public/FoundationCollectionTests.swift b/Tests/Public/FoundationCollectionTests.swift
index 68c7ec51..f5c02338 100644
--- a/Tests/Public/FoundationCollectionTests.swift
+++ b/Tests/Public/FoundationCollectionTests.swift
@@ -34,7 +34,7 @@ class FoundationCollectionTests: XCTestCase {
var boxedNSOrderedSet: MustacheBox!
override func setUp() {
- boxedArray = Box(["collection": Box([["key": "value"] as NSDictionary])])
+ boxedArray = Box(["collection": [["key": "value"] as NSDictionary]])
boxedNSArray = {
let array = NSMutableArray()
array.add(["key": "value"])
@@ -42,7 +42,7 @@ class FoundationCollectionTests: XCTestCase {
data.setObject(array, forKey: "collection" as NSString)
return Box(data)
}()
- boxedSet = Box(["collection": Box(Set([["key": "value"] as NSDictionary]))])
+ boxedSet = Box(["collection": Set([["key": "value"] as NSDictionary])])
boxedNSSet = {
let set = NSMutableSet()
set.add(["key": "value"])
diff --git a/Tests/Public/HookFunctionTests.swift b/Tests/Public/HookFunctionTests.swift
index cc7dd758..226ee0b7 100644
--- a/Tests/Public/HookFunctionTests.swift
+++ b/Tests/Public/HookFunctionTests.swift
@@ -199,7 +199,7 @@ class HookFunctionTests: XCTestCase {
template.baseContext = template.baseContext.extendedContext(Box(willRender))
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["filter": Box(Filter(filter))]))
+ rendering = try! template.render(Box(["filter": Filter(filter)]))
XCTAssertEqual(rendering, "")
XCTAssertEqual(willRenderCount, 1)
XCTAssertTrue(renderedValue!.isEmpty)
@@ -208,7 +208,7 @@ class HookFunctionTests: XCTestCase {
template.baseContext = template.baseContext.extendedContext(Box(willRender))
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": Box("foo"), "filter": Box(Filter(filter))]))
+ rendering = try! template.render(Box(["subject": "foo", "filter": Filter(filter)]))
XCTAssertEqual(rendering, "FOO")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! String), "FOO")
@@ -217,7 +217,7 @@ class HookFunctionTests: XCTestCase {
template.baseContext = template.baseContext.extendedContext(Box(willRender))
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": Box("foo"), "filter": Box(Filter(filter))]))
+ rendering = try! template.render(Box(["subject": "foo", "filter": Filter(filter)]))
XCTAssertEqual(rendering, "3")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! Int), 3)
@@ -343,7 +343,7 @@ class HookFunctionTests: XCTestCase {
let box = Box([
"observer2": MustacheBox(willRender: willRender2, didRender: didRender2),
"observer3": MustacheBox(willRender: willRender3, didRender: didRender3),
- "observed": Box("observed")
+ "observed": "observed"
])
_ = try! template.render(box)
@@ -370,7 +370,7 @@ class HookFunctionTests: XCTestCase {
}
let template = try! Template(string: "{{#items}}{{.}}{{/items}}")
- let box = Box(["items": Box([Box(willRender1), Box(willRender2)])])
+ let box = Box(["items": [willRender1, willRender2]])
_ = try! template.render(box)
XCTAssertTrue(willRenderCalled1)
@@ -388,7 +388,7 @@ class HookFunctionTests: XCTestCase {
var render = { (info: RenderingInfo) -> Rendering in
return Rendering("&you")
}
- var box = Box(["object": Box(render), "observer": Box(willRender)])
+ var box = Box(["object": render, "observer": willRender])
var template = try! Template(string: "{{# observer }}{{ object }}{{/ }}")
var rendering = try! template.render(box)
XCTAssertEqual(rendering, "&YOU")
@@ -396,7 +396,7 @@ class HookFunctionTests: XCTestCase {
render = { (info: RenderingInfo) -> Rendering in
return Rendering("&you", .html)
}
- box = Box(["object": Box(render), "observer": Box(willRender)])
+ box = Box(["object": render, "observer": willRender])
template = try! Template(string: "{{# observer }}{{ object }}{{/ }}")
rendering = try! template.render(box)
XCTAssertEqual(rendering, "&YOU")
diff --git a/Tests/Public/LambdaTests.swift b/Tests/Public/LambdaTests.swift
index 0d0a84b5..2e29c87c 100644
--- a/Tests/Public/LambdaTests.swift
+++ b/Tests/Public/LambdaTests.swift
@@ -31,7 +31,7 @@ class LambdaTests: XCTestCase {
let lambda = Lambda { "world" }
let template = try! Template(string: "Hello, {{lambda}}!")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "Hello, world!")
@@ -41,9 +41,9 @@ class LambdaTests: XCTestCase {
// https://github.com/mustache/spec/blob/83b0721610a4e11832e83df19c73ace3289972b9/specs/%7Elambdas.yml#L29
let lambda = Lambda { "{{planet}}" }
let template = try! Template(string: "Hello, {{lambda}}!")
- let data = [
- "planet": Box("world"),
- "lambda": Box(lambda),
+ let data: [String: Any] = [
+ "planet": "world",
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "Hello, world!")
@@ -55,9 +55,9 @@ class LambdaTests: XCTestCase {
// not honor mustache spec white space rules.
let lambda = Lambda { "|planet| => {{planet}}" }
let template = try! Template(string: "{{= | | =}}Hello, (|&lambda|)!")
- let data = [
- "planet": Box("world"),
- "lambda": Box(lambda),
+ let data: [String: Any] = [
+ "planet": "world",
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "Hello, (|planet| => world)!")
@@ -69,7 +69,7 @@ class LambdaTests: XCTestCase {
let lambda = Lambda { calls += 1; return "\(calls)" }
let template = try! Template(string: "{{lambda}} == {{{lambda}}} == {{lambda}}")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "1 == 2 == 3")
@@ -80,7 +80,7 @@ class LambdaTests: XCTestCase {
let lambda = Lambda { ">" }
let template = try! Template(string: "<{{lambda}}{{{lambda}}}")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "<>>")
@@ -97,7 +97,7 @@ class LambdaTests: XCTestCase {
}
let template = try! Template(string: "<{{#lambda}}{{x}}{{/lambda}}>")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "")
@@ -109,9 +109,9 @@ class LambdaTests: XCTestCase {
return "\(string){{planet}}\(string)"
}
let template = try! Template(string: "<{{#lambda}}-{{/lambda}}>")
- let data = [
- "planet": Box("Earth"),
- "lambda": Box(lambda),
+ let data: [String: Any] = [
+ "planet": "Earth",
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "<-Earth->")
@@ -123,9 +123,9 @@ class LambdaTests: XCTestCase {
return "\(string){{planet}} => |planet|\(string)"
}
let template = try! Template(string: "{{= | | =}}<|#lambda|-|/lambda|>")
- let data = [
- "planet": Box("Earth"),
- "lambda": Box(lambda),
+ let data: [String: Any] = [
+ "planet": "Earth",
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "<-{{planet}} => Earth->")
@@ -138,7 +138,7 @@ class LambdaTests: XCTestCase {
}
let template = try! Template(string: "{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "__FILE__ != __LINE__")
@@ -151,7 +151,7 @@ class LambdaTests: XCTestCase {
}
let template = try! Template(string: "<{{^lambda}}{{static}}{{/lambda}}>")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "<>")
@@ -164,7 +164,7 @@ class LambdaTests: XCTestCase {
let lambda = Lambda { "{{>partial}}" }
let template = try! templateRepository.template(string: "<{{lambda}}>")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
do {
_ = try template.render(Box(data))
@@ -183,7 +183,7 @@ class LambdaTests: XCTestCase {
let lambda = Lambda { (string: String) in "{{>partial}}" }
let template = try! templateRepository.template(string: "<{{#lambda}}...{{/lambda}}>")
let data = [
- "lambda": Box(lambda),
+ "lambda": lambda,
]
do {
_ = try template.render(Box(data))
@@ -198,14 +198,14 @@ class LambdaTests: XCTestCase {
func testArity0LambdaInSectionTag() {
let lambda = Lambda { "success" }
let template = try! Template(string: "{{#lambda}}<{{.}}>{{/lambda}}")
- let rendering = try! template.render(Box(["lambda": Box(lambda)]))
+ let rendering = try! template.render(Box(["lambda": lambda]))
XCTAssertEqual(rendering, "")
}
func testArity1LambdaInVariableTag() {
let lambda = Lambda { (string) in string }
let template = try! Template(string: "<{{lambda}}>")
- let rendering = try! template.render(Box(["lambda": Box(lambda)]))
+ let rendering = try! template.render(Box(["lambda": lambda]))
XCTAssertEqual(rendering, "<(Lambda)>")
}
}
diff --git a/Tests/Public/RenderFunctionTests.swift b/Tests/Public/RenderFunctionTests.swift
index fed58cb4..6dc34b66 100644
--- a/Tests/Public/RenderFunctionTests.swift
+++ b/Tests/Public/RenderFunctionTests.swift
@@ -213,7 +213,7 @@ class RenderFunctionTests: XCTestCase {
return tagRendering!
}
- let box = Box(["render": Box(render), "subject": Box("-")])
+ let box = Box(["render": render, "subject": "-"])
_ = try! Template(string: "{{#render}}{{subject}}={{subject}}{{/render}}").render(box)
XCTAssertEqual(tagRendering!.string, "-=-")
XCTAssertEqual(tagRendering!.contentType, ContentType.html)
@@ -249,7 +249,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return try altTemplate.render(info.context)
}
- let box = Box(["render": Box(render), "subject": Box("-")])
+ let box = Box(["render": render, "subject": "-"])
let rendering = try! Template(string: "{{render}}").render(box)
XCTAssertEqual(rendering, "-")
}
@@ -259,7 +259,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return try altTemplate.render(info.context)
}
- let box = Box(["render": Box(render), "subject": Box("-")])
+ let box = Box(["render": render, "subject": "-"])
let rendering = try! Template(string: "{{#render}}{{/render}}").render(box)
XCTAssertEqual(rendering, "-")
}
@@ -290,20 +290,20 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionCanExtendValueContextStackInVariableTag() {
let render = { (info: RenderingInfo) -> Rendering in
- let context = info.context.extendedContext(Box(["subject2": Box("+++")]))
+ let context = info.context.extendedContext(Box(["subject2": "+++"]))
let template = try! Template(string: "{{subject}}{{subject2}}")
return try template.render(context)
}
- let box = Box(["render": Box(render), "subject": Box("---")])
+ let box = Box(["render": render, "subject": "---"])
let rendering = try! Template(string: "{{render}}").render(box)
XCTAssertEqual(rendering, "---+++")
}
func testRenderFunctionCanExtendValueContextStackInSectionTag() {
let render = { (info: RenderingInfo) -> Rendering in
- return try info.tag.render(info.context.extendedContext(Box(["subject2": Box("+++")])))
+ return try info.tag.render(info.context.extendedContext(Box(["subject2": "+++"])))
}
- let box = Box(["render": Box(render), "subject": Box("---")])
+ let box = Box(["render": render, "subject": "---"])
let rendering = try! Template(string: "{{#render}}{{subject}}{{subject2}}{{/render}}").render(box)
XCTAssertEqual(rendering, "---+++")
}
@@ -318,7 +318,7 @@ class RenderFunctionTests: XCTestCase {
let template = try! Template(string: "{{subject}}{{subject}}")
return try template.render(context)
}
- let box = Box(["render": Box(render), "subject": Box("-")])
+ let box = Box(["render": render, "subject": "-"])
let rendering = try! Template(string: "{{subject}}{{render}}{{subject}}{{subject}}{{subject}}{{subject}}").render(box)
XCTAssertEqual(rendering, "-------")
XCTAssertEqual(tagWillRenderCount, 2)
@@ -332,7 +332,7 @@ class RenderFunctionTests: XCTestCase {
return box
})))
}
- let box = Box(["render": Box(render), "subject": Box("-")])
+ let box = Box(["render": render, "subject": "-"])
let rendering = try! Template(string: "{{subject}}{{#render}}{{subject}}{{subject}}{{/render}}{{subject}}{{subject}}{{subject}}{{subject}}").render(box)
XCTAssertEqual(rendering, "-------")
XCTAssertEqual(tagWillRenderCount, 2)
@@ -354,7 +354,7 @@ class RenderFunctionTests: XCTestCase {
let template = try! Template(string: "{{#render}}{{subject}}{{/render}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- let box = Box(["render": Box(render), "subject": Box("---")])
+ let box = Box(["render": render, "subject": "---"])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "delegate")
}
@@ -376,7 +376,7 @@ class RenderFunctionTests: XCTestCase {
let template = try! Template(string: "{{render}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- let box = Box(["render": Box(render), "subject": Box("---")])
+ let box = Box(["render": render, "subject": "---"])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "delegate")
}
@@ -398,7 +398,7 @@ class RenderFunctionTests: XCTestCase {
let template = try! Template(string: "{{#render}}{{/render}}")
template.baseContext = template.baseContext.extendedContext(Box(willRender))
- let box = Box(["render": Box(render), "subject": Box("---")])
+ let box = Box(["render": render, "subject": "---"])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "delegate")
}
@@ -410,7 +410,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("2")
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
let rendering = try! Template(string: "{{#items}}{{/items}}").render(box)
XCTAssertEqual(rendering, "12")
}
@@ -422,7 +422,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("2")
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
let rendering = try! Template(string: "{{items}}").render(box)
XCTAssertEqual(rendering, "12")
}
@@ -434,7 +434,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
let rendering = try! Template(string: "{{items}}").render(box)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -446,7 +446,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
let rendering = try! Template(string: "{{{items}}}").render(box)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -458,7 +458,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>")
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
let rendering = try! Template(string: "{{items}}").render(box)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -470,7 +470,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>")
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
let rendering = try! Template(string: "{{{items}}}").render(box)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -482,7 +482,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
do {
_ = try Template(string: "{{items}}").render(box)
XCTFail("Expected MustacheError")
@@ -500,7 +500,7 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": Box([Box(render1), Box(render2)])])
+ let box = Box(["items": [render1, render2]])
do {
_ = try Template(string: "{{#items}}{{/items}}").render(box)
XCTFail("Expected MustacheError")
@@ -514,7 +514,7 @@ class RenderFunctionTests: XCTestCase {
func testDynamicPartial() {
let repository = TemplateRepository(templates: ["partial": "{{subject}}"])
let template = try! repository.template(named: "partial")
- let box = Box(["partial": Box(template), "subject": Box("---")])
+ let box = Box(["partial": template, "subject": "---"])
let rendering = try! Template(string: "{{partial}}").render(box)
XCTAssertEqual(rendering, "---")
}
@@ -522,7 +522,7 @@ class RenderFunctionTests: XCTestCase {
func testDynamicPartialIsNotHTMLEscaped() {
let repository = TemplateRepository(templates: ["partial": "<{{subject}}>"])
let template = try! repository.template(named: "partial")
- let box = Box(["partial": Box(template), "subject": Box("---")])
+ let box = Box(["partial": template, "subject": "---"])
let rendering = try! Template(string: "{{partial}}").render(box)
XCTAssertEqual(rendering, "<--->")
}
@@ -532,9 +532,9 @@ class RenderFunctionTests: XCTestCase {
"layout": "<{{$a}}Default{{subject}}{{/a}},{{$b}}Ignored{{/b}}>",
"partial": "[{{#layout}}---{{$b}}Overriden{{subject}}{{/b}}---{{/layout}}]"])
let template = try! repository.template(named: "partial")
- let data = [
- "layout": Box(try! repository.template(named: "layout")),
- "subject": Box("---")]
+ let data: [String: Any] = [
+ "layout": try! repository.template(named: "layout"),
+ "subject": "---"]
let rendering = try! template.render(Box(data))
XCTAssertEqual(rendering, "[]")
}
diff --git a/Tests/Public/ServicesTests/EachFilterTests.swift b/Tests/Public/ServicesTests/EachFilterTests.swift
index 1bf4f132..6d0075e8 100644
--- a/Tests/Public/ServicesTests/EachFilterTests.swift
+++ b/Tests/Public/ServicesTests/EachFilterTests.swift
@@ -30,7 +30,7 @@ class EachFilterTests: XCTestCase {
let set = Set(["a", "b"])
let template = try! Template(string: "{{#each(set)}}({{@index}},{{.}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["set": Box(set)]))
+ let rendering = try! template.render(Box(["set": set]))
XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].index(of: rendering) != nil)
}
@@ -47,7 +47,7 @@ class EachFilterTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering("<\(rendering.string)>", rendering.contentType)
}
- let box = Box(["array": Box([Box(render)])])
+ let box = Box(["array": [render]])
let template = try! Template(string: "{{#each(array)}}{{@index}}{{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
let rendering = try! template.render(box)
@@ -59,7 +59,7 @@ class EachFilterTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering("<\(rendering.string)>", rendering.contentType)
}
- let box = Box(["dictionary": Box(["a": Box(render)])])
+ let box = Box(["dictionary": ["a": render]])
let template = try! Template(string: "{{#each(dictionary)}}{{@key}}{{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
let rendering = try! template.render(box)
@@ -74,7 +74,7 @@ class EachFilterTests: XCTestCase {
let template = try! Template(string: "{{#each(items)}}({{@index}},{{increment(.)}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
template.registerInBaseContext("increment", Box(increment))
- let rendering = try! template.render(Box(["items": Box(items)]))
+ let rendering = try! template.render(Box(["items": items]))
XCTAssertEqual(rendering, "(0,2)(1,3)(2,4)")
}
@@ -82,7 +82,7 @@ class EachFilterTests: XCTestCase {
let items = ["a","bb","ccc"]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{length}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["items": Box(items)]))
+ let rendering = try! template.render(Box(["items": items]))
XCTAssertEqual(rendering, "(0,1)(1,2)(2,3)")
}
@@ -91,7 +91,7 @@ class EachFilterTests: XCTestCase {
let items = [Box(item)]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{.}}){{/}}")
template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["items": Box(items)]))
+ let rendering = try! template.render(Box(["items": items]))
XCTAssertEqual(rendering, "(0,foo)")
}
}
diff --git a/Tests/Public/ServicesTests/LocalizerTests.swift b/Tests/Public/ServicesTests/LocalizerTests.swift
index 86700cc2..07c5260d 100644
--- a/Tests/Public/ServicesTests/LocalizerTests.swift
+++ b/Tests/Public/ServicesTests/LocalizerTests.swift
@@ -36,7 +36,7 @@ class LocalizerTests: XCTestCase {
func testLocalizer() {
let template = try! Template(string: "{{localize(string)}}")
- let box = Box(["localize": Box(localizer), "string": Box("testable?")])
+ let box = Box(["localize": localizer, "string": "testable?"])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "YES")
}
@@ -44,20 +44,20 @@ class LocalizerTests: XCTestCase {
func testLocalizerFromTable() {
let template = try! Template(string: "{{localize(string)}}")
let localizer = StandardLibrary.Localizer(bundle: localizableBundle, table: "Table")
- let box = Box(["localize": Box(localizer), "string": Box("table_testable?")])
+ let box = Box(["localize": localizer, "string": "table_testable?"])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "YES")
}
func testLocalizerAsRenderingObjectWithoutArgumentDoesNotNeedPercentEscapedLocalizedString() {
var template = try! Template(string: "{{#localize}}%d{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
var rendering = try! template.render()
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%d", value: nil, table: nil), "ha ha percent d %d")
XCTAssertEqual(rendering, "ha ha percent d %d")
template = try! Template(string: "{{#localize}}%@{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
rendering = try! template.render()
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%@", value: nil, table: nil), "ha ha percent @ %@")
XCTAssertEqual(rendering, "ha ha percent @ %@")
@@ -65,13 +65,13 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsRenderingObjectWithoutArgumentNeedsPercentEscapedLocalizedString() {
var template = try! Template(string: "{{#localize}}%d {{foo}}{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
var rendering = try! template.render(Box(["foo": "bar"]))
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%%d %@", value: nil, table: nil), "ha ha percent d %%d %@")
XCTAssertEqual(rendering, "ha ha percent d %d bar")
template = try! Template(string: "{{#localize}}%@ {{foo}}{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
rendering = try! template.render(Box(["foo": "bar"]))
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%%@ %@", value: nil, table: nil), "ha ha percent @ %%@ %@")
XCTAssertEqual(rendering, "ha ha percent @ %@ bar")
@@ -79,7 +79,7 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsFilter() {
let template = try! Template(string: "{{localize(foo)}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
let rendering = try! template.render(Box(["foo": "bar"]))
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "bar", value: nil, table: nil), "translated_bar")
XCTAssertEqual(rendering, "translated_bar")
@@ -87,7 +87,7 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsRenderable() {
let template = try! Template(string: "{{#localize}}bar{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
let rendering = try! template.render()
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "bar", value: nil, table: nil), "translated_bar")
XCTAssertEqual(rendering, "translated_bar")
@@ -95,7 +95,7 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsRenderableWithArgument() {
let template = try! Template(string: "{{#localize}}..{{foo}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
let rendering = try! template.render(Box(["foo": "bar"]))
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "..%@..", value: nil, table: nil), "!!%@!!")
XCTAssertEqual(rendering, "!!bar!!")
@@ -103,7 +103,7 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsRenderableWithArgumentAndConditions() {
let template = try! Template(string: "{{#localize}}.{{foo}}.{{^false}}{{baz}}{{/}}.{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
let rendering = try! template.render(Box(["foo": "bar", "baz": "truc"]))
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: ".%@.%@.", value: nil, table: nil), "!%@!%@!")
XCTAssertEqual(rendering, "!bar!truc!")
@@ -111,24 +111,24 @@ class LocalizerTests: XCTestCase {
func testLocalizerRendersHTMLEscapedValuesOfHTMLTemplates() {
var template = try! Template(string: "{{#localize}}..{{foo}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
var rendering = try! template.render(Box(["foo": "&"]))
XCTAssertEqual(rendering, "!!&!!")
template = try! Template(string: "{{#localize}}..{{{foo}}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
rendering = try! template.render(Box(["foo": "&"]))
XCTAssertEqual(rendering, "!!&!!")
}
func testLocalizerRendersUnescapedValuesOfTextTemplates() {
var template = try! Template(string: "{{% CONTENT_TYPE:TEXT }}{{#localize}}..{{foo}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
var rendering = try! template.render(Box(["foo": "&"]))
XCTAssertEqual(rendering, "!!&!!")
template = try! Template(string: "{{% CONTENT_TYPE:TEXT }}{{#localize}}..{{{foo}}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": Box(localizer)]))
+ template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
rendering = try! template.render(Box(["foo": "&"]))
XCTAssertEqual(rendering, "!!&!!")
}
diff --git a/Tests/Public/ServicesTests/NSFormatterTests.swift b/Tests/Public/ServicesTests/NSFormatterTests.swift
index 9b53e5b8..70d582e4 100644
--- a/Tests/Public/ServicesTests/NSFormatterTests.swift
+++ b/Tests/Public/ServicesTests/NSFormatterTests.swift
@@ -36,7 +36,7 @@ class NSFormatterTests: XCTestCase {
// test filtering a number
let template = try! Template(string: "{{ percent(number) }}")
- let box = Box(["number": Box(0.5), "percent": Box(percentFormatter)])
+ let box = Box(["number": 0.5, "percent": percentFormatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "50%")
}
@@ -51,7 +51,7 @@ class NSFormatterTests: XCTestCase {
// test filtering a string
let template = try! Template(string: "{{ percent(string) }}")
- let box = Box(["string": Box("foo"), "percent": Box(percentFormatter)])
+ let box = Box(["string": "foo", "percent": percentFormatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "")
}
@@ -62,7 +62,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{ number }} {{ number }}{{/ percent }}")
- let box = Box(["number": Box(0.5), "percent": Box(percentFormatter)])
+ let box = Box(["number": 0.5, "percent": percentFormatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "50% 50%")
}
@@ -73,7 +73,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{ value }}{{/ percent }}")
- let box = Box(["value": Box("foo"), "percent": Box(percentFormatter)])
+ let box = Box(["value": "foo", "percent": percentFormatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "foo")
}
@@ -84,7 +84,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{# number }}Number is {{ number }}.{{/ number }}{{/ percent }}")
- let box = Box(["number": Box(0.5), "percent": Box(percentFormatter)])
+ let box = Box(["number": 0.5, "percent": percentFormatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "Number is 50%.")
}
@@ -95,7 +95,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "NO is {{ NO }}. {{^ NO }}NO is false.{{/ NO }} percent(NO) is {{ percent(NO) }}. {{# percent(NO) }}percent(NO) is true.{{/ percent(NO) }} {{# percent }}{{^ NO }}NO is now {{ NO }} and is still false.{{/ NO }}{{/ percent }}")
- let box = Box(["number": Box(0.5), "NO": Box(0), "percent": Box(percentFormatter)])
+ let box = Box(["number": 0.5, "NO": 0, "percent": percentFormatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "NO is 0. NO is false. percent(NO) is 0%. percent(NO) is true. NO is now 0% and is still false.")
}
@@ -103,7 +103,7 @@ class NSFormatterTests: XCTestCase {
func testFormatterIsTruthy() {
let formatter = Formatter()
let template = try! Template(string: "{{# formatter }}Formatter is true.{{/ formatter }}{{^ formatter }}Formatter is false.{{/ formatter }}")
- let box = Box(["formatter": Box(formatter)])
+ let box = Box(["formatter": formatter])
let rendering = try! template.render(box)
XCTAssertEqual(rendering, "Formatter is true.")
}
@@ -111,7 +111,7 @@ class NSFormatterTests: XCTestCase {
func testFormatterRendersSelfAsSomething() {
let formatter = Formatter()
let template = try! Template(string: "{{ formatter }}")
- let box = Box(["formatter": Box(formatter)])
+ let box = Box(["formatter": formatter])
let rendering = try! template.render(box)
XCTAssertTrue(rendering.characters.count > 0)
}
@@ -124,7 +124,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": Box(percentFormatter)])
+ let box = Box(["format": percentFormatter])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -140,7 +140,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": Box(percentFormatter), "value": Box(NSNull())])
+ let box = Box(["format": percentFormatter, "value": NSNull()])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -156,7 +156,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- var box = Box(["format": Box(percentFormatter), "value": Box("1")])
+ var box = Box(["format": percentFormatter, "value": "1"])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -166,7 +166,7 @@ class NSFormatterTests: XCTestCase {
rendering = try! template.render(box)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": Box(percentFormatter), "value": Box("YES")])
+ box = Box(["format": percentFormatter, "value": "YES"])
template = try! Template(string: "<{{format(value)}}>")
rendering = try! template.render(box)
@@ -176,7 +176,7 @@ class NSFormatterTests: XCTestCase {
rendering = try! template.render(box)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": Box(percentFormatter), "value": Box("foo")])
+ box = Box(["format": percentFormatter, "value": "foo"])
template = try! Template(string: "<{{format(value)}}>")
rendering = try! template.render(box)
@@ -195,7 +195,7 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": Box(percentFormatter), "value": Box(Date())])
+ let box = Box(["format": percentFormatter, "value": Date()])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -213,7 +213,7 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- let box = Box(["format": Box(dateFormatter)])
+ let box = Box(["format": dateFormatter])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -228,7 +228,7 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- let box = Box(["format": Box(dateFormatter), "value": Box(NSNull())])
+ let box = Box(["format": dateFormatter, "value": NSNull()])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -243,7 +243,7 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- var box = Box(["format": Box(dateFormatter), "value": Box("1")])
+ var box = Box(["format": dateFormatter, "value": "1"])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
@@ -253,7 +253,7 @@ class NSFormatterTests: XCTestCase {
rendering = try! template.render(box)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": Box(dateFormatter), "value": Box("YES")])
+ box = Box(["format": dateFormatter, "value": "YES"])
template = try! Template(string: "<{{format(value)}}>")
rendering = try! template.render(box)
@@ -263,7 +263,7 @@ class NSFormatterTests: XCTestCase {
rendering = try! template.render(box)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": Box(dateFormatter), "value": Box("foo")])
+ box = Box(["format": dateFormatter, "value": "foo"])
template = try! Template(string: "<{{format(value)}}>")
rendering = try! template.render(box)
@@ -281,7 +281,7 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- let box = Box(["format": Box(dateFormatter), "value": Box(0)])
+ let box = Box(["format": dateFormatter, "value": 0])
var template = try! Template(string: "<{{format(value)}}>")
var rendering = try! template.render(box)
diff --git a/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift b/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift
index 51ae5c2c..48ebe7ce 100644
--- a/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift
+++ b/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift
@@ -42,7 +42,7 @@ class HoganSuite: SuiteTestCase {
]
let repo = TemplateRepository(templates: templates)
let template = try! repo.template(named: "template")
- let rendering = try! template.render(Box(["lambda": Box(lambda)]))
+ let rendering = try! template.render(Box(["lambda": lambda]))
XCTAssertEqual(rendering, "altered child1 - altered parent2")
}
@@ -57,7 +57,7 @@ class HoganSuite: SuiteTestCase {
]
let repo = TemplateRepository(templates: templates)
let template = try! repo.template(named: "template")
- let rendering = try! template.render(Box(["lambda": Box(lambda)]))
+ let rendering = try! template.render(Box(["lambda": lambda]))
XCTAssertEqual(rendering, "changed test2")
}
}
diff --git a/Tests/Public/TemplateTests/TemplateTests.swift b/Tests/Public/TemplateTests/TemplateTests.swift
index de347280..a2f76bda 100644
--- a/Tests/Public/TemplateTests/TemplateTests.swift
+++ b/Tests/Public/TemplateTests/TemplateTests.swift
@@ -34,7 +34,7 @@ class TemplateTests: XCTestCase {
func testTemplateExtendBaseContextWithValue() {
let template = try! Template(string: "{{name}}")
- template.extendBaseContext(Box(["name": Box("Arthur")]))
+ template.extendBaseContext(Box(["name": "Arthur"]))
var rendering = try! template.render()
XCTAssertEqual(rendering, "Arthur")
From e7d807918a0566a1a9aecc9d2439e6a1f22b7f7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 05:46:36 +0200
Subject: [PATCH 19/46] #32: major refactoring: get rid of the Box() function,
and have templates render Any.
---
.../MustacheDemoOSX/ViewController.swift | 16 +-
.../contents.xcplayground | 2 +-
Docs/Playground.playground/section-1.swift | 32 +-
.../Playground.playground/timeline.xctimeline | 6 -
Sources/Box.swift | 688 +++++++-----------
Sources/Configuration.swift | 8 +-
Sources/Context.swift | 22 +-
Sources/CoreFunctions.swift | 25 +-
Sources/EachFilter.swift | 52 +-
Sources/ExpressionInvocation.swift | 2 +-
Sources/Fixit-1.1.0.swift | 27 +
Sources/Formatter.swift | 12 +-
Sources/Foundation.swift | 62 +-
Sources/MustacheBox.swift | 7 +-
Sources/RenderingEngine.swift | 2 +-
Sources/StandardLibrary.swift | 10 +-
Sources/SwiftStandardLibrary.swift | 4 +-
Sources/Template.swift | 14 +-
Tests/Public/BoxTests.swift | 511 +++++--------
Tests/Public/BoxValueTests.swift | 121 +--
.../ConfigurationBaseContextTests.swift | 36 +-
.../ConfigurationContentTypeTests.swift | 54 +-
.../ConfigurationExtendBaseContextTests.swift | 10 +-
.../ConfigurationTagDelimitersTests.swift | 26 +-
.../ContextRegisteredKeyTests.swift | 18 +-
Tests/Public/ContextTests/ContextTests.swift | 18 +-
...ntextValueForMustacheExpressionTests.swift | 12 +-
.../MustacheBoxDocumentationTests.swift | 8 +-
.../MustacheRenderableGuideTests.swift | 54 +-
.../DocumentationTests/ReadMeTests.swift | 28 +-
Tests/Public/FilterTests/FilterTests.swift | 149 ++--
.../FilterTests/VariadicFilterTests.swift | 94 +--
Tests/Public/FoundationCollectionTests.swift | 104 ++-
Tests/Public/HookFunctionTests.swift | 108 +--
.../Public/KeyedSubscriptFunctionTests.swift | 6 +-
Tests/Public/LambdaTests.swift | 28 +-
Tests/Public/MustacheBoxTests.swift | 4 +-
Tests/Public/ObjcKeyAccessTests.swift | 4 +-
Tests/Public/RenderFunctionTests.swift | 224 +++---
.../ServicesTests/EachFilterTests.swift | 40 +-
...matterTests.swift => FormatterTests.swift} | 106 +--
.../Public/ServicesTests/LocalizerTests.swift | 50 +-
Tests/Public/ServicesTests/LoggerTests.swift | 12 +-
.../ServicesTests/StandardLibraryTests.swift | 40 +-
Tests/Public/SuitesTests/SuiteTestCase.swift | 21 +-
.../twitter/hogan.js/HoganSuite.swift | 4 +-
Tests/Public/TagTests/TagTests.swift | 58 +-
.../TemplateRepositoryTests.swift | 4 +-
.../TemplateFromMethodsTests.swift | 6 +-
.../Public/TemplateTests/TemplateTests.swift | 10 +-
Xcode/Mustache.xcodeproj/project.pbxproj | 20 +-
51 files changed, 1315 insertions(+), 1664 deletions(-)
delete mode 100644 Docs/Playground.playground/timeline.xctimeline
create mode 100644 Sources/Fixit-1.1.0.swift
rename Tests/Public/ServicesTests/{NSFormatterTests.swift => FormatterTests.swift} (76%)
diff --git a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
index e81a3ebf..2a1ee969 100644
--- a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
+++ b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
@@ -32,15 +32,15 @@ class ViewController: NSViewController {
// Play with those goodies in your templates:
// They are documented at https://github.com/groue/GRMustache.swift/blob/master/Guides/goodies.md
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
- template.registerInBaseContext("percent", Box(percentFormatter))
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- template.registerInBaseContext("zip", Box(StandardLibrary.zip))
+ let percentFormatter = NumberFormatter()
+ percentFormatter.numberStyle = .percent
+ template.register(percentFormatter, forKey: "percent")
+ template.register(StandardLibrary.each, forKey: "each")
+ template.register(StandardLibrary.zip, forKey: "zip")
template.registerInBaseContext("localize", Box(StandardLibrary.Localizer(bundle: nil, table: nil)))
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
- template.registerInBaseContext("URLEscape", Box(StandardLibrary.URLEscape))
- template.registerInBaseContext("javascriptEscape", Box(StandardLibrary.javascriptEscape))
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+ template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
+ template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
let data = model.JSONString.dataUsingEncoding(NSUTF8StringEncoding)!
let JSONObject: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
diff --git a/Docs/Playground.playground/contents.xcplayground b/Docs/Playground.playground/contents.xcplayground
index 664a2bea..49376369 100644
--- a/Docs/Playground.playground/contents.xcplayground
+++ b/Docs/Playground.playground/contents.xcplayground
@@ -1,5 +1,5 @@
-
+
diff --git a/Docs/Playground.playground/section-1.swift b/Docs/Playground.playground/section-1.swift
index 30892ec1..40ceee51 100644
--- a/Docs/Playground.playground/section-1.swift
+++ b/Docs/Playground.playground/section-1.swift
@@ -2,7 +2,31 @@
import Mustache
-// Renders "Hello Arthur"
-let template = try! Template(string: "Hello {{ name }}")
-let rendering = try! template.render(Box(["name": "Arthur"]))
-print(rendering)
+let percentFormatter = NumberFormatter()
+percentFormatter.numberStyle = .percent
+
+let template = try! Template(string: "{{# percent }}\nhourly: {{ hourly }}\ndaily: {{ daily }}\nweekly: {{ weekly }}\n {{/ percent }}")
+template.register(percentFormatter, forKey: "percent")
+template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
+template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
+template.register(StandardLibrary.each, forKey: "each")
+template.register(StandardLibrary.zip, forKey: "zip")
+
+let localizer = StandardLibrary.Localizer(bundle: nil, table: nil)
+template.register(localizer, forkey: "localize")
+let logger = StandardLibrary.Logger()
+template.extendBaseContext(Box(logger))
+
+// Rendering:
+//
+// hourly: 10%
+// daily: 150%
+// weekly: 400%
+
+let data = [
+ "hourly": 0.1,
+ "daily": 1.5,
+ "weekly": 4,
+]
+let rendering = try! template.render(Box(data))
diff --git a/Docs/Playground.playground/timeline.xctimeline b/Docs/Playground.playground/timeline.xctimeline
deleted file mode 100644
index bf468afe..00000000
--- a/Docs/Playground.playground/timeline.xctimeline
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
diff --git a/Sources/Box.swift b/Sources/Box.swift
index 42517fe2..6da09d35 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -88,494 +88,332 @@ extension MustacheBox {
}
-/// Values that conform to the `MustacheBoxable` protocol can feed Mustache
-/// templates.
-///
-/// - parameter boxable: An optional value that conform to the `MustacheBoxable`
-/// protocol.
-///
-/// - returns: A MustacheBox that wraps *boxable*.
-public func Box(_ boxable: MustacheBoxable?) -> MustacheBox {
- return boxable?.mustacheBox ?? Box()
-}
-
-
/// Attempt to turn value into a box.
///
/// - parameter object: An object.
/// - returns: A MustacheBox that wraps *object*.
-func BoxAny(_ value: Any?) -> MustacheBox {
+func Box(_ value: Any?) -> MustacheBox {
guard let value = value else {
- return Box()
+ return EmptyBox
}
+
switch value {
case let boxable as MustacheBoxable:
return boxable.mustacheBox
case let array as [Any?]:
- return Box(array)
+ return MustacheBox(array: array)
case let set as Set:
- return Box(set)
+ return MustacheBox(set: set)
case let dictionary as [AnyHashable: Any?]:
- return Box(dictionary)
- case let f as FilterFunction:
- return Box(f)
- case let f as RenderFunction:
- return Box(f)
- case let f as WillRenderFunction:
- return Box(f)
- case let f as DidRenderFunction:
- return Box(f)
+ return MustacheBox(dictionary: dictionary)
+ case let filter as FilterFunction:
+ return MustacheBox(filter: filter)
+ case let render as RenderFunction:
+ return MustacheBox(render: render)
+ case let willRender as WillRenderFunction:
+ return MustacheBox(willRender: willRender)
+ case let didRender as DidRenderFunction:
+ return MustacheBox(didRender: didRender)
case let f as KeyedSubscriptFunction:
return MustacheBox(keyedSubscript: f)
default:
- NSLog("Mustache: value `\(value)` is discarded (not an array, not a set, not a dictionary, not a MustacheBoxable value.")
- return Box()
+ return MustacheBox(value: value, boolValue: true)
}
}
-extension Collection {
+/// Concatenates the rendering of the collection items.
+///
+/// There are two tricks when rendering collections:
+///
+/// 1. Items can render as Text or HTML, and our collection should render with
+/// the same type. It is an error to mix content types.
+///
+/// 2. We have to tell items that they are rendered as an enumeration item.
+/// This allows collections to avoid enumerating their items when they are
+/// part of another collections:
+///
+/// {{# arrays }} // Each array renders as an enumeration item, and has itself enter the context stack.
+/// {{#.}} // Each array renders "normally", and enumerates its items
+/// ...
+/// {{/.}}
+/// {{/ arrays }}
+///
+/// - parameter info: A RenderingInfo
+/// - parameter box: A closure that turns collection items into a
+/// MustacheBox.
+/// - returns: A Rendering
+private func concatenateRenderings(array: [Any?], info: RenderingInfo) throws -> Rendering {
+ // Prepare the rendering. We don't known the contentType yet: it depends on items
+ var buffer = ""
+ var contentType: ContentType? = nil
+
+ // Tell items they are rendered as an enumeration item.
+ //
+ // Some values don't render the same whenever they render as an
+ // enumeration item, or alone: {{# values }}...{{/ values }} vs.
+ // {{# value }}...{{/ value }}.
+ //
+ // This is the case of Int, UInt, Double, Bool: they enter the context
+ // stack when used in an iteration, and do not enter the context stack
+ // when used as a boolean.
+ //
+ // This is also the case of collections: they enter the context stack
+ // when used as an item of a collection, and enumerate their items when
+ // used as a collection.
+ var info = info
+ info.enumerationItem = true
- /// Concatenates the rendering of the collection items.
- ///
- /// There are two tricks when rendering collections:
- ///
- /// 1. Items can render as Text or HTML, and our collection should render with
- /// the same type. It is an error to mix content types.
- ///
- /// 2. We have to tell items that they are rendered as an enumeration item.
- /// This allows collections to avoid enumerating their items when they are
- /// part of another collections:
- ///
- /// {{# arrays }} // Each array renders as an enumeration item, and has itself enter the context stack.
- /// {{#.}} // Each array renders "normally", and enumerates its items
- /// ...
- /// {{/.}}
- /// {{/ arrays }}
- ///
- /// - parameter info: A RenderingInfo
- /// - parameter box: A closure that turns collection items into a
- /// MustacheBox.
- /// - returns: A Rendering
- fileprivate func renderItems(_ info: RenderingInfo, box: (Iterator.Element) -> MustacheBox) throws -> Rendering {
- // Prepare the rendering. We don't known the contentType yet: it depends on items
- var buffer = ""
- var contentType: ContentType? = nil
-
- // Tell items they are rendered as an enumeration item.
+ for element in array {
+ let boxRendering = try Box(element).render(info)
+ if contentType == nil
+ {
+ // First element: now we know our contentType
+ contentType = boxRendering.contentType
+ buffer += boxRendering.string
+ }
+ else if contentType == boxRendering.contentType
+ {
+ // Consistent content type: keep on buffering.
+ buffer += boxRendering.string
+ }
+ else
+ {
+ // Inconsistent content type: this is an error. How are we
+ // supposed to mix Text and HTML?
+ throw MustacheError(kind: .renderError, message: "Content type mismatch")
+ }
+ }
+
+ if let contentType = contentType {
+ // {{ collection }}
+ // {{# collection }}...{{/ collection }}
//
- // Some values don't render the same whenever they render as an
- // enumeration item, or alone: {{# values }}...{{/ values }} vs.
- // {{# value }}...{{/ value }}.
+ // We know our contentType, hence the collection is not empty and
+ // we render our buffer.
+ return Rendering(buffer, contentType)
+ } else {
+ // {{ collection }}
//
- // This is the case of Int, UInt, Double, Bool: they enter the context
- // stack when used in an iteration, and do not enter the context stack
- // when used as a boolean.
+ // We don't know our contentType, hence the collection is empty.
//
- // This is also the case of collections: they enter the context stack
- // when used as an item of a collection, and enumerate their items when
- // used as a collection.
- var info = info
- info.enumerationItem = true
-
- for item in self {
- let boxRendering = try box(item).render(info)
- if contentType == nil
- {
- // First item: now we know our contentType
- contentType = boxRendering.contentType
- buffer += boxRendering.string
- }
- else if contentType == boxRendering.contentType
- {
- // Consistent content type: keep on buffering.
- buffer += boxRendering.string
- }
- else
- {
- // Inconsistent content type: this is an error. How are we
- // supposed to mix Text and HTML?
- throw MustacheError(kind: .renderError, message: "Content type mismatch")
- }
- }
-
- if let contentType = contentType {
- // {{ collection }}
- // {{# collection }}...{{/ collection }}
- //
- // We know our contentType, hence the collection is not empty and
- // we render our buffer.
- return Rendering(buffer, contentType)
- } else {
- // {{ collection }}
- //
- // We don't know our contentType, hence the collection is empty.
- //
- // Now this code is executed. This means that the collection is
- // rendered, despite its emptiness.
- //
- // We are not rendering a regular {{# section }} tag, because empty
- // collections have a false boolValue, and RenderingEngine would prevent
- // us to render.
- //
- // We are not rendering an inverted {{^ section }} tag, because
- // RenderingEngine takes care of the rendering of inverted sections.
- //
- // So we are rendering a {{ variable }} tag. As en empty collection, we
- // must return an empty rendering.
- //
- // Renderings have a content type. In order to render an empty
- // rendering that has the contentType of the tag, let's use the
- // `render` method of the tag.
- return try info.tag.render(info.context)
- }
+ // Now this code is executed. This means that the collection is
+ // rendered, despite its emptiness.
+ //
+ // We are not rendering a regular {{# section }} tag, because empty
+ // collections have a false boolValue, and RenderingEngine would prevent
+ // us to render.
+ //
+ // We are not rendering an inverted {{^ section }} tag, because
+ // RenderingEngine takes care of the rendering of inverted sections.
+ //
+ // So we are rendering a {{ variable }} tag. As en empty collection, we
+ // must return an empty rendering.
+ //
+ // Renderings have a content type. In order to render an empty
+ // rendering that has the contentType of the tag, let's use the
+ // `render` method of the tag.
+ return try info.tag.render(info.context)
}
}
-extension Collection where IndexDistance == Int {
- /// This function returns a MustacheBox that wraps a set-like collection.
- ///
- /// The returned box can be queried for the following keys:
- ///
- /// - `first`: the first object in the collection
- /// - `count`: number of elements in the collection
- ///
- /// - parameter value: the value of the returned box.
- /// - parameter box: A closure that turns collection items into a
- /// MustacheBox.
- /// - returns: A MustacheBox that wraps the collection.
- func mustacheBoxWithSetValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
- return MustacheBox(
- converter: MustacheBox.Converter(arrayValue: { self.map({ box($0) }) }),
- value: value,
- boolValue: !isEmpty,
- keyedSubscript: { (key) in
+extension MustacheBox {
+
+ /// Arrays can feed Mustache templates.
+ ///
+ /// let array = [1,2,3]
+ ///
+ /// // Renders "123"
+ /// let template = try! Template(string: "{{#array}}{{.}}{{/array}}")
+ /// try! template.render(Box(["array": Box(array)]))
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{array}}` renders the concatenation of the array items.
+ ///
+ /// - `{{#array}}...{{/array}}` renders as many times as there are items in
+ /// `array`, pushing each item on its turn on the top of the context stack.
+ ///
+ /// - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
+ ///
+ ///
+ /// ### Keys exposed to templates
+ ///
+ /// An array can be queried for the following keys:
+ ///
+ /// - `count`: number of elements in the array
+ /// - `first`: the first object in the array
+ /// - `last`: the last object in the array
+ ///
+ /// Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders
+ /// once, if and only if `array` is not empty.
+ ///
+ ///
+ /// - parameter array: An array of boxable values.
+ /// - returns: A MustacheBox that wraps *array*.
+ convenience init(array: [Any?]) {
+ self.init(
+ converter: MustacheBox.Converter(arrayValue: { array.map { Box($0) } }),
+ value: array,
+ boolValue: !array.isEmpty,
+ keyedSubscript: { key in
switch key {
case "first": // C: CollectionType
- if let first = self.first {
- return box(first)
+ if let first = array.first {
+ return Box(first)
+ } else {
+ return EmptyBox
+ }
+ case "last": // C.Index: BidirectionalIndexType
+ if let last = array.last {
+ return Box(last)
} else {
- return Box()
+ return EmptyBox
}
case "count": // C.IndexDistance == Int
- return Box(self.count)
+ return Box(array.count)
default:
- return Box()
+ return EmptyBox
}
},
render: { (info: RenderingInfo) in
if info.enumerationItem {
// {{# collections }}...{{/ collections }}
- return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithSetValue(value, box: box)))
+ return try info.tag.render(info.context.extendedContext(array))
} else {
// {{ collection }}
// {{# collection }}...{{/ collection }}
- return try self.renderItems(info, box: box)
+ return try concatenateRenderings(array: array, info: info)
}
- }
- )
+ })
}
-}
-
-extension BidirectionalCollection where IndexDistance == Int {
- /// This function returns a MustacheBox that wraps an array-like collection.
- ///
- /// The returned box can be queried for the following keys:
- ///
- /// - `first`: the first object in the collection
- /// - `count`: number of elements in the collection
- /// - `last`: the last object in the collection
- ///
- /// - parameter value: the value of the returned box.
- /// - parameter box: A closure that turns collection items into a
- /// MustacheBox.
- /// - returns: A MustacheBox that wraps the collection.
- func mustacheBoxWithArrayValue(_ value: Any?, box: @escaping (Iterator.Element) -> MustacheBox) -> MustacheBox {
- return MustacheBox(
- converter: MustacheBox.Converter(arrayValue: { self.map({ box($0) }) }),
- value: value,
- boolValue: !isEmpty,
+
+ /// Sets can feed Mustache templates.
+ ///
+ /// let set:Set = [1,2,3]
+ ///
+ /// // Renders "132", or "231", etc.
+ /// let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
+ /// try! template.render(Box(["set": Box(set)]))
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{set}}` renders the concatenation of the set items.
+ ///
+ /// - `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
+ /// pushing each item on its turn on the top of the context stack.
+ ///
+ /// - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
+ ///
+ ///
+ /// ### Keys exposed to templates
+ ///
+ /// A set can be queried for the following keys:
+ ///
+ /// - `count`: number of elements in the set
+ /// - `first`: the first object in the set
+ ///
+ /// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once,
+ /// if and only if `set` is not empty.
+ ///
+ ///
+ /// - parameter set: A set.
+ /// - returns: A MustacheBox that wraps *set*.
+ convenience init(set: Set) {
+ self.init(
+ converter: MustacheBox.Converter(arrayValue: { set.map({ Box($0) }) }),
+ value: set,
+ boolValue: !set.isEmpty,
keyedSubscript: { (key) in
switch key {
case "first": // C: CollectionType
- if let first = self.first {
- return box(first)
+ if let first = set.first {
+ return Box(first)
} else {
- return Box()
- }
- case "last": // C.Index: BidirectionalIndexType
- if let last = self.last {
- return box(last)
- } else {
- return Box()
+ return EmptyBox
}
case "count": // C.IndexDistance == Int
- return Box(self.count)
+ return Box(set.count)
default:
- return Box()
+ return EmptyBox
}
},
render: { (info: RenderingInfo) in
if info.enumerationItem {
// {{# collections }}...{{/ collections }}
- return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithArrayValue(value, box: box)))
+ return try info.tag.render(info.context.extendedContext(set))
} else {
// {{ collection }}
// {{# collection }}...{{/ collection }}
- return try self.renderItems(info, box: box)
+ return try concatenateRenderings(array: Array(set), info: info)
}
}
)
}
-}
-
-
-/// Sets can feed Mustache templates.
-///
-/// let set:Set = [1,2,3]
-///
-/// // Renders "132", or "231", etc.
-/// let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
-/// try! template.render(Box(["set": Box(set)]))
-///
-///
-/// ### Rendering
-///
-/// - `{{set}}` renders the concatenation of the set items.
-///
-/// - `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
-/// pushing each item on its turn on the top of the context stack.
-///
-/// - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
-///
-///
-/// ### Keys exposed to templates
-///
-/// A set can be queried for the following keys:
-///
-/// - `count`: number of elements in the set
-/// - `first`: the first object in the set
-///
-/// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once,
-/// if and only if `set` is not empty.
-///
-///
-/// - parameter set: A set.
-/// - returns: A MustacheBox that wraps *set*.
-public func Box(_ set: Set?) -> MustacheBox {
- guard let set = set else {
- return Box()
- }
- return set.mustacheBoxWithSetValue(set, box: { BoxAny($0) })
-}
-
-/// Arrays can feed Mustache templates.
-///
-/// let array = [1,2,3]
-///
-/// // Renders "123"
-/// let template = try! Template(string: "{{#array}}{{.}}{{/array}}")
-/// try! template.render(Box(["array": Box(array)]))
-///
-///
-/// ### Rendering
-///
-/// - `{{array}}` renders the concatenation of the array items.
-///
-/// - `{{#array}}...{{/array}}` renders as many times as there are items in
-/// `array`, pushing each item on its turn on the top of the context stack.
-///
-/// - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
-///
-///
-/// ### Keys exposed to templates
-///
-/// An array can be queried for the following keys:
-///
-/// - `count`: number of elements in the array
-/// - `first`: the first object in the array
-/// - `last`: the last object in the array
-///
-/// Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders
-/// once, if and only if `array` is not empty.
-///
-///
-/// - parameter array: An array of boxable values.
-/// - returns: A MustacheBox that wraps *array*.
-public func Box(_ array: [Any?]?) -> MustacheBox {
- guard let array = array else {
- return Box()
- }
- return array.mustacheBoxWithArrayValue(array, box: { BoxAny($0) })
-}
-
-/// A dictionary can feed Mustache templates.
-///
-/// let dictionary: [String: String] = [
-/// "firstName": "Freddy",
-/// "lastName": "Mercury"]
-///
-/// // Renders "Freddy Mercury"
-/// let template = try! Template(string: "{{firstName}} {{lastName}}")
-/// let rendering = try! template.render(Box(dictionary))
-///
-///
-/// ### Rendering
-///
-/// - `{{dictionary}}` renders the built-in Swift String Interpolation of the
-/// dictionary.
-///
-/// - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
-/// context stack, and renders the section once.
-///
-/// - `{{^dictionary}}...{{/dictionary}}` does not render.
-///
-///
-/// In order to iterate over the key/value pairs of a dictionary, use the `each`
-/// filter from the Standard Library:
-///
-/// // Register StandardLibrary.each for the key "each":
-/// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
-/// template.registerInBaseContext("each", Box(StandardLibrary.each))
-///
-/// // Renders ""
-/// let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"]
-/// let rendering = try! template.render(Box(["dictionary": dictionary]))
-///
-/// - parameter dictionary: A dictionary
-/// - returns: A MustacheBox that wraps *dictionary*.
-public func Box(_ dictionary: [AnyHashable: Any?]?) -> MustacheBox {
- guard let dictionary = dictionary else {
- return Box()
- }
- return MustacheBox(
- converter: MustacheBox.Converter(dictionaryValue: {
- var boxDictionary: [String: MustacheBox] = [:]
- for (key, value) in dictionary {
- if let key = key as? String {
- boxDictionary[key] = BoxAny(value)
+
+ /// A dictionary can feed Mustache templates.
+ ///
+ /// let dictionary: [String: String] = [
+ /// "firstName": "Freddy",
+ /// "lastName": "Mercury"]
+ ///
+ /// // Renders "Freddy Mercury"
+ /// let template = try! Template(string: "{{firstName}} {{lastName}}")
+ /// let rendering = try! template.render(Box(dictionary))
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{dictionary}}` renders the built-in Swift String Interpolation of the
+ /// dictionary.
+ ///
+ /// - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
+ /// context stack, and renders the section once.
+ ///
+ /// - `{{^dictionary}}...{{/dictionary}}` does not render.
+ ///
+ ///
+ /// In order to iterate over the key/value pairs of a dictionary, use the `each`
+ /// filter from the Standard Library:
+ ///
+ /// // Register StandardLibrary.each for the key "each":
+ /// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
+ /// template.register(StandardLibrary.each, forKey: "each")
+ ///
+ /// // Renders ""
+ /// let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"]
+ /// let rendering = try! template.render(Box(["dictionary": dictionary]))
+ ///
+ /// - parameter dictionary: A dictionary
+ /// - returns: A MustacheBox that wraps *dictionary*.
+ convenience init(dictionary: [AnyHashable: Any?]) {
+ self.init(
+ converter: MustacheBox.Converter(dictionaryValue: {
+ var boxDictionary: [String: MustacheBox] = [:]
+ for (key, value) in dictionary {
+ if let key = key as? String {
+ boxDictionary[key] = Box(value)
+ } else {
+ NSLog("Mustache: non-string key in dictionary (\(key)) is discarded.")
+ }
+ }
+ return boxDictionary
+ }),
+ value: dictionary,
+ keyedSubscript: { (key: String) in
+ if let value = dictionary[key] {
+ return Box(value)
} else {
- NSLog("Mustache: non-string key in dictionary (\(key)) is discarded.")
+ return EmptyBox
}
- }
- return boxDictionary
- }),
- value: dictionary,
- keyedSubscript: { (key: String) in
- if let value = dictionary[key] {
- return BoxAny(value)
- } else {
- return Box()
- }
- })
-}
-
-/// A function that wraps a `FilterFunction` into a `MustacheBox` so that it can
-/// feed template.
-///
-/// let square: FilterFunction = Filter { (x: Int?) in
-/// return Box(x! * x!)
-/// }
-///
-/// let template = try! Template(string: "{{ square(x) }}")
-/// template.registerInBaseContext("square", Box(square))
-///
-/// // Renders "100"
-/// try! template.render(Box(["x": 10]))
-///
-/// - parameter filter: A FilterFunction.
-/// - returns: A MustacheBox that wraps *filter*.
-///
-/// See also:
-///
-/// - FilterFunction
-public func Box(_ filter: @escaping FilterFunction) -> MustacheBox {
- return MustacheBox(filter: filter)
-}
-
-/// A function that wraps a `RenderFunction` into a `MustacheBox` so that it can
-/// feed template.
-///
-/// let foo: RenderFunction = { (_) in Rendering("foo") }
-///
-/// // Renders "foo"
-/// let template = try! Template(string: "{{ foo }}")
-/// try! template.render(Box(["foo": Box(foo)]))
-///
-/// - parameter render: A RenderFunction.
-/// - returns: A MustacheBox that wraps *render*.
-///
-/// See also:
-///
-/// - RenderFunction
-public func Box(_ render: @escaping RenderFunction) -> MustacheBox {
- return MustacheBox(render: render)
-}
-
-/// A function that wraps a `WillRenderFunction` into a `MustacheBox` so that it
-/// can feed template.
-///
-/// let logTags: WillRenderFunction = { (tag: Tag, box: MustacheBox) in
-/// print("\(tag) will render \(box.value!)")
-/// return box
-/// }
-///
-/// // By entering the base context of the template, the logTags function
-/// // will be notified of all tags.
-/// let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
-/// template.extendBaseContext(Box(logTags))
-///
-/// // Prints:
-/// // {{# user }} at line 1 will render { firstName = Errol; lastName = Flynn; }
-/// // {{ firstName }} at line 1 will render Errol
-/// // {{ lastName }} at line 1 will render Flynn
-/// let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
-/// try! template.render(Box(data))
-///
-/// - parameter willRender: A WillRenderFunction
-/// - returns: A MustacheBox that wraps *willRender*.
-///
-/// See also:
-///
-/// - WillRenderFunction
-public func Box(_ willRender: @escaping WillRenderFunction) -> MustacheBox {
- return MustacheBox(willRender: willRender)
-}
-
-/// A function that wraps a `DidRenderFunction` into a `MustacheBox` so that it
-/// can feed template.
-///
-/// let logRenderings: DidRenderFunction = { (tag: Tag, box: MustacheBox, string: String?) in
-/// print("\(tag) did render \(box.value!) as `\(string!)`")
-/// }
-///
-/// // By entering the base context of the template, the logRenderings function
-/// // will be notified of all tags.
-/// let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
-/// template.extendBaseContext(Box(logRenderings))
-///
-/// // Renders "Errol Flynn"
-/// //
-/// // Prints:
-/// // {{ firstName }} at line 1 did render Errol as `Errol`
-/// // {{ lastName }} at line 1 did render Flynn as `Flynn`
-/// // {{# user }} at line 1 did render { firstName = Errol; lastName = Flynn; } as `Errol Flynn`
-/// let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
-/// try! template.render(Box(data))
-///
-/// - parameter didRender: A DidRenderFunction/
-/// - returns: A MustacheBox that wraps *didRender*.
-///
-/// See also:
-///
-/// - DidRenderFunction
-public func Box(_ didRender: @escaping DidRenderFunction) -> MustacheBox {
- return MustacheBox(didRender: didRender)
+ })
+ }
}
-/// The empty box, the box that represents missing values.
-public func Box() -> MustacheBox {
- return EmptyBox
-}
+//public func Box() -> MustacheBox {
+// return EmptyBox
+//}
-private let EmptyBox = MustacheBox()
+let EmptyBox = MustacheBox()
diff --git a/Sources/Configuration.swift b/Sources/Configuration.swift
index 51d42626..24ad3bc6 100644
--- a/Sources/Configuration.swift
+++ b/Sources/Configuration.swift
@@ -237,8 +237,8 @@ public struct Configuration {
- baseContext
- registerInBaseContext
*/
- public mutating func extendBaseContext(_ box: MustacheBox) {
- baseContext = baseContext.extendedContext(box)
+ public mutating func extendBaseContext(_ value: Any?) {
+ baseContext = baseContext.extendedContext(value)
}
/**
@@ -284,8 +284,8 @@ public struct Configuration {
- baseContext
- extendBaseContext
*/
- public mutating func registerInBaseContext(_ key: String, _ box: MustacheBox) {
- baseContext = baseContext.contextWithRegisteredKey(key, box: box)
+ public mutating func register(_ value: Any?, forKey key: String) {
+ baseContext = baseContext.extendedContext(withRegisteredValue: value, forKey: key)
}
diff --git a/Sources/Context.swift b/Sources/Context.swift
index 2ae68a49..c494b368 100644
--- a/Sources/Context.swift
+++ b/Sources/Context.swift
@@ -74,8 +74,8 @@ final public class Context {
- parameter box: A box.
- returns: A new context that contains *box*.
*/
- public convenience init(_ box: MustacheBox) {
- self.init(type: .box(box: box, parent: Context()))
+ public convenience init(_ value: Any?) {
+ self.init(type: .box(box: Box(value), parent: Context()))
}
/**
@@ -86,9 +86,8 @@ final public class Context {
- parameter box: A box.
- returns: A new context with *box* registered for *key*.
*/
- public convenience init(registeredKey key: String, box: MustacheBox) {
- let d = [key: box]
- self.init(type: .root, registeredKeysContext: Context(Box(d)))
+ public convenience init(registeredKey key: String, value: Any?) {
+ self.init(type: .root, registeredKeysContext: Context([key: value]))
}
@@ -103,8 +102,8 @@ final public class Context {
- returns: A new context with *box* pushed at the top of the stack.
*/
- public func extendedContext(_ box: MustacheBox) -> Context {
- return Context(type: .box(box: box, parent: self), registeredKeysContext: registeredKeysContext)
+ public func extendedContext(_ value: Any?) -> Context {
+ return Context(type: .box(box: Box(value), parent: self), registeredKeysContext: registeredKeysContext)
}
/**
@@ -116,9 +115,8 @@ final public class Context {
- returns: A new context with *box* registered for *key*.
*/
- public func contextWithRegisteredKey(_ key: String, box: MustacheBox) -> Context {
- let d = [key: box]
- let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(d))
+ public func extendedContext(withRegisteredValue value: Any?, forKey key: String) -> Context {
+ let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext([key: value])
return Context(type: self.type, registeredKeysContext: registeredKeysContext)
}
@@ -133,7 +131,7 @@ final public class Context {
public var topBox: MustacheBox {
switch type {
case .root:
- return Box()
+ return EmptyBox
case .box(box: let box, parent: _):
return box
case .partialOverride(partialOverride: _, parent: let parent):
@@ -175,7 +173,7 @@ final public class Context {
switch type {
case .root:
- return Box()
+ return EmptyBox
case .box(box: let box, parent: let parent):
let innerBox = box.mustacheBoxForKey(key)
if innerBox.isEmpty {
diff --git a/Sources/CoreFunctions.swift b/Sources/CoreFunctions.swift
index df819a4b..3e9fa4fe 100644
--- a/Sources/CoreFunctions.swift
+++ b/Sources/CoreFunctions.swift
@@ -52,7 +52,7 @@ advanced usage, only supported with the low-level function
`func Box(boolValue:value:keyedSubscript:filter:render:willRender:didRender:) -> MustacheBox`.
// A KeyedSubscriptFunction that turns "key" into "KEY":
- let keyedSubscript: KeyedSubscriptFunction = { (key: String) -> MustacheBox in
+ let keyedSubscript: KeyedSubscriptFunction = { (key: String) -> Any? in
return Box(key.uppercaseString)
}
@@ -72,7 +72,7 @@ in the context stack in order to resolve a key, return the empty box `Box()`.
In order to express "missing value", and prevent the rendering engine from
digging deeper, return `Box(NSNull())`.
*/
-public typealias KeyedSubscriptFunction = (_ key: String) -> MustacheBox
+public typealias KeyedSubscriptFunction = (_ key: String) -> Any?
// =============================================================================
@@ -118,7 +118,7 @@ types of filters:
See the documentation of the `Filter()` functions.
*/
-public typealias FilterFunction = (_ box: MustacheBox, _ partialApplication: Bool) throws -> MustacheBox
+public typealias FilterFunction = (_ box: MustacheBox, _ partialApplication: Bool) throws -> Any?
// -----------------------------------------------------------------------------
@@ -145,7 +145,7 @@ MustacheError of type RenderError.
- parameter filter: A function `(MustacheBox) throws -> MustacheBox`.
- returns: A FilterFunction.
*/
-public func Filter(_ filter: @escaping (MustacheBox) throws -> MustacheBox) -> FilterFunction {
+public func Filter(_ filter: @escaping (MustacheBox) throws -> Any?) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
// This is a single-argument filter: we do not wait for another one.
@@ -179,7 +179,7 @@ MustacheError of type RenderError.
- parameter filter: A function `(T?) throws -> MustacheBox`.
- returns: A FilterFunction.
*/
-public func Filter(_ filter: @escaping (T?) throws -> MustacheBox) -> FilterFunction {
+public func Filter(_ filter: @escaping (T?) throws -> Any?) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
// This is a single-argument filter: we do not wait for another one.
@@ -211,7 +211,7 @@ For example:
- parameter filter: A function `([MustacheBox]) throws -> MustacheBox`.
- returns: A FilterFunction.
*/
-public func VariadicFilter(_ filter: @escaping ([MustacheBox]) throws -> MustacheBox) -> FilterFunction {
+public func VariadicFilter(_ filter: @escaping ([MustacheBox]) throws -> Any?) -> FilterFunction {
// f(a,b,c) is implemented as f(a)(b)(c).
//
@@ -220,12 +220,12 @@ public func VariadicFilter(_ filter: @escaping ([MustacheBox]) throws -> Mustach
//
// It is the partialApplication flag the tells when it's time to return the
// final result.
- func partialFilter(_ filter: @escaping ([MustacheBox]) throws -> MustacheBox, arguments: [MustacheBox]) -> FilterFunction {
+ func partialFilter(_ filter: @escaping ([MustacheBox]) throws -> Any?, arguments: [MustacheBox]) -> FilterFunction {
return { (nextArgument: MustacheBox, partialApplication: Bool) in
let arguments = arguments + [nextArgument]
if partialApplication {
// Wait for another argument
- return Box(partialFilter(filter, arguments: arguments))
+ return partialFilter(filter, arguments: arguments)
} else {
// No more argument: compute final value
return try filter(arguments)
@@ -272,9 +272,8 @@ public func Filter(_ filter: @escaping (Rendering) throws -> Rendering) -> Filte
throw MustacheError(kind: .renderError, message: "Too many arguments")
}
// Box a RenderFunction
- return Box { (info: RenderingInfo) in
- let rendering = try box.render(info)
- return try filter(rendering)
+ return { (info: RenderingInfo) in
+ return try filter(box.render(info))
}
}
}
@@ -689,7 +688,7 @@ public func Lambda(_ lambda: @escaping () -> String) -> RenderFunction {
// {{# lambda }}...{{/ lambda }}
//
// Behave as a true object, and render the section.
- let context = info.context.extendedContext(Box(Lambda(lambda)))
+ let context = info.context.extendedContext(Lambda(lambda))
return try info.tag.render(context)
}
}
@@ -811,7 +810,7 @@ section.
]
try! template.render(Box(data))
*/
-public typealias WillRenderFunction = (_ tag: Tag, _ box: MustacheBox) -> MustacheBox
+public typealias WillRenderFunction = (_ tag: Tag, _ box: MustacheBox) -> Any?
// =============================================================================
diff --git a/Sources/EachFilter.swift b/Sources/EachFilter.swift
index 369ba1dd..ec372e9e 100644
--- a/Sources/EachFilter.swift
+++ b/Sources/EachFilter.swift
@@ -23,7 +23,7 @@
import Foundation
-let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
+let EachFilter = Filter { (box: MustacheBox) -> Any? in
// {{# each(nothing) }}...{{/ }}
if box.isEmpty {
@@ -35,33 +35,33 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
// // Renders "firstName: Charles, lastName: Bronson."
// let dictionary = ["firstName": "Charles", "lastName": "Bronson"]
// let template = try! Template(string: "{{# each(dictionary) }}{{ @key }}: {{ . }}{{^ @last }}, {{/ @last }}{{/ each(dictionary) }}.")
- // template.registerInBaseContext("each", Box(StandardLibrary.each))
- // try! template.render(Box(["dictionary": dictionary]))
+ // template.register(StandardLibrary.each, forKey: "each")
+ // try! template.render(["dictionary": dictionary])
//
// The dictionaryValue box property makes sure to return a
// [String: MustacheBox] whatever the boxed dictionary-like value
// (NSDictionary, [String: Int], [String: CustomObject], etc.
if let dictionary = box.dictionaryValue {
let count = dictionary.count
- let transformedBoxes = dictionary.enumerated().map { (index: Int, element: (key: String, box: MustacheBox)) -> MustacheBox in
+ let customRenderFunctions = dictionary.enumerated().map { (index: Int, element: (key: String, box: MustacheBox)) -> Any? in
let customRenderFunction: RenderFunction = { info in
// Push positional keys in the context stack and then perform
// a regular rendering.
- var position: [String: MustacheBox] = [:]
- position["@index"] = Box(index)
- position["@indexPlusOne"] = Box(index + 1)
- position["@indexIsEven"] = Box(index % 2 == 0)
- position["@first"] = Box(index == 0)
- position["@last"] = Box((index == count - 1))
- position["@key"] = Box(element.key)
+ var position: [String: Any] = [:]
+ position["@index"] = index
+ position["@indexPlusOne"] = index + 1
+ position["@indexIsEven"] = (index % 2 == 0)
+ position["@first"] = (index == 0)
+ position["@last"] = ((index == count - 1))
+ position["@key"] = element.key
var info = info
- info.context = info.context.extendedContext(Box(position))
+ info.context = info.context.extendedContext(position)
return try element.box.render(info)
}
- return Box(customRenderFunction)
+ return customRenderFunction
}
- return Box(transformedBoxes)
+ return customRenderFunctions
}
@@ -70,31 +70,31 @@ let EachFilter = Filter { (box: MustacheBox) -> MustacheBox in
// // Renders "1: bread, 2: ham, 3: butter"
// let array = ["bread", "ham", "butter"]
// let template = try! Template(string: "{{# each(array) }}{{ @indexPlusOne }}: {{ . }}{{^ @last }}, {{/ @last }}{{/ each(array) }}.")
- // template.registerInBaseContext("each", Box(StandardLibrary.each))
- // try! template.render(Box(["array": array]))
+ // template.register(StandardLibrary.each, forKey: "each")
+ // try! template.render(["array": array])
//
// The arrayValue box property makes sure to return a [MustacheBox] whatever
// the boxed collection: NSArray, NSSet, [String], [CustomObject], etc.
if let boxes = box.arrayValue {
let count = boxes.count
- let transformedBoxes = boxes.enumerated().map { (index: Int, box: MustacheBox) -> MustacheBox in
+ let customRenderFunctions = boxes.enumerated().map { (index: Int, box: MustacheBox) -> Any? in
let customRenderFunction: RenderFunction = { info in
// Push positional keys in the context stack and then perform
// a regular rendering.
- var position: [String: MustacheBox] = [:]
- position["@index"] = Box(index)
- position["@indexPlusOne"] = Box(index + 1)
- position["@indexIsEven"] = Box(index % 2 == 0)
- position["@first"] = Box(index == 0)
- position["@last"] = Box((index == count - 1))
+ var position: [String: Any] = [:]
+ position["@index"] = index
+ position["@indexPlusOne"] = index + 1
+ position["@indexIsEven"] = (index % 2 == 0)
+ position["@first"] = (index == 0)
+ position["@last"] = ((index == count - 1))
var info = info
- info.context = info.context.extendedContext(Box(position))
+ info.context = info.context.extendedContext(position)
return try box.render(info)
}
- return Box(customRenderFunction)
+ return customRenderFunction
}
- return Box(transformedBoxes)
+ return customRenderFunctions
}
// Non-iterable value
diff --git a/Sources/ExpressionInvocation.swift b/Sources/ExpressionInvocation.swift
index eab914fb..975880e7 100644
--- a/Sources/ExpressionInvocation.swift
+++ b/Sources/ExpressionInvocation.swift
@@ -61,7 +61,7 @@ struct ExpressionInvocation {
}
let argumentBox = try evaluate(context: context, expression: argumentExpression)
- return try filter(argumentBox, partialApplication)
+ return try Box(filter(argumentBox, partialApplication))
}
}
}
diff --git a/Sources/Fixit-1.1.0.swift b/Sources/Fixit-1.1.0.swift
new file mode 100644
index 00000000..2cb4a902
--- /dev/null
+++ b/Sources/Fixit-1.1.0.swift
@@ -0,0 +1,27 @@
+// The MIT License
+//
+// Copyright (c) 2015 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+extension Template {
+ @available(*, unavailable, renamed:"register(_:forKey:)")
+ public func registerInBaseContext(_ key: String, _ value: Any?) { }
+}
diff --git a/Sources/Formatter.swift b/Sources/Formatter.swift
index 6e84f652..db554315 100644
--- a/Sources/Formatter.swift
+++ b/Sources/Formatter.swift
@@ -47,7 +47,7 @@ extension Formatter {
percentFormatter.numberStyle = .PercentStyle
var template = try! Template(string: "{{ percent(x) }}")
- template.registerInBaseContext("percent", Box(percentFormatter))
+ template.register(percentFormatter, forKey: "percent")
// Renders "50%"
try! template.render(Box(["x": 0.5]))
@@ -61,7 +61,7 @@ extension Formatter {
"- {{name}} ({{proportion}})\n" +
"{{/ingredients}}" +
"{{/percent}}")
- template.registerInBaseContext("percent", Box(percentFormatter))
+ template.register(percentFormatter, forKey: "percent")
// - bread (50%)
// - ham (35%)
@@ -102,10 +102,10 @@ extension Formatter {
// > right class, return a properly formatted and, if necessary,
// > localized string.
if let object = box.value as? NSObject {
- return Box(self.string(for: object))
+ return self.string(for: object)
} else {
// Not the correct class: return nil, i.e. empty Box.
- return Box()
+ return nil
}
},
@@ -129,8 +129,8 @@ extension Formatter {
// So nil result means that object is not of the correct class. Leave
// it untouched.
- if let object = box.value as? NSObject, let formatted = self.string(for: object) {
- return Box(formatted)
+ if let formatted = self.string(for: box.value) {
+ return formatted
} else {
return box
}
diff --git a/Sources/Foundation.swift b/Sources/Foundation.swift
index f1541787..c9e30ef3 100644
--- a/Sources/Foundation.swift
+++ b/Sources/Foundation.swift
@@ -108,9 +108,8 @@ extension NSObject : MustacheBoxable {
if let enumerable = self as? NSFastEnumeration {
// Enumerable
- // Turn enumerable into a Swift array of MustacheBoxes that we know how to box
- let array = IteratorSequence(NSFastEnumerationIterator(enumerable)).map(BoxAny)
- return array.mustacheBoxWithArrayValue(self, box: { $0 })
+ // Turn enumerable into a Swift array that we know how to box
+ return Box(Array(IteratorSequence(NSFastEnumerationIterator(enumerable))))
} else {
// Generic NSObject
@@ -121,14 +120,14 @@ extension NSObject : MustacheBoxable {
keyedSubscript: { (key: String) in
if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
// Use valueForKey: for safe keys
- return BoxAny(self.value(forKey: key))
+ return self.value(forKey: key)
} else {
// Missing key
- return Box()
+ return nil
}
})
#else
- return MustacheBox(value: self)
+ return self
#endif
}
}
@@ -217,14 +216,13 @@ extension NSNumber {
case "Q":
return Box(uint64Value)
case "f":
- return Box(Double(floatValue))
+ return Box(floatValue)
case "d":
return Box(doubleValue)
case "B":
return Box(boolValue)
default:
- NSLog("GRMustache support for NSNumber of type \(objCType) is not implemented: value is discarded.")
- return Box()
+ return Box(self)
}
}
}
@@ -311,8 +309,7 @@ extension NSSet {
/// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders
/// once, if and only if `set` is not empty.
open override var mustacheBox: MustacheBox {
- let array = IteratorSequence(NSFastEnumerationIterator(self)).map(BoxAny)
- return array.mustacheBoxWithSetValue(self, box: { $0 })
+ return Box(Set(IteratorSequence(NSFastEnumerationIterator(self)).flatMap { $0 as? AnyHashable }))
}
}
@@ -357,25 +354,33 @@ extension NSDictionary {
///
/// // Attach StandardLibrary.each to the key "each":
/// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
- /// template.registerInBaseContext("each", Box(StandardLibrary.each))
+ /// template.register(StandardLibrary.each, forKey: "each")
///
/// // Renders ""
/// let dictionary = ["name": "Arthur", "age": 36] as NSDictionary
/// let rendering = try! template.render(Box(["dictionary": dictionary]))
open override var mustacheBox: MustacheBox {
- return MustacheBox(
- converter: MustacheBox.Converter(dictionaryValue: { IteratorSequence(NSFastEnumerationIterator(self)).reduce([String: MustacheBox](), { (boxDictionary, key) in
- var boxDictionary = boxDictionary
- if let key = key as? String {
- boxDictionary[key] = BoxAny(self[key])
- } else {
- NSLog("GRMustache found a non-string key in NSDictionary (\(key)): value is discarded.")
- }
- return boxDictionary
- })}),
- value: self,
- keyedSubscript: { BoxAny(self[$0])
- })
+ return Box(self as? [AnyHashable: Any])
+// return MustacheBox(
+// converter: MustacheBox.Converter(dictionaryValue: {
+// var boxDictionary: [String: MustacheBox] = [:]
+// for (key, value) in self {
+// if let key = key as? String {
+// boxDictionary[key] = Box(value)
+// } else {
+// NSLog("Mustache: non-string key in dictionary (\(key)) is discarded.")
+// }
+// }
+// return boxDictionary
+// }),
+// value: self,
+// keyedSubscript: { (key: String) in
+// if let value = self[key] {
+// return Box(value)
+// } else {
+// return EmptyBox
+// }
+// })
}
}
@@ -385,12 +390,7 @@ extension ReferenceConvertible where Self: MustacheBoxable {
///
/// See NSObject.mustacheBox
public var mustacheBox: MustacheBox {
- if let object = self as? ReferenceType {
- return object.mustacheBox
- } else {
- NSLog("Value `\(self)` can not feed Mustache templates: it is discarded.")
- return Box()
- }
+ return (self as! ReferenceType).mustacheBox
}
}
diff --git a/Sources/MustacheBox.swift b/Sources/MustacheBox.swift
index e237db55..599ff769 100644
--- a/Sources/MustacheBox.swift
+++ b/Sources/MustacheBox.swift
@@ -150,7 +150,10 @@ final public class MustacheBox : NSObject {
- returns: The MustacheBox for *key*.
*/
public func mustacheBoxForKey(_ key: String) -> MustacheBox {
- return keyedSubscript?(key) ?? Box()
+ guard let keyedSubscript = keyedSubscript else {
+ return EmptyBox
+ }
+ return Box(keyedSubscript(key))
}
@@ -373,7 +376,7 @@ final public class MustacheBox : NSObject {
value: self,
// It lets Mustache extracts properties by name:
- keyedSubscript: { (key: String) -> MustacheBox in
+ keyedSubscript: { (key: String) -> Any? in
switch key {
case "firstName": return Box(self.firstName)
case "lastName": return Box(self.lastName)
diff --git a/Sources/RenderingEngine.swift b/Sources/RenderingEngine.swift
index 294f9319..6b910972 100644
--- a/Sources/RenderingEngine.swift
+++ b/Sources/RenderingEngine.swift
@@ -151,7 +151,7 @@ final class RenderingEngine {
// 2. Let willRender functions alter the box
for willRender in context.willRenderStack {
- box = willRender(tag, box)
+ box = Box(willRender(tag, box))
}
diff --git a/Sources/StandardLibrary.swift b/Sources/StandardLibrary.swift
index c37e6a69..623c756f 100644
--- a/Sources/StandardLibrary.swift
+++ b/Sources/StandardLibrary.swift
@@ -52,7 +52,7 @@ public struct StandardLibrary {
### Usage
let template = ...
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
*/
public static let HTMLEscape: MustacheBoxable = HTMLEscapeHelper()
@@ -78,7 +78,7 @@ public struct StandardLibrary {
### Usage
let template = ...
- template.registerInBaseContext("URLEscape", Box(StandardLibrary.URLEscape))
+ template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
*/
public static let URLEscape: MustacheBoxable = URLEscapeHelper()
@@ -112,7 +112,7 @@ public struct StandardLibrary {
### Usage
let template = ...
- template.registerInBaseContext("javascriptEscape", Box(StandardLibrary.javascriptEscape))
+ template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
*/
public static let javascriptEscape: MustacheBoxable = JavascriptEscapeHelper()
@@ -168,7 +168,7 @@ public struct StandardLibrary {
### Usage
let template = ...
- template.registerInBaseContext("each", Box(StandardLibrary.each))
+ template.register(StandardLibrary.each, forKey: "each")
*/
public static let each = EachFilter
@@ -215,7 +215,7 @@ public struct StandardLibrary {
### Usage
let template = ...
- template.registerInBaseContext("zip", Box(StandardLibrary.zip))
+ template.register(StandardLibrary.zip, forKey: "zip")
*/
public static let zip = ZipFilter
}
diff --git a/Sources/SwiftStandardLibrary.swift b/Sources/SwiftStandardLibrary.swift
index 98339700..99f87e5e 100644
--- a/Sources/SwiftStandardLibrary.swift
+++ b/Sources/SwiftStandardLibrary.swift
@@ -181,9 +181,9 @@ extension String : MustacheBoxable {
keyedSubscript: { (key: String) in
switch key {
case "length":
- return Box(self.characters.count)
+ return self.characters.count
default:
- return Box()
+ return nil
}
})
}
diff --git a/Sources/Template.swift b/Sources/Template.swift
index 2e94412d..b24dd4d2 100644
--- a/Sources/Template.swift
+++ b/Sources/Template.swift
@@ -135,8 +135,8 @@ final public class Template {
that describes the problem.
- returns: The rendered string.
*/
- public func render(_ box: MustacheBox = Box()) throws -> String {
- let rendering = try render(baseContext.extendedContext(box))
+ public func render(_ value: Any? = nil) throws -> String {
+ let rendering = try render(baseContext.extendedContext(value))
return rendering.string
}
@@ -213,8 +213,8 @@ final public class Template {
- registerInBaseContext
- Context.extendedContext
*/
- public func extendBaseContext(_ box: MustacheBox) {
- baseContext = baseContext.extendedContext(box)
+ public func extendBaseContext(_ value: Any?) {
+ baseContext = baseContext.extendedContext(value)
}
/**
@@ -235,10 +235,10 @@ final public class Template {
- baseContext
- extendBaseContext
- - Context.contextWithRegisteredKey
+ - Context.extendedContext(withRegisteredValue:forKey:)
*/
- public func registerInBaseContext(_ key: String, _ box: MustacheBox) {
- baseContext = baseContext.contextWithRegisteredKey(key, box: box)
+ public func register(_ value: Any?, forKey key: String) {
+ baseContext = baseContext.extendedContext(withRegisteredValue: value, forKey: key)
}
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index c2686caf..a6a9a543 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -57,46 +57,41 @@ private struct CustomBoxable : MustacheBoxable {
class BoxTests: XCTestCase {
- // MARK: - Box(MustacheBoxable)
+ // MARK: - MustacheBoxable
func testInt() {
do {
// Explicit type
let value: Int = 1
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable
let value: MustacheBoxable = 1
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = 1
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{.}}")
- let box = Box(1)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(1)
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: Int = 1
let template = try! Template(string: "{{#nested}}1{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
@@ -106,84 +101,74 @@ class BoxTests: XCTestCase {
// Explicit type
let value: CustomBoxable = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable
let value: MustacheBoxable = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{.}}")
- let box = Box(CustomBoxable(1))
- let rendering = try! template.render(box)
+ let rendering = try! template.render(CustomBoxable(1))
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: CustomBoxable = CustomBoxable(1)
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
- // MARK: - Box(MustacheBoxable?)
+ // MARK: - MustacheBoxable?
func testOptionalInt() {
do {
// Explicit type
let value: Int? = 1
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable?
let value: MustacheBoxable? = 1
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = 1 as Int?
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{.}}")
- let box = Box(1 as Int?)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(1 as Int?)
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: Int? = 1
let template = try! Template(string: "{{#nested}}1{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
@@ -193,76 +178,67 @@ class BoxTests: XCTestCase {
// Explicit type
let value: CustomBoxable? = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable?
let value: MustacheBoxable? = CustomBoxable(1)
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = CustomBoxable(1) as CustomBoxable?
let template = try! Template(string: "{{.}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{.}}")
- let box = Box(CustomBoxable(1) as CustomBoxable?)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(CustomBoxable(1) as CustomBoxable?)
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: CustomBoxable? = CustomBoxable(1)
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
- // MARK: - Box(Set)
+ // MARK: - Set
func testSetOfInt() {
do {
// Explicit type
let value: Set = [0,1,2]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
do {
// Infered element type
let value: Set = [0,1,2]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0,1,2] as Set)
- let rendering = try! template.render(box)
+ let rendering = try! template.render([0,1,2] as Set)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
do {
// Nested
let value: Set = [0,1,2]
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
}
@@ -272,84 +248,74 @@ class BoxTests: XCTestCase {
// Explicit type
let value: Set = [CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
do {
// Infered element type
let value: Set = [CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)] as Set)
- let rendering = try! template.render(box)
+ let rendering = try! template.render([CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)] as Set)
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
do {
// Nested
let value: Set = [CustomHashableBoxable(0),CustomHashableBoxable(1),CustomHashableBoxable(2)]
let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertTrue(["012", "021", "102", "120", "201", "210"].index(of: rendering) != nil)
}
}
- // MARK: - Box([String: MustacheBoxable])
+ // MARK: - [String: MustacheBoxable]
func testDictionaryOfStringInt() {
do {
// Explicit type
let value: [String: Int] = ["name": 1]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable
let value: [String: MustacheBoxable] = ["name": 1]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered element type
let value: Dictionary = ["name": 1]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = ["name": 1]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": 1])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["name": 1])
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: [String: Int] = ["name": 1]
let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
@@ -359,100 +325,88 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [String: CustomBoxable] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable
let value: [String: MustacheBoxable] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered element type
let value: Dictionary = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": CustomBoxable(1)])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["name": CustomBoxable(1)])
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: [String: CustomBoxable] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
- // MARK: - Box([String: MustacheBoxable?])
+ // MARK: - [String: MustacheBoxable?]
func testDictionaryOfStringOptionalInt() {
do {
// Explicit type
let value: [String: Int?] = ["name": 1]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable?
let value: [String: MustacheBoxable?] = ["name": 1 as Int?]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered element type
let value: Dictionary = ["name": 1 as Int?]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = ["name": 1 as Int?]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": 1 as Int?])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["name": 1 as Int?])
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: [String: Int?] = ["name": 1]
let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
@@ -462,206 +416,164 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [String: CustomBoxable?] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// As MustacheBoxable?
let value: [String: MustacheBoxable?] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered element type
let value: Dictionary = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{name}}")
- let box = Box(["name": CustomBoxable(1)])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["name": CustomBoxable(1)])
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: [String: CustomBoxable?] = ["name": CustomBoxable(1)]
let template = try! Template(string: "{{#nested}}{{name}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
- // MARK: - Box([String: Any])
+ // MARK: - [String: Any]
func testDictionaryOfStringAny() {
do {
// Explicit type
let value: [String: Any] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// As MustacheBoxable (won't compile)
// let value: [String: MustacheBoxable & Hashable] = ["int": 1, "string": "foo"]
// let template = try! Template(string: "{{int}}, {{string}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered element type (won't compile)
// let value: Dictionary = ["int": 1, "string": "foo"]
// let template = try! Template(string: "{{int}}, {{string}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered type (won't compile)
-// let value = ["int": 1, "string": "foo"]
// let template = try! Template(string: "{{int}}, {{string}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let value = ["int": 1, "string": "foo"]
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo")
}
- do {
- // Infered type
- let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(["int": 1, "string": "foo"])
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "1, foo")
- }
do {
// Nested
let value: [String: Any] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{#nested}}{{int}}, {{string}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1, foo")
}
}
- // MARK: - Box([String: Any?])
+ // MARK: - [String: Any?]
func testDictionaryOfStringOptionalAny() {
do {
// Explicit type
let value: [String: Any?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
// As MustacheBoxable?
let value: [String: MustacheBoxable?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
// Infered element type (won't compile)
// let value: Dictionary = ["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?]
// let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo, ")
}
do {
// Infered type (won't compile)
-// let value = ["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?]
// let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let value = ["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?]
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo, ")
}
- do {
- // Infered type
- let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(["int": 1 as Int?, "string": "foo" as String?, "missing": nil as CustomBoxable?])
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "1, foo, ")
- }
do {
// Nested
let value: [String: Any?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{#nested}}{{int}}, {{string}}, {{missing}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1, foo, ")
}
}
- // MARK: - Box([AnyHashable: Any])
+ // MARK: - [AnyHashable: Any]
func testDictionaryOfAnyHashableAny() {
do {
// Explicit type
let value: [AnyHashable: Any] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// As MustacheBoxable
let value: [AnyHashable: MustacheBoxable] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered element type (won't compile)
// let value: Dictionary = [AnyHashable("int"): 1, AnyHashable("string"): "foo"]
// let template = try! Template(string: "{{int}}, {{string}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered type (won't compile)
-// let value = [AnyHashable("int"): 1, AnyHashable("string"): "foo"]
// let template = try! Template(string: "{{int}}, {{string}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let value = [AnyHashable("int"): 1, AnyHashable("string"): "foo"]
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo")
}
- do {
- // Infered type
- let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box([AnyHashable("int"): 1, AnyHashable("string"): "foo"])
- let rendering = try! template.render(box)
- XCTAssertEqual(rendering, "1, foo")
- }
do {
// Nested
let value: [AnyHashable: Any] = ["int": 1, "string": "foo"]
let template = try! Template(string: "{{#nested}}{{int}}, {{string}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1, foo")
}
}
@@ -671,39 +583,35 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [AnyHashable: Any] = ["int": [1, 2], "string": ["foo", "bar"]]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "12, foobar")
}
do {
// Infered element type (won't compile)
let value: Dictionary = ["int": [1, 2], "string": ["foo", "bar"]]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "12, foobar")
}
do {
// Infered type (won't compile)
let value = ["int": [1, 2], "string": ["foo", "bar"]]
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "12, foobar")
}
do {
// Infered type
let template = try! Template(string: "{{int}}, {{string}}")
- let box = Box(["int": [1, 2], "string": ["foo", "bar"]])
- let rendering = try! template.render(box)
+ let value = ["int": [1, 2], "string": ["foo", "bar"]]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "12, foobar")
}
do {
// Nested
let value: [AnyHashable: Any] = ["int": [1, 2], "string": ["foo", "bar"]]
let template = try! Template(string: "{{#nested}}{{int}}, {{string}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "12, foobar")
}
}
@@ -713,145 +621,130 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [AnyHashable: Any] = ["int": ["name": 1], "string": ["name": "foo"]]
let template = try! Template(string: "{{int.name}}, {{string.name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered element type (won't compile)
let value: Dictionary = ["int": ["name": 1], "string": ["name": "foo"]]
let template = try! Template(string: "{{int.name}}, {{string.name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered type (won't compile)
let value = ["int": ["name": 1], "string": ["name": "foo"]]
let template = try! Template(string: "{{int.name}}, {{string.name}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// Infered type
let template = try! Template(string: "{{int.name}}, {{string.name}}")
- let box = Box(["int": ["name": 1], "string": ["name": "foo"]])
- let rendering = try! template.render(box)
+ let value = ["int": ["name": 1], "string": ["name": "foo"]]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo")
}
do {
// Nested
let value: [AnyHashable: Any] = ["int": ["name": 1], "string": ["name": "foo"]]
let template = try! Template(string: "{{#nested}}{{int.name}}, {{string.name}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1, foo")
}
}
- // MARK: - Box([AnyHashable: Any?])
+ // MARK: - [AnyHashable: Any?]
func testDictionaryOfAnyHashableOptionalAny() {
do {
// Explicit type
let value: [AnyHashable: Any?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
// As MustacheBoxable?
let value: [AnyHashable: MustacheBoxable?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
// Infered element type
let value: Dictionary = [AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
// Infered type
let value = [AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil]
let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1, foo, ")
}
do {
// Infered type (won't compile)
// let template = try! Template(string: "{{int}}, {{string}}, {{missing}}")
-// let box = Box([AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil])
-// let rendering = try! template.render(box)
+// let value = [AnyHashable("int"): 1 as Any?, AnyHashable("string"): "foo" as Any?, AnyHashable("missing"): nil]
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "1, foo, ")
}
do {
// Nested
let value: [AnyHashable: Any?] = ["int": 1, "string": "foo", "missing": nil]
let template = try! Template(string: "{{#nested}}{{int}}, {{string}}, {{missing}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1, foo, ")
}
}
- // MARK: - Box([MustacheBoxable])
+ // MARK: - [MustacheBoxable]
func testArrayOfInt() {
do {
// Explicit type
let value: [Int] = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// As MustacheBoxable
let value: [MustacheBoxable] = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered element type
let value: Array = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered type
let value = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0,1,2,3])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([0,1,2,3])
XCTAssertEqual(rendering, "0123")
}
do {
// Nested
let value: [Int] = [0,1,2,3]
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0123")
}
}
@@ -861,100 +754,88 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [CustomBoxable] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// As MustacheBoxable
let value: [MustacheBoxable] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered element type
let value: Array = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered type
let value = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)])
XCTAssertEqual(rendering, "0123")
}
do {
// Nested
let value: [CustomBoxable] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0123")
}
}
- // MARK: - Box([MustacheBoxable?])
+ // MARK: - [MustacheBoxable?]
func testArrayOfOptionalInt() {
do {
// Explicit type
let value: [Int?] = [0,1,2,3,nil]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// As MustacheBoxable?
let value: [MustacheBoxable?] = [0,1,2,3,nil]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered element type
let value: Array = [0 as Int?, 1 as Int?, 2 as Int?,3 as Int?, nil as Int?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered type
let value = [0 as Int?, 1 as Int?, 2 as Int?, 3 as Int?, nil as Int?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0 as Int?, 1 as Int?, 2 as Int?, 3 as Int?, nil as Int?])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([0 as Int?, 1 as Int?, 2 as Int?, 3 as Int?, nil as Int?])
XCTAssertEqual(rendering, "0123")
}
do {
// Nested
let value: [Int?] = [0,1,2,3,nil]
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0123")
}
}
@@ -964,92 +845,81 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [CustomBoxable?] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// As MustacheBoxable?
let value: [MustacheBoxable?] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered element type
let value: Array = [CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
// Infered type
let value = [CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([CustomBoxable(0) as CustomBoxable?, CustomBoxable(1) as CustomBoxable?, CustomBoxable(2) as CustomBoxable?, CustomBoxable(3) as CustomBoxable?])
XCTAssertEqual(rendering, "0123")
}
do {
// Nested
let value: [CustomBoxable?] = [CustomBoxable(0), CustomBoxable(1), CustomBoxable(2), CustomBoxable(3)]
let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0123")
}
}
- // MARK: - Box([Any])
+ // MARK: - [Any]
func testArrayOfAny() {
do {
// Explicit type
let value: [Any] = [0,"foo"]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0foo")
}
do {
// Infered element type (won't compile)
// let value: Array = [0,"foo"]
// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "0foo")
}
do {
// Infered type (won't compile)
// let value = [0,"foo"]
// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "0foo")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0,"foo"])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([0,"foo"])
XCTAssertEqual(rendering, "0foo")
}
do {
// Nested
let value: [Any] = [0,"foo"]
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0foo")
}
}
@@ -1059,39 +929,34 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [Any] = [[0,"foo"]]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0foo")
}
do {
// Infered element type
let value: Array = [[0,"foo"]]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0foo")
}
do {
// Infered type
let value = [[0,"foo"]]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0foo")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([[0,"foo"]])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([[0,"foo"]])
XCTAssertEqual(rendering, "0foo")
}
do {
// Nested
let value: [Any] = [[0,"foo"]]
let template = try! Template(string: "{{#nested}}{{#.}}{{.}}{{/}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0foo")
}
}
@@ -1101,115 +966,92 @@ class BoxTests: XCTestCase {
// Explicit type
let value: [Any] = [["name": 1]]
let template = try! Template(string: "{{#.}}{{name}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered element type (won't compile)
let value: Array = [["name": 1]]
let template = try! Template(string: "{{#.}}{{name}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
// Infered type
let value = [["name": 1]]
let template = try! Template(string: "{{#.}}{{name}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "1")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{name}}{{/}}")
- let box = Box([["name": 1]])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([["name": 1]])
XCTAssertEqual(rendering, "1")
}
do {
// Nested
let value: [Any] = [["name": 1]]
let template = try! Template(string: "{{#nested}}{{#.}}{{name}}{{/}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "1")
}
}
- // MARK: - Box([Any?])
+ // MARK: - [Any?]
func testArrayOfOptionalAny() {
do {
// Explicit type
let value: [Any?] = [0,nil,"foo"]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0foo")
}
do {
// As MustacheBoxable?
let value: [MustacheBoxable?] = [0,nil,"foo"]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0foo")
}
do {
// Infered element type (won't compile)
// let value: Array = [0 as Int?, nil as CustomBoxable?, "foo" as String?]
// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "0foo")
}
do {
// Infered type (won't compile)
// let value = [0 as Int?, nil as CustomBoxable?, "foo" as String?]
// let template = try! Template(string: "{{#.}}{{.}}{{/}}")
-// let box = Box(value)
-// let rendering = try! template.render(box)
+// let rendering = try! template.render(value)
// XCTAssertEqual(rendering, "0foo")
}
do {
- // Direct Box argument
+ // Direct argument
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box([0 as Int?, nil as CustomBoxable?, "foo" as String?])
- let rendering = try! template.render(box)
+ let rendering = try! template.render([0 as Int?, nil as CustomBoxable?, "foo" as String?])
XCTAssertEqual(rendering, "0foo")
}
do {
// Nested
let value: [Any?] = [0,nil,"foo"]
let template = try! Template(string: "{{#nested}}{{.}}{{/}}")
- let box = Box(["nested": value])
- let rendering = try! template.render(box)
+ let rendering = try! template.render(["nested": value])
XCTAssertEqual(rendering, "0foo")
}
}
- // MARK: - Box([non boxable])
-
- func testArrayOfNonMustacheBoxable() {
- class Class { }
- let array: [Any] = [Class()]
- let context = Context(Box(array))
- let box = context.mustacheBoxForKey("first")
- XCTAssertTrue(box.value == nil)
- }
-
-
- // MARK: - Box(NSArray)
+ // MARK: - NSArray
func testNSArrayOfInt() {
let value: NSArray = [0,1,2,3]
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(value)
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "0123")
}
@@ -1217,35 +1059,24 @@ class BoxTests: XCTestCase {
class Class: MustacheBoxable {
var mustacheBox: MustacheBox {
return MustacheBox(keyedSubscript: { (key: String) in
- return Box(key)
+ return key
})
}
}
let array = NSArray(object: Class())
- let context = Context(Box(array))
+ let context = Context(array)
let box = try! context.mustacheBoxForExpression("first.foo")
XCTAssertEqual((box.value as! String), "foo")
}
- func testNSArrayOfNonMustacheBoxable() {
- class Class {
- }
-
- let array = NSArray(object: Class())
- let context = Context(Box(array))
- let box = context.mustacheBoxForKey("first")
- XCTAssertTrue(box.value == nil)
- }
-
- // MARK: - Box(Range)
+ // MARK: - Range
func testRange() {
let value = 0..<10
let template = try! Template(string: "{{#.}}{{.}}{{/}}")
- let box = Box(Array(value))
- let rendering = try! template.render(box)
+ let rendering = try! template.render(Array(value))
XCTAssertEqual(rendering, "0123456789")
}
}
diff --git a/Tests/Public/BoxValueTests.swift b/Tests/Public/BoxValueTests.swift
index 494e4173..f5892a8e 100644
--- a/Tests/Public/BoxValueTests.swift
+++ b/Tests/Public/BoxValueTests.swift
@@ -26,102 +26,59 @@ import Mustache
class BoxValueTests: XCTestCase {
- func testCustomValueExtraction() {
- // Test that one can extract a custom value from MustacheBox.
+ func testBoxValue() {
+ // Test how values can be extracted from boxes
- struct BoxableStruct {
- let name: String
- func mustacheBox() -> MustacheBox {
+ struct BoxableStruct : MustacheBoxable {
+ var mustacheBox: MustacheBox {
return MustacheBox(value: self)
}
}
- struct Struct {
- let name: String
+ struct Struct : CustomDebugStringConvertible {
+ var debugDescription: String {
+ return "Struct"
+ }
}
- class BoxableClass {
- let name: String
- init(name: String) {
- self.name = name
+ class BoxableClass : MustacheBoxable {
+ init() {
}
- func mustacheBox() -> MustacheBox {
+ var mustacheBox: MustacheBox {
return MustacheBox(value: self)
}
}
- class Class {
- let name: String
- init(name: String) {
- self.name = name
+ class Class : CustomDebugStringConvertible {
+ init() {
+ }
+ var debugDescription: String {
+ return "Class"
}
}
- let boxableStruct = BoxableStruct(name: "BoxableStruct")
- let boxableClass = BoxableClass(name: "BoxableClass")
- let optionalBoxableClass: BoxableClass? = BoxableClass(name: "BoxableClass")
- let nsObject = NSDate()
- let referenceConvertible = Date()
-
- let boxedBoxableStruct = boxableStruct.mustacheBox()
- let boxedStruct = MustacheBox(value: Struct(name: "Struct"))
- let boxedBoxableClass = boxableClass.mustacheBox()
- let boxedOptionalBoxableClass = optionalBoxableClass!.mustacheBox()
- let boxedClass = MustacheBox(value: Class(name: "Class"))
- let boxedNSObject = Box(nsObject)
- let boxedReferenceConvertible = Box(referenceConvertible)
-
- let extractedBoxableStruct = boxedBoxableStruct.value as! BoxableStruct
- let extractedStruct = boxedStruct.value as! Struct
- let extractedBoxableClass = boxedBoxableClass.value as! BoxableClass
- let extractedOptionalBoxableClass = boxedOptionalBoxableClass.value as? BoxableClass
- let extractedClass = boxedClass.value as! Class
- let extractedNSObject = boxedNSObject.value as! NSDate
- let extractedReferenceConvertible = boxedReferenceConvertible.value as! Date
-
- XCTAssertEqual(extractedBoxableStruct.name, "BoxableStruct")
- XCTAssertEqual(extractedStruct.name, "Struct")
- XCTAssertEqual(extractedBoxableClass.name, "BoxableClass")
- XCTAssertEqual(extractedOptionalBoxableClass!.name, "BoxableClass")
- XCTAssertEqual(extractedClass.name, "Class")
- XCTAssertEqual(extractedNSObject, nsObject)
- XCTAssertEqual(extractedReferenceConvertible, referenceConvertible)
- }
-
- func testArrayValueForArray() {
- let originalValue = [1,2,3]
- let box = Box(originalValue)
- let extractedValue = box.value as! [Int]
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! Int }, [1,2,3])
- }
-
- func testArrayValueForNSArray() {
- let originalValue = NSArray(object: "foo")
- let box = Box(originalValue)
- let extractedValue = box.value as! NSArray
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
- }
-
- func testArrayValueForNSOrderedSet() {
- let originalValue = NSOrderedSet(object: "foo")
- let box = Box(originalValue)
- let extractedValue = box.value as! NSOrderedSet
- XCTAssertEqual(extractedValue, originalValue)
- let extractedArray: [MustacheBox] = box.arrayValue!
- XCTAssertEqual(extractedArray.map { $0.value as! String }, ["foo"])
- }
-
- func testDictionaryValueForNSDictionary() {
- let originalValue = NSDictionary(object: "value", forKey: "key" as NSCopying)
- let box = Box(originalValue)
- let extractedValue = box.value as! NSDictionary
- XCTAssertEqual(extractedValue, originalValue)
- let extractedDictionary: [String: MustacheBox] = box.dictionaryValue!
- XCTAssertEqual((extractedDictionary["key"]!.value as! String), "value")
+ func assert(value: Any?, isBoxedAs: T.Type) {
+ let template = try! Template(string: "{{#test(value)}}success{{/}}")
+ let data: [String: Any] = [
+ "value": value,
+ "test": Filter { (box: MustacheBox) in
+ box.value is T
+ }
+ ]
+ XCTAssertEqual(try! template.render(data), "success", "\(String(reflecting: value)) is not boxed as \(T.self)")
+ }
+ assert(value: BoxableStruct(), isBoxedAs: BoxableStruct.self)
+ assert(value: Struct(), isBoxedAs: Struct.self)
+ assert(value: BoxableClass(), isBoxedAs: BoxableClass.self)
+ assert(value: Class(), isBoxedAs: Class.self)
+ assert(value: NSObject(), isBoxedAs: NSObject.self)
+ assert(value: NSDate(), isBoxedAs: NSDate.self)
+ assert(value: Date(), isBoxedAs: Date.self)
+ assert(value: [1, 2, 3], isBoxedAs: [Any?].self)
+ assert(value: Set([1, 2, 3]), isBoxedAs: Set.self)
+ assert(value: ["foo": 1], isBoxedAs: Dictionary.self)
+ assert(value: NSArray(array: [1, 2, 3]), isBoxedAs: [Any?].self)
+ assert(value: NSSet(array: [1, 2, 3]), isBoxedAs: Set.self)
+ assert(value: NSDictionary(object: 1, forKey: "foo" as NSCopying), isBoxedAs: Dictionary.self)
}
-
}
diff --git a/Tests/Public/ConfigurationTests/ConfigurationBaseContextTests.swift b/Tests/Public/ConfigurationTests/ConfigurationBaseContextTests.swift
index d2cd5d31..f5c62c33 100644
--- a/Tests/Public/ConfigurationTests/ConfigurationBaseContextTests.swift
+++ b/Tests/Public/ConfigurationTests/ConfigurationBaseContextTests.swift
@@ -32,7 +32,7 @@ class ConfigurationBaseContextTests: XCTestCase {
}
func testDefaultConfigurationCustomBaseContext() {
- DefaultConfiguration.baseContext = Context(Box(["foo": "success"]))
+ DefaultConfiguration.baseContext = Context(["foo": "success"])
let template = try! Template(string: "{{foo}}")
let rendering = try! template.render()
@@ -40,16 +40,16 @@ class ConfigurationBaseContextTests: XCTestCase {
}
func testTemplateBaseContextOverridesDefaultConfigurationBaseContext() {
- DefaultConfiguration.baseContext = Context(Box(["foo": "failure"]))
+ DefaultConfiguration.baseContext = Context(["foo": "failure"])
let template = try! Template(string: "{{foo}}")
- template.baseContext = Context(Box(["foo": "success"]))
+ template.baseContext = Context(["foo": "success"])
let rendering = try! template.render()
XCTAssertEqual(rendering, "success")
}
func testDefaultRepositoryConfigurationHasDefaultConfigurationBaseContext() {
- DefaultConfiguration.baseContext = Context(Box(["foo": "success"]))
+ DefaultConfiguration.baseContext = Context(["foo": "success"])
let repository = TemplateRepository()
let template = try! repository.template(string: "{{foo}}")
@@ -59,7 +59,7 @@ class ConfigurationBaseContextTests: XCTestCase {
func testRepositoryConfigurationBaseContextWhenSettingTheWholeConfiguration() {
var configuration = Configuration()
- configuration.baseContext = Context(Box(["foo": "success"]))
+ configuration.baseContext = Context(["foo": "success"])
let repository = TemplateRepository()
repository.configuration = configuration
@@ -71,7 +71,7 @@ class ConfigurationBaseContextTests: XCTestCase {
func testRepositoryConfigurationBaseContextWhenUpdatingRepositoryConfiguration() {
let repository = TemplateRepository()
- repository.configuration.baseContext = Context(Box(["foo": "success"]))
+ repository.configuration.baseContext = Context(["foo": "success"])
let template = try! repository.template(string: "{{foo}}")
let rendering = try! template.render()
@@ -79,10 +79,10 @@ class ConfigurationBaseContextTests: XCTestCase {
}
func testRepositoryConfigurationBaseContextOverridesDefaultConfigurationBaseContextWhenSettingTheWholeConfiguration() {
- DefaultConfiguration.baseContext = Context(Box(["foo": "failure"]))
+ DefaultConfiguration.baseContext = Context(["foo": "failure"])
var configuration = Configuration()
- configuration.baseContext = Context(Box(["foo": "success"]))
+ configuration.baseContext = Context(["foo": "success"])
let repository = TemplateRepository()
repository.configuration = configuration
@@ -93,10 +93,10 @@ class ConfigurationBaseContextTests: XCTestCase {
}
func testRepositoryConfigurationBaseContextOverridesDefaultConfigurationBaseContextWhenUpdatingRepositoryConfiguration() {
- DefaultConfiguration.baseContext = Context(Box(["foo": "failure"]))
+ DefaultConfiguration.baseContext = Context(["foo": "failure"])
let repository = TemplateRepository()
- repository.configuration.baseContext = Context(Box(["foo": "success"]))
+ repository.configuration.baseContext = Context(["foo": "success"])
let template = try! repository.template(string: "{{foo}}")
let rendering = try! template.render()
@@ -105,13 +105,13 @@ class ConfigurationBaseContextTests: XCTestCase {
func testTemplateBaseContextOverridesRepositoryConfigurationBaseContextWhenSettingTheWholeConfiguration() {
var configuration = Configuration()
- configuration.baseContext = Context(Box(["foo": "failure"]))
+ configuration.baseContext = Context(["foo": "failure"])
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{foo}}")
- template.baseContext = Context(Box(["foo": "success"]))
+ template.baseContext = Context(["foo": "success"])
let rendering = try! template.render()
XCTAssertEqual(rendering, "success")
@@ -119,10 +119,10 @@ class ConfigurationBaseContextTests: XCTestCase {
func testTemplateBaseContextOverridesRepositoryConfigurationBaseContextWhenUpdatingRepositoryConfiguration() {
let repository = TemplateRepository()
- repository.configuration.baseContext = Context(Box(["foo": "failure"]))
+ repository.configuration.baseContext = Context(["foo": "failure"])
let template = try! repository.template(string: "{{foo}}")
- template.baseContext = Context(Box(["foo": "success"]))
+ template.baseContext = Context(["foo": "success"])
let rendering = try! template.render()
XCTAssertEqual(rendering, "success")
@@ -134,7 +134,7 @@ class ConfigurationBaseContextTests: XCTestCase {
var rendering = try! repository.template(string: "{{^foo}}success{{/foo}}").render()
XCTAssertEqual(rendering, "success")
- DefaultConfiguration.baseContext = Context(Box(["foo": "failure"]))
+ DefaultConfiguration.baseContext = Context(["foo": "failure"])
rendering = try! repository.template(string: "{{^foo}}success{{/foo}}").render()
XCTAssertEqual(rendering, "success")
}
@@ -145,14 +145,14 @@ class ConfigurationBaseContextTests: XCTestCase {
var rendering = try! repository.template(string: "{{^foo}}success{{/foo}}").render()
XCTAssertEqual(rendering, "success")
- repository.configuration.baseContext = Context(Box(["foo": "failure"]))
+ repository.configuration.baseContext = Context(["foo": "failure"])
rendering = try! repository.template(string: "{{^foo}}success{{/foo}}").render()
XCTAssertEqual(rendering, "success")
var configuration = Configuration()
- configuration.baseContext = Context(Box(["foo": "failure"]))
+ configuration.baseContext = Context(["foo": "failure"])
repository.configuration = configuration
rendering = try! repository.template(string: "{{^foo}}success{{/foo}}").render()
XCTAssertEqual(rendering, "success")
}
-}
\ No newline at end of file
+}
diff --git a/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift b/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift
index 5d08e809..e08b3921 100644
--- a/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift
+++ b/Tests/Public/ConfigurationTests/ConfigurationContentTypeTests.swift
@@ -45,14 +45,14 @@ class ConfigurationContentTypeTests: XCTestCase {
func testDefaultConfigurationContentTypeHTMLHasTemplateRenderEscapedInput() {
DefaultConfiguration.contentType = .html
let template = try! Template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
func testDefaultConfigurationContentTypeTextLHasTemplateRenderUnescapedInput() {
DefaultConfiguration.contentType = .text
let template = try! Template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -75,7 +75,7 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- _ = try! template.render(Box(render))
+ _ = try! template.render(render)
XCTAssertEqual(testedContentType!, ContentType.html)
}
@@ -98,7 +98,7 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- _ = try! template.render(Box(render))
+ _ = try! template.render(render)
XCTAssertEqual(testedContentType!, ContentType.text)
}
@@ -113,7 +113,7 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{#.}}{{/.}}")
- _ = try! template.render(Box(render))
+ _ = try! template.render(render)
XCTAssertEqual(testedContentType!, ContentType.html)
}
@@ -128,7 +128,7 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{#.}}{{/.}}")
- _ = try! template.render(Box(render))
+ _ = try! template.render(render)
XCTAssertEqual(testedContentType!, ContentType.text)
}
@@ -143,7 +143,7 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- _ = try! template.render(Box(render))
+ _ = try! template.render(render)
XCTAssertEqual(testedContentType!, ContentType.html)
}
@@ -159,21 +159,21 @@ class ConfigurationContentTypeTests: XCTestCase {
}
let template = try! Template(string: "{{.}}")
- _ = try! template.render(Box(render))
+ _ = try! template.render(render)
XCTAssertEqual(testedContentType!, ContentType.text)
}
func testPragmaContentTypeTextOverridesDefaultConfiguration() {
DefaultConfiguration.contentType = .html
let template = try! Template(string:"{{%CONTENT_TYPE:TEXT}}{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
func testPragmaContentTypeHTMLOverridesDefaultConfiguration() {
DefaultConfiguration.contentType = .text
let template = try! Template(string:"{{%CONTENT_TYPE:HTML}}{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -193,7 +193,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -201,7 +201,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration.contentType = .html
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -211,7 +211,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -219,7 +219,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration.contentType = .text
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -230,7 +230,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -239,7 +239,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration.contentType = .text
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -250,7 +250,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -259,7 +259,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration.contentType = .html
let template = try! repository.template(string: "{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -269,7 +269,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{%CONTENT_TYPE:TEXT}}{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -277,7 +277,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration.contentType = .html
let template = try! repository.template(string: "{{%CONTENT_TYPE:TEXT}}{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -287,7 +287,7 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{%CONTENT_TYPE:HTML}}{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
@@ -295,35 +295,35 @@ class ConfigurationContentTypeTests: XCTestCase {
let repository = TemplateRepository()
repository.configuration.contentType = .text
let template = try! repository.template(string: "{{%CONTENT_TYPE:HTML}}{{.}}")
- let rendering = try! template.render(Box("&"))
+ let rendering = try! template.render("&")
XCTAssertEqual(rendering, "&")
}
func testDefaultConfigurationMutationHasNoEffectAfterAnyTemplateHasBeenCompiled() {
let repository = TemplateRepository()
- var rendering = try! repository.template(string: "{{.}}").render(Box("&"))
+ var rendering = try! repository.template(string: "{{.}}").render("&")
XCTAssertEqual(rendering, "&")
DefaultConfiguration.contentType = .text
- rendering = try! repository.template(string: "{{.}}").render(Box("&"))
+ rendering = try! repository.template(string: "{{.}}").render("&")
XCTAssertEqual(rendering, "&")
}
func testRepositoryConfigurationMutationHasNoEffectAfterAnyTemplateHasBeenCompiled() {
let repository = TemplateRepository()
- var rendering = try! repository.template(string: "{{.}}").render(Box("&"))
+ var rendering = try! repository.template(string: "{{.}}").render("&")
XCTAssertEqual(rendering, "&")
repository.configuration.contentType = .text
- rendering = try! repository.template(string: "{{.}}").render(Box("&"))
+ rendering = try! repository.template(string: "{{.}}").render("&")
XCTAssertEqual(rendering, "&")
var configuration = Configuration()
configuration.contentType = .text
repository.configuration = configuration
- rendering = try! repository.template(string: "{{.}}").render(Box("&"))
+ rendering = try! repository.template(string: "{{.}}").render("&")
XCTAssertEqual(rendering, "&")
}
}
diff --git a/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift b/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift
index d303f66e..d9346dfa 100644
--- a/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift
+++ b/Tests/Public/ConfigurationTests/ConfigurationExtendBaseContextTests.swift
@@ -28,7 +28,7 @@ class ConfigurationExtendBaseContextTests: XCTestCase {
func testConfigurationExtendBaseContextWithValue() {
var configuration = Configuration()
- configuration.extendBaseContext(Box(["name": "Arthur"]))
+ configuration.extendBaseContext(["name": "Arthur"])
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{name}}")
@@ -36,7 +36,7 @@ class ConfigurationExtendBaseContextTests: XCTestCase {
var rendering = try! template.render()
XCTAssertEqual(rendering, "Arthur")
- rendering = try! template.render(Box(["name": "Bobby"]))
+ rendering = try! template.render(["name": "Bobby"])
XCTAssertEqual(rendering, "Bobby")
}
@@ -45,11 +45,11 @@ class ConfigurationExtendBaseContextTests: XCTestCase {
}
func testConfigurationExtendBaseContextWithWillRenderFunction() {
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
- return Box("delegate")
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
+ return "delegate"
}
var configuration = Configuration()
- configuration.extendBaseContext(Box(willRender))
+ configuration.extendBaseContext(willRender)
let repository = TemplateRepository()
repository.configuration = configuration
let template = try! repository.template(string: "{{name}}")
diff --git a/Tests/Public/ConfigurationTests/ConfigurationTagDelimitersTests.swift b/Tests/Public/ConfigurationTests/ConfigurationTagDelimitersTests.swift
index dec0fa55..1501d46c 100644
--- a/Tests/Public/ConfigurationTests/ConfigurationTagDelimitersTests.swift
+++ b/Tests/Public/ConfigurationTests/ConfigurationTagDelimitersTests.swift
@@ -43,7 +43,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
DefaultConfiguration.tagDelimiterPair = ("<%", "%>")
let template = try! Template(string: "<%subject%>")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -51,7 +51,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
DefaultConfiguration.tagDelimiterPair = ("<%", "%>")
let template = try! Template(string: "<%=[[ ]]=%>[[subject]]")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -75,7 +75,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
repository.configuration = configuration
let template = try! repository.template(string: "<%subject%>")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -84,7 +84,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
repository.configuration.tagDelimiterPair = ("<%", "%>")
let template = try! repository.template(string: "<%subject%>")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -97,7 +97,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
repository.configuration = configuration
let template = try! repository.template(string: "[[subject]]")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -108,7 +108,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
repository.configuration.tagDelimiterPair = ("[[", "]]")
let template = try! repository.template(string: "[[subject]]")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -119,7 +119,7 @@ class ConfigurationTagDelimitersTests: XCTestCase {
repository.configuration = configuration
let template = try! repository.template(string: "<%=[[ ]]=%>[[subject]]")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
@@ -128,35 +128,35 @@ class ConfigurationTagDelimitersTests: XCTestCase {
repository.configuration.tagDelimiterPair = ("<%", "%>")
let template = try! repository.template(string: "<%=[[ ]]=%>[[subject]]")
- let rendering = try! template.render(Box(["subject": "---"]))
+ let rendering = try! template.render(["subject": "---"])
XCTAssertEqual(rendering, "---")
}
func testDefaultConfigurationMutationHasNoEffectAfterAnyTemplateHasBeenCompiled() {
let repository = TemplateRepository()
- var rendering = try! repository.template(string: "{{foo}}<%foo%>").render(Box(["foo": "foo"]))
+ var rendering = try! repository.template(string: "{{foo}}<%foo%>").render(["foo": "foo"])
XCTAssertEqual(rendering, "foo<%foo%>")
DefaultConfiguration.tagDelimiterPair = ("<%", "%>")
- rendering = try! repository.template(string: "{{foo}}<%foo%>").render(Box(["foo": "foo"]))
+ rendering = try! repository.template(string: "{{foo}}<%foo%>").render(["foo": "foo"])
XCTAssertEqual(rendering, "foo<%foo%>")
}
func testRepositoryConfigurationMutationHasNoEffectAfterAnyTemplateHasBeenCompiled() {
let repository = TemplateRepository()
- var rendering = try! repository.template(string: "{{foo}}<%foo%>").render(Box(["foo": "foo"]))
+ var rendering = try! repository.template(string: "{{foo}}<%foo%>").render(["foo": "foo"])
XCTAssertEqual(rendering, "foo<%foo%>")
repository.configuration.tagDelimiterPair = ("<%", "%>")
- rendering = try! repository.template(string: "{{foo}}<%foo%>").render(Box(["foo": "foo"]))
+ rendering = try! repository.template(string: "{{foo}}<%foo%>").render(["foo": "foo"])
XCTAssertEqual(rendering, "foo<%foo%>")
var configuration = Configuration()
configuration.tagDelimiterPair = ("<%", "%>")
repository.configuration = configuration
- rendering = try! repository.template(string: "{{foo}}<%foo%>").render(Box(["foo": "foo"]))
+ rendering = try! repository.template(string: "{{foo}}<%foo%>").render(["foo": "foo"])
XCTAssertEqual(rendering, "foo<%foo%>")
}
}
diff --git a/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift b/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift
index 6fb52e57..373ead05 100644
--- a/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift
+++ b/Tests/Public/ContextTests/ContextRegisteredKeyTests.swift
@@ -28,36 +28,36 @@ class ContextRegisteredKeyTests: XCTestCase {
func testRegisteredKeyCanBeAccessed() {
let template = try! Template(string: "{{safe}}")
- template.registerInBaseContext("safe", Box("important"))
+ template.register("important", forKey: "safe")
let rendering = try! template.render()
XCTAssertEqual(rendering, "important")
}
func testMultipleRegisteredKeysCanBeAccessed() {
let template = try! Template(string: "{{safe1}}, {{safe2}}")
- template.registerInBaseContext("safe1", Box("important1"))
- template.registerInBaseContext("safe2", Box("important2"))
+ template.register("important1", forKey: "safe1")
+ template.register("important2", forKey: "safe2")
let rendering = try! template.render()
XCTAssertEqual(rendering, "important1, important2")
}
func testRegisteredKeysCanNotBeShadowed() {
let template = try! Template(string: "{{safe}}, {{fragile}}")
- template.registerInBaseContext("safe", Box("important"))
- let rendering = try! template.render(Box(["safe": "error", "fragile": "not important"]))
+ template.register("important", forKey: "safe")
+ let rendering = try! template.render(["safe": "error", "fragile": "not important"])
XCTAssertEqual(rendering, "important, not important")
}
func testDeepRegisteredKeyCanBeAccessedViaFullKeyPath() {
let template = try! Template(string: "{{safe.name}}")
- template.registerInBaseContext("safe", Box(["name": "important"]))
+ template.register(["name": "important"], forKey: "safe")
let rendering = try! template.render()
XCTAssertEqual(rendering, "important")
}
func testDeepRegisteredKeyCanBeAccessedViaScopedExpression() {
let template = try! Template(string: "{{#safe}}{{.name}}{{/safe}}")
- template.registerInBaseContext("safe", Box(["name": "important"]))
+ template.register(["name": "important"], forKey: "safe")
let rendering = try! template.render()
XCTAssertEqual(rendering, "important")
}
@@ -65,8 +65,8 @@ class ContextRegisteredKeyTests: XCTestCase {
func testDeepRegisteredKeyCanBeShadowed() {
// This is more a caveat than a feature, isn't it?
let template = try! Template(string: "{{#safe}}{{#evil}}{{name}}{{/evil}}{{/safe}}")
- template.registerInBaseContext("safe", Box(["name": "important"]))
- let rendering = try! template.render(Box(["evil": ["name": "hacked"]] as NSDictionary))
+ template.register(["name": "important"], forKey: "safe")
+ let rendering = try! template.render(["evil": ["name": "hacked"]])
XCTAssertEqual(rendering, "hacked")
}
}
diff --git a/Tests/Public/ContextTests/ContextTests.swift b/Tests/Public/ContextTests/ContextTests.swift
index e28d78be..28fbf458 100644
--- a/Tests/Public/ContextTests/ContextTests.swift
+++ b/Tests/Public/ContextTests/ContextTests.swift
@@ -32,8 +32,8 @@ class ContextTests: XCTestCase {
var rendering = try! template.render()
XCTAssertEqual(rendering, "")
- let box = Box(["foo": "bar"])
- template.baseContext = Context(box)
+ let value = ["foo": "bar"]
+ template.baseContext = Context(value)
rendering = try! template.render()
XCTAssertEqual(rendering, "bar")
}
@@ -44,12 +44,12 @@ class ContextTests: XCTestCase {
func testContextWithWillRenderFunction() {
var success = false
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
success = true
return box
}
let template = try! Template(string: "{{success}}")
- template.baseContext = Context(Box(willRender))
+ template.baseContext = Context(willRender)
_ = try! template.render()
XCTAssertTrue(success)
}
@@ -58,24 +58,24 @@ class ContextTests: XCTestCase {
var context = Context()
XCTAssertTrue(context.topBox.isEmpty)
- context = context.extendedContext(Box("object"))
+ context = context.extendedContext("object")
XCTAssertEqual((context.topBox.value as! String), "object")
// TODO: import protected test from GRMustacheContextTopMustacheObjectTest.testTopMustacheObject
// TODO: check if those commented lines are worth decommenting
-// let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+// let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
// return box
// }
-// context = context.extendedContext(Box(willRender))
+// context = context.extendedContext(willRender)
// XCTAssertEqual(context.topBox.value as String, "object")
- context = context.extendedContext(Box("object2"))
+ context = context.extendedContext("object2")
XCTAssertEqual((context.topBox.value as! String), "object2")
}
func testSubscript() {
- let context = Context(Box(["name": "name1", "a": ["name": "name2"]] as NSDictionary))
+ let context = Context(["name": "name1", "a": ["name": "name2"]])
// '.' is an expression, not a key
XCTAssertTrue(context.mustacheBoxForKey(".").isEmpty)
diff --git a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
index 78bd36d3..9bc240e2 100644
--- a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
+++ b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
@@ -27,31 +27,31 @@ import Mustache
class ContextValueForMustacheExpressionTests: XCTestCase {
func testImplicitIteratorExpression() {
- let context = Context(Box("success"))
+ let context = Context("success")
let box = try! context.mustacheBoxForExpression(".")
let string = box.value as? String
XCTAssertEqual(string!, "success")
}
func testIdentifierExpression() {
- let context = Context(Box(["name": "success"]))
+ let context = Context(["name": "success"])
let box = try! context.mustacheBoxForExpression("name")
let string = box.value as? String
XCTAssertEqual(string!, "success")
}
func testScopedExpression() {
- let context = Context(Box(["a": ["name": "success"]] as NSDictionary))
+ let context = Context(["a": ["name": "success"]])
let box = try! context.mustacheBoxForExpression("a.name")
let string = box.value as? String
XCTAssertEqual(string!, "success")
}
func testFilteredExpression() {
- let filter = Filter({ (string: String?) -> MustacheBox in
- return Box(string!.uppercased())
+ let filter = Filter({ (string: String?) -> Any? in
+ return string!.uppercased()
})
- let context = Context(Box(["name": "success", "f": filter]))
+ let context = Context(["name": "success", "f": filter])
let box = try! context.mustacheBoxForExpression("f(name)")
let string = box.value as? String
XCTAssertEqual(string!, "SUCCESS")
diff --git a/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift b/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
index 203a5036..6e133a67 100644
--- a/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
+++ b/Tests/Public/DocumentationTests/MustacheBoxDocumentationTests.swift
@@ -32,7 +32,7 @@ class MustacheBoxDocumentationTests: XCTestCase {
}
let template = try! Template(string: "{{object}}")
let data = ["object": render]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "foo")
}
@@ -48,7 +48,7 @@ class MustacheBoxDocumentationTests: XCTestCase {
//
// Extend the current context with ["value": "foo"], and proceed
// with regular rendering of the inner content of the section.
- let context = info.context.extendedContext(Box(["value": "foo"]))
+ let context = info.context.extendedContext(["value": "foo"])
return try info.tag.render(context)
}
}
@@ -56,12 +56,12 @@ class MustacheBoxDocumentationTests: XCTestCase {
// Renders "variable"
let template1 = try! Template(string: "{{object}}")
- let rendering1 = try! template1.render(Box(data))
+ let rendering1 = try! template1.render(data)
XCTAssertEqual(rendering1, "variable")
// Renders "value: foo"
let template2 = try! Template(string: "{{#object}}value: {{value}}{{/object}}")
- let rendering2 = try! template2.render(Box(data))
+ let rendering2 = try! template2.render(data)
XCTAssertEqual(rendering2, "value: foo")
}
}
diff --git a/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift b/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
index 66df57d7..0755cc39 100644
--- a/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
+++ b/Tests/Public/DocumentationTests/MustacheRenderableGuideTests.swift
@@ -36,10 +36,10 @@ class MustacheRenderableGuideTests: XCTestCase {
}
}
- var rendering = try! Template(string: "{{.}}").render(Box(render))
+ var rendering = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(rendering, "I'm rendering a {{ variable }} tag.")
- rendering = try! Template(string: "{{#.}}{{/}}").render(Box(render))
+ rendering = try! Template(string: "{{#.}}{{/}}").render(render)
XCTAssertEqual(rendering, "I'm rendering a {{# section }}...{{/ }} tag.")
}
@@ -48,7 +48,7 @@ class MustacheRenderableGuideTests: XCTestCase {
return Rendering("Arthur & Cie")
}
- let rendering = try! Template(string: "{{.}}|{{{.}}}").render(Box(render))
+ let rendering = try! Template(string: "{{.}}|{{{.}}}").render(render)
XCTAssertEqual(rendering, "Arthur & Cie|Arthur & Cie")
}
@@ -58,10 +58,10 @@ class MustacheRenderableGuideTests: XCTestCase {
return Rendering("\(rendering.string)", rendering.contentType)
}
- let box = Box([
+ let value: [String: Any] = [
"strong": render,
- "name": "Arthur"])
- let rendering = try! Template(string: "{{#strong}}{{name}}{{/strong}}").render(box)
+ "name": "Arthur"]
+ let rendering = try! Template(string: "{{#strong}}{{name}}{{/strong}}").render(value)
XCTAssertEqual(rendering, "Arthur")
}
@@ -70,8 +70,8 @@ class MustacheRenderableGuideTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering(rendering.string + rendering.string, rendering.contentType)
}
- let box = Box(["twice": render])
- let rendering = try! Template(string: "{{#twice}}Success{{/twice}}").render(box)
+ let value = ["twice": render]
+ let rendering = try! Template(string: "{{#twice}}Success{{/twice}}").render(value)
XCTAssertEqual(rendering, "SuccessSuccess")
}
@@ -80,11 +80,11 @@ class MustacheRenderableGuideTests: XCTestCase {
let template = try! Template(string: "\(info.tag.innerTemplateString)")
return try template.render(info.context)
}
- let box = Box([
+ let value: [String: Any] = [
"link": render,
"name": "Arthur",
- "url": "/people/123"])
- let rendering = try! Template(string: "{{# link }}{{ name }}{{/ link }}").render(box)
+ "url": "/people/123"]
+ let rendering = try! Template(string: "{{# link }}{{ name }}{{/ link }}").render(value)
XCTAssertEqual(rendering, "Arthur")
}
@@ -102,8 +102,8 @@ class MustacheRenderableGuideTests: XCTestCase {
"name": "Orson Welles",
"url": "/people/123",
"link": link2]
- let box = Box(["items": [item1, item2]])
- let rendering = try! Template(string: "{{#items}}{{link}}{{/items}}").render(box)
+ let value = ["items": [item1, item2]]
+ let rendering = try! Template(string: "{{#items}}{{link}}{{/items}}").render(value)
XCTAssertEqual(rendering, "Citizen KaneOrson Welles")
}
@@ -112,19 +112,19 @@ class MustacheRenderableGuideTests: XCTestCase {
let firstName: String
let lastName: String
var mustacheBox: MustacheBox {
- let keyedSubscript = { (key: String) -> MustacheBox in
+ let keyedSubscript = { (key: String) -> Any? in
switch key {
case "firstName":
- return Box(self.firstName)
+ return self.firstName
case "lastName":
- return Box(self.lastName)
+ return self.lastName
default:
- return Box()
+ return nil
}
}
let render = { (info: RenderingInfo) -> Rendering in
let template = try! Template(named: "Person", bundle: Bundle(for: MustacheRenderableGuideTests.self))
- let context = info.context.extendedContext(Box(self))
+ let context = info.context.extendedContext(self)
return try template.render(context)
}
return MustacheBox(
@@ -138,19 +138,19 @@ class MustacheRenderableGuideTests: XCTestCase {
let title: String
let director: Person
var mustacheBox: MustacheBox {
- let keyedSubscript = { (key: String) -> MustacheBox in
+ let keyedSubscript = { (key: String) -> Any? in
switch key {
case "title":
- return Box(self.title)
+ return self.title
case "director":
- return Box(self.director)
+ return self.director
default:
- return Box()
+ return nil
}
}
let render = { (info: RenderingInfo) -> Rendering in
let template = try! Template(named: "Movie", bundle: Bundle(for: MustacheRenderableGuideTests.self))
- let context = info.context.extendedContext(Box(self))
+ let context = info.context.extendedContext(self)
return try template.render(context)
}
return MustacheBox(
@@ -164,7 +164,7 @@ class MustacheRenderableGuideTests: XCTestCase {
let movie = Movie(title:"Citizen Kane", director: director)
let template = try! Template(string: "{{ movie }}")
- let rendering = try! template.render(Box(["movie": movie]))
+ let rendering = try! template.render(["movie": movie])
XCTAssertEqual(rendering, "Citizen Kane by Orson Welles")
}
@@ -185,7 +185,7 @@ class MustacheRenderableGuideTests: XCTestCase {
}
let template = try! Template(string: "{{#list(nav)}}{{title}}{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["list": Filter(listFilter)]))
+ template.baseContext = template.baseContext.extendedContext(["list": Filter(listFilter)])
let item1 = [
"url": "http://mustache.github.io",
@@ -193,9 +193,9 @@ class MustacheRenderableGuideTests: XCTestCase {
let item2 = [
"url": "http://github.com/groue/GRMustache.swift",
"title": "GRMustache.swift"]
- let box = Box(["nav": [item1, item2]])
+ let value = ["nav": [item1, item2]]
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "")
}
}
diff --git a/Tests/Public/DocumentationTests/ReadMeTests.swift b/Tests/Public/DocumentationTests/ReadMeTests.swift
index 99358d3b..c95feb7f 100644
--- a/Tests/Public/DocumentationTests/ReadMeTests.swift
+++ b/Tests/Public/DocumentationTests/ReadMeTests.swift
@@ -59,7 +59,7 @@ class ReadMeTests: XCTestCase {
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
"in_ca": true]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "Hello Chris\nYou have just won 10000 dollars!\n\nWell, 6000.0 dollars, after taxes.\n")
}
@@ -83,15 +83,15 @@ class ReadMeTests: XCTestCase {
// Register the pluralize filter for all Mustache renderings:
- Mustache.DefaultConfiguration.registerInBaseContext("pluralize", Box(pluralizeFilter))
+ Mustache.DefaultConfiguration.register(pluralizeFilter, forKey: "pluralize")
// I have 3 cats.
let testBundle = Bundle(for: type(of: self))
let template = try! Template(named: "ReadMeExample2", bundle: testBundle)
- let data: NSDictionary = ["cats": ["Kitty", "Pussy", "Melba"]]
- let rendering = try! template.render(Box(data))
+ let data = ["cats": ["Kitty", "Pussy", "Melba"]]
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "I have 3 cats.")
}
@@ -107,9 +107,9 @@ class ReadMeTests: XCTestCase {
return MustacheBox(value: self, keyedSubscript: { (key: String) in
switch key {
case "name":
- return Box(self.name)
+ return self.name
default:
- return Box()
+ return nil
}
})
}
@@ -117,7 +117,7 @@ class ReadMeTests: XCTestCase {
let user = User(name: "Arthur")
let template = try! Template(string: "Hello {{name}}!")
- let rendering = try! template.render(Box(user))
+ let rendering = try! template.render(user)
XCTAssertEqual(rendering, "Hello Arthur!")
}
@@ -127,10 +127,10 @@ class ReadMeTests: XCTestCase {
percentFormatter.numberStyle = .percent
let template = try! Template(string: "{{ percent(x) }}")
- template.registerInBaseContext("percent", Box(percentFormatter))
+ template.register(percentFormatter, forKey: "percent")
let data = ["x": 0.5]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "50%")
}
@@ -140,10 +140,10 @@ class ReadMeTests: XCTestCase {
percentFormatter.numberStyle = .percent
let template = try! Template(string: "{{# percent }}{{ x }}{{/ }}")
- template.registerInBaseContext("percent", Box(percentFormatter))
+ template.register(percentFormatter, forKey: "percent")
let data = ["x": 0.5]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "50%")
}
@@ -158,15 +158,15 @@ class ReadMeTests: XCTestCase {
dateFormatter.dateStyle = .medium
let template = try! Template(string: templateString)
- template.registerInBaseContext("format", Box(dateFormatter))
+ template.register(dateFormatter, forKey: "format")
- let data: NSDictionary = [
+ let data: [String: Any] = [
"name": "Arthur",
"date": Date(),
"real_date": Date().addingTimeInterval(60*60*24*3),
"late": true
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssert(rendering.characters.count > 0)
}
}
diff --git a/Tests/Public/FilterTests/FilterTests.swift b/Tests/Public/FilterTests/FilterTests.swift
index cdcaac86..1d4d1145 100644
--- a/Tests/Public/FilterTests/FilterTests.swift
+++ b/Tests/Public/FilterTests/FilterTests.swift
@@ -31,116 +31,105 @@ class FilterTests: XCTestCase {
}
func testFilterCanChain() {
- let box = Box([
+ let value: [String: Any] = [
"name": "Name",
- "uppercase": Filter { (string: String?) -> MustacheBox in
- return Box(string?.uppercased())
+ "uppercase": Filter { (string: String?) in
+ return string?.uppercased()
},
- "prefix": Filter { (string: String?) -> MustacheBox in
- return Box("prefix\(string!)")
+ "prefix": Filter { (string: String?) in
+ return "prefix\(string!)"
}
- ])
+ ]
let template = try! Template(string:"<{{name}}> <{{prefix(name)}}> <{{uppercase(name)}}> <{{prefix(uppercase(name))}}> <{{uppercase(prefix(name))}}>")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, " ")
}
func testScopedValueAreExtractedOutOfAFilterExpression() {
let template = try! Template(string:"<{{f(object).name}}> {{#f(object)}}<{{name}}>{{/f(object)}}")
- var box: MustacheBox
+ var value: [String: Any]
var rendering: String
- box = Box([
+ value = [
"object": ["name": "objectName"],
"name": "rootName",
- "f": Filter { (box: MustacheBox) -> MustacheBox in
- return box
- }
- ])
- rendering = try! template.render(box)
+ "f": Filter { (box: MustacheBox) in box }
+ ]
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, " ")
- box = Box([
+ value = [
"object": ["name": "objectName"],
"name": "rootName",
- "f": Filter { (_: MustacheBox) -> MustacheBox in
- return Box(["name": "filterName"])
- }
- ])
- rendering = try! template.render(box)
+ "f": Filter { _ in ["name": "filterName"] }
+ ]
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, " ")
- box = Box([
+ value = [
"object": ["name": "objectName"],
"name": "rootName",
- "f": Filter { (_: MustacheBox) -> MustacheBox in
- return Box(true)
- }
- ])
- rendering = try! template.render(box)
+ "f": Filter { _ in true }
+ ]
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "<> ")
}
func testFilterArgumentsDoNotEnterSectionContextStack() {
- let box = Box([
+ let value: [String: Any] = [
"test": "success",
"filtered": ["test": "failure"],
- "filter": Filter { (_: MustacheBox) -> MustacheBox in
- return Box(true)
- }])
+ "filter": Filter { (_: MustacheBox) in true }
+ ]
let template = try! Template(string:"{{#filter(filtered)}}<{{test}} instead of {{#filtered}}{{test}}{{/filtered}}>{{/filter(filtered)}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "")
}
func testFilterNameSpace() {
- let doubleFilter = Box(Filter { (x: Int?) -> MustacheBox in
- return Box((x ?? 0) * 2)
- })
- let box = Box([
+ let doubleFilter = Filter { (x: Int?) in
+ return (x ?? 0) * 2
+ }
+ let value: [String: Any] = [
"x": 1,
"math": ["double": doubleFilter]
- ])
+ ]
let template = try! Template(string:"{{ math.double(x) }}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "2")
}
func testFilterCanReturnFilter() {
- let filterValue = Box(Filter { (string1: String?) -> MustacheBox in
- return Box(Filter { (string2: String?) -> MustacheBox in
- return Box("\(string1!)\(string2!)")
- })
- })
- let box = Box([
+ let filterValue = Filter { (string1: String?) in
+ return Filter { (string2: String?) in
+ return "\(string1!)\(string2!)"
+ }
+ }
+ let value: [String: Any] = [
"prefix": "prefix",
"value": "value",
- "f": filterValue])
+ "f": filterValue]
let template = try! Template(string:"{{f(prefix)(value)}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "prefixvalue")
}
func testImplicitIteratorCanReturnFilter() {
- let box = Box(Filter { (_: MustacheBox) -> MustacheBox in
- return Box("filter")
- })
+ let filter = Filter { (_: MustacheBox) in "filter" }
let template = try! Template(string:"{{.(a)}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(filter)
XCTAssertEqual(rendering, "filter")
}
func testMissingFilterError() {
- let box = Box([
+ let value: [String: Any] = [
"name": "Name",
- "replace": Filter { (_: MustacheBox) -> MustacheBox in
- return Box("replace")
- }
- ])
+ "replace": Filter { (_: MustacheBox) in "replace" }
+ ]
var template = try! Template(string:"<{{missing(missing)}}>")
do {
- _ = try template.render(box)
+ _ = try template.render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -150,7 +139,7 @@ class FilterTests: XCTestCase {
template = try! Template(string:"<{{missing(name)}}>")
do {
- _ = try template.render(box)
+ _ = try template.render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -160,7 +149,7 @@ class FilterTests: XCTestCase {
template = try! Template(string:"<{{replace(missing(name))}}>")
do {
- _ = try template.render(box)
+ _ = try template.render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -170,7 +159,7 @@ class FilterTests: XCTestCase {
template = try! Template(string:"<{{missing(replace(name))}}>")
do {
- _ = try template.render(box)
+ _ = try template.render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -180,14 +169,14 @@ class FilterTests: XCTestCase {
}
func testNotAFilterError() {
- let box = Box([
+ let value = [
"name": "Name",
"filter": "filter"
- ])
+ ]
let template = try! Template(string:"<{{filter(name)}}>")
do {
- _ = try template.render(box)
+ _ = try template.render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -220,7 +209,7 @@ class FilterTests: XCTestCase {
func testNotAFilterErrorDescriptionContainsLineNumber() {
let template = try! Template(string: "\n{{f(x)}}")
do {
- _ = try template.render(Box(["f": "foo"]))
+ _ = try template.render(["f": "foo"])
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -239,55 +228,55 @@ class FilterTests: XCTestCase {
func testFilterOfOptionalInt() {
let square = Filter { (x: Int?) in
if let x = x {
- return Box(x * x)
+ return x * x
} else {
- return Box("Nil")
+ return "Nil"
}
}
let template = try! Template(string: "{{square(x)}}")
- template.registerInBaseContext("square", Box(square))
+ template.register(square, forKey: "square")
var rendering: String
- rendering = try! template.render(Box(["x": 10]))
+ rendering = try! template.render(["x": 10])
XCTAssertEqual(rendering, "100")
- rendering = try! template.render(Box())
+ rendering = try! template.render(nil)
XCTAssertEqual(rendering, "Nil")
- rendering = try! template.render(Box(["x": NSNull()]))
+ rendering = try! template.render(["x": NSNull()])
XCTAssertEqual(rendering, "Nil")
- rendering = try! template.render(Box(["x": "foo"]))
+ rendering = try! template.render(["x": "foo"])
XCTAssertEqual(rendering, "Nil")
}
func testFilterOfOptionalString() {
let twice = Filter { (x: String?) in
if let x = x {
- return Box(x + x)
+ return x + x
} else {
- return Box("Nil")
+ return "Nil"
}
}
let template = try! Template(string: "{{twice(x)}}")
- template.registerInBaseContext("twice", Box(twice))
+ template.register(twice, forKey: "twice")
var rendering: String
- rendering = try! template.render(Box(["x": "A"]))
+ rendering = try! template.render(["x": "A"])
XCTAssertEqual(rendering, "AA")
- rendering = try! template.render(Box(["x": "A" as NSString]))
+ rendering = try! template.render(["x": "A" as NSString])
XCTAssertEqual(rendering, "AA")
- rendering = try! template.render(Box())
+ rendering = try! template.render(nil)
XCTAssertEqual(rendering, "Nil")
- rendering = try! template.render(Box(["x": NSNull()]))
+ rendering = try! template.render(["x": NSNull()])
XCTAssertEqual(rendering, "Nil")
- rendering = try! template.render(Box(["x": 1]))
+ rendering = try! template.render(["x": 1])
XCTAssertEqual(rendering, "Nil")
}
@@ -299,7 +288,7 @@ class FilterTests: XCTestCase {
}
let template = try! Template(string: "\n\n{{f(x)}}")
- template.registerInBaseContext("f", Box(filter))
+ template.register(filter, forKey: "f")
do {
_ = try template.render()
@@ -320,7 +309,7 @@ class FilterTests: XCTestCase {
}
let template = try! Template(string: "\n\n{{f(x)}}")
- template.registerInBaseContext("f", Box(filter))
+ template.register(filter, forKey: "f")
do {
_ = try template.render()
@@ -343,7 +332,7 @@ class FilterTests: XCTestCase {
}
let template = try! Template(string: "\n\n{{f(x)}}")
- template.registerInBaseContext("f", Box(filter))
+ template.register(filter, forKey: "f")
do {
_ = try template.render()
diff --git a/Tests/Public/FilterTests/VariadicFilterTests.swift b/Tests/Public/FilterTests/VariadicFilterTests.swift
index 7bf7796d..823bb2cc 100644
--- a/Tests/Public/FilterTests/VariadicFilterTests.swift
+++ b/Tests/Public/FilterTests/VariadicFilterTests.swift
@@ -27,98 +27,98 @@ import Mustache
class VariadicFilterTests: XCTestCase {
func testVariadicFilterCanAccessArguments() {
- let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
- return Box(boxes.map { ($0.value as? String) ?? "" }.joined(separator: ","))
+ let filter = VariadicFilter({ (boxes: [MustacheBox]) -> Any? in
+ return boxes.map { ($0.value as? String) ?? "" }.joined(separator: ",")
})
- let box = Box([
- "a": Box("a"),
- "b": Box("b"),
- "c": Box("c"),
- "join": Box(filter)])
+ let value: [String: Any] = [
+ "a": "a",
+ "b": "b",
+ "c": "c",
+ "join": filter]
let template = try! Template(string:"{{join(a)}} {{join(a,b)}} {{join(a,b,c)}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "a a,b a,b,c")
}
func testVariadicFilterCanReturnFilter() {
- let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
+ let filter = VariadicFilter({ (boxes: [MustacheBox]) -> Any? in
let joined = boxes.map { ($0.value as? String) ?? "" }.joined(separator: ",")
- return Box(Filter({ (box: MustacheBox) -> MustacheBox in
- return Box(joined + "+" + ((box.value as? String) ?? ""))
- }))
+ return Filter { (box: MustacheBox) -> Any? in
+ return joined + "+" + ((box.value as? String) ?? "")
+ }
})
- let box = Box([
- "a": Box("a"),
- "b": Box("b"),
- "c": Box("c"),
- "f": Box(filter)])
+ let value: [String: Any] = [
+ "a": "a",
+ "b": "b",
+ "c": "c",
+ "f": filter]
let template = try! Template(string:"{{f(a)(a)}} {{f(a,b)(a)}} {{f(a,b,c)(a)}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "a+a a,b+a a,b,c+a")
}
func testVariadicFilterCanBeRootOfScopedExpression() {
- let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
- return Box(["foo": "bar"])
+ let filter = VariadicFilter({ (boxes: [MustacheBox]) -> Any? in
+ return ["foo": "bar"]
})
- let box = Box(["f": Box(filter)])
+ let value = ["f": filter]
let template = try! Template(string:"{{f(a,b).foo}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "bar")
}
func testVariadicFilterCanBeUsedForObjectSections() {
- let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
- return Box(["foo": "bar"])
+ let filter = VariadicFilter({ (boxes: [MustacheBox]) -> Any? in
+ return ["foo": "bar"]
})
- let box = Box(["f": Box(filter)])
+ let value = ["f": filter]
let template = try! Template(string:"{{#f(a,b)}}{{foo}}{{/}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "bar")
}
func testVariadicFilterCanBeUsedForEnumerableSections() {
- let filter = VariadicFilter({ (boxes: [MustacheBox]) -> MustacheBox in
- return Box(boxes)
+ let filter = VariadicFilter({ (boxes: [MustacheBox]) -> Any? in
+ return boxes
})
- let box = Box([
- "a": Box("a"),
- "b": Box("b"),
- "c": Box("c"),
- "f": Box(filter)])
+ let value: [String: Any] = [
+ "a": "a",
+ "b": "b",
+ "c": "c",
+ "f": filter]
let template = try! Template(string:"{{#f(a,b)}}{{.}}{{/}} {{#f(a,b,c)}}{{.}}{{/}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "ab abc")
}
func testVariadicFilterCanBeUsedForBooleanSections() {
- let filter = VariadicFilter { (boxes) -> MustacheBox in
+ let filter = VariadicFilter { (boxes) -> Any? in
return boxes.first!
}
- let box = Box([
- "yes": Box(true),
- "no": Box(false),
- "f": Box(filter)])
+ let value: [String: Any] = [
+ "yes": true,
+ "no": false,
+ "f": filter]
let template = try! Template(string:"{{#f(yes)}}YES{{/}} {{^f(no)}}NO{{/}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "YES NO")
}
func testImplicitIteratorCanBeVariadicFilterArgument() {
- let box = Box([
- "f": Box(VariadicFilter { (boxes) -> MustacheBox in
+ let value: [String: Any] = [
+ "f": VariadicFilter { (boxes) -> Any? in
var result = ""
for box in boxes {
if let dictionary = box.dictionaryValue {
result += String(dictionary.count)
}
}
- return Box(result)
- }),
- "foo": Box(["a": "a", "b": "b", "c": "c"])
- ])
+ return result
+ },
+ "foo": ["a": "a", "b": "b", "c": "c"]
+ ]
let template = try! Template(string:"{{f(foo,.)}} {{f(.,foo)}}")
- let rendering = try! template.render(box)
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "32 23")
}
}
diff --git a/Tests/Public/FoundationCollectionTests.swift b/Tests/Public/FoundationCollectionTests.swift
index f5c02338..702378ae 100644
--- a/Tests/Public/FoundationCollectionTests.swift
+++ b/Tests/Public/FoundationCollectionTests.swift
@@ -27,45 +27,27 @@ import Mustache;
// TODO: write an equivalent test class for Swift collections
class FoundationCollectionTests: XCTestCase {
- var boxedArray: MustacheBox!
- var boxedNSArray: MustacheBox!
- var boxedSet: MustacheBox!
- var boxedNSSet: MustacheBox!
- var boxedNSOrderedSet: MustacheBox!
+ var array: [Any?]!
+ var nsArray: NSArray!
+ var set: Set!
+ var nsSet: NSSet!
+ var nsOrderedSet: NSOrderedSet!
override func setUp() {
- boxedArray = Box(["collection": [["key": "value"] as NSDictionary]])
- boxedNSArray = {
- let array = NSMutableArray()
- array.add(["key": "value"])
- let data = NSMutableDictionary()
- data.setObject(array, forKey: "collection" as NSString)
- return Box(data)
- }()
- boxedSet = Box(["collection": Set([["key": "value"] as NSDictionary])])
- boxedNSSet = {
- let set = NSMutableSet()
- set.add(["key": "value"])
- let data = NSMutableDictionary()
- data.setObject(set, forKey: "collection" as NSString)
- return Box(data)
- }()
- boxedNSOrderedSet = {
- let orderedSet = NSMutableOrderedSet()
- orderedSet.add(["key": "value"])
- let data = NSMutableDictionary()
- data.setObject(orderedSet, forKey: "collection" as NSString)
- return Box(data)
- }()
+ array = [["key": "value"]]
+ nsArray = [["key": "value"]]
+ set = Set([["key": "value"] as NSDictionary])
+ nsSet = NSSet(array: [["key": "value"]])
+ nsOrderedSet = NSOrderedSet(array: [["key": "value"]])
}
func testNSArrayIsIterated() {
- let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(boxedNSArray)
+ let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(["collection": nsArray])
XCTAssertEqual(rendering, "value")
}
func testNSArrayIsNotIteratedWithValueForKey() {
- let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(boxedNSArray)
+ let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(["collection": nsArray])
XCTAssertEqual(rendering, "")
}
@@ -79,32 +61,32 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":NSArray()])), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":NSArray(object: "foo")])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection":NSArray()]), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection":NSArray(object: "foo")]), "Not empty")
}
func testNSArrayCountKey() {
- let rendering = try! Template(string: "{{collection.count}}").render(boxedNSArray)
+ let rendering = try! Template(string: "{{collection.count}}").render(["collection": nsArray])
XCTAssertEqual(rendering, "1")
}
func testNSArrayKeyFirst() {
- let rendering = try! Template(string: "{{collection.first.key}}").render(boxedNSArray)
+ let rendering = try! Template(string: "{{collection.first.key}}").render(["collection": nsArray])
XCTAssertEqual(rendering, "value")
}
func testNSArrayLastKey() {
- let rendering = try! Template(string: "{{collection.last.key}}").render(boxedNSArray)
+ let rendering = try! Template(string: "{{collection.last.key}}").render(["collection": nsArray])
XCTAssertEqual(rendering, "value")
}
func testArrayIsIterated() {
- let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(boxedArray)
+ let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(["collection": array])
XCTAssertEqual(rendering, "value")
}
func testArrayIsNotIteratedWithValueForKey() {
- let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(boxedArray)
+ let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(["collection": array])
XCTAssertEqual(rendering, "")
}
@@ -118,32 +100,32 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box([] as [String])])), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(["foo"])])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection": [] as [String]]), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection": ["foo"]]), "Not empty")
}
func testArrayCountKey() {
- let rendering = try! Template(string: "{{collection.count}}").render(boxedArray)
+ let rendering = try! Template(string: "{{collection.count}}").render(["collection": array])
XCTAssertEqual(rendering, "1")
}
func testArrayKeyFirst() {
- let rendering = try! Template(string: "{{collection.first.key}}").render(boxedArray)
+ let rendering = try! Template(string: "{{collection.first.key}}").render(["collection": array])
XCTAssertEqual(rendering, "value")
}
func testArrayLastKey() {
- let rendering = try! Template(string: "{{collection.last.key}}").render(boxedArray)
+ let rendering = try! Template(string: "{{collection.last.key}}").render(["collection": array])
XCTAssertEqual(rendering, "value")
}
func testNSSetIsIterated() {
- let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(boxedNSSet)
+ let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(["collection": nsSet])
XCTAssertEqual(rendering, "value")
}
func testNSSetIsNotIteratedWithValueForKey() {
- let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(boxedNSSet)
+ let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(["collection": nsSet])
XCTAssertEqual(rendering, "")
}
@@ -157,33 +139,33 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":NSSet()])), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":NSSet(object: "foo")])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection":NSSet()]), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection":NSSet(object: "foo")]), "Not empty")
}
func testNSSetCountKey() {
- let rendering = try! Template(string: "{{collection.count}}").render(boxedNSSet)
+ let rendering = try! Template(string: "{{collection.count}}").render(["collection": nsSet])
XCTAssertEqual(rendering, "1")
}
func testNSSetFirstKey() {
- let rendering = try! Template(string: "{{collection.first.key}}").render(boxedNSSet)
+ let rendering = try! Template(string: "{{collection.first.key}}").render(["collection": nsSet])
XCTAssertEqual(rendering, "value")
}
func testNSSetLastKey() {
// There is no such thing as set.last
- let rendering = try! Template(string: "{{collection.last.key}}").render(boxedNSSet)
+ let rendering = try! Template(string: "{{collection.last.key}}").render(["collection": nsSet])
XCTAssertEqual(rendering, "")
}
func testSetIsIterated() {
- let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(boxedSet)
+ let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(["collection": set])
XCTAssertEqual(rendering, "value")
}
func testSetIsNotIteratedWithValueForKey() {
- let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(boxedSet)
+ let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(["collection": set])
XCTAssertEqual(rendering, "")
}
@@ -197,48 +179,48 @@ class FoundationCollectionTests: XCTestCase {
// recommended technique.
let templateString = "{{#collection.isEmpty}}Empty{{/}}{{^collection.isEmpty}}Not empty{{/}}"
XCTAssertEqual(try! Template(string: templateString).render(), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(Set())])), "Not empty")
- XCTAssertEqual(try! Template(string: templateString).render(Box(["collection":Box(Set(["foo"]))])), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection": Set()]), "Not empty")
+ XCTAssertEqual(try! Template(string: templateString).render(["collection": Set(["foo"])]), "Not empty")
}
func testSetCountKey() {
- let rendering = try! Template(string: "{{collection.count}}").render(boxedSet)
+ let rendering = try! Template(string: "{{collection.count}}").render(["collection": set])
XCTAssertEqual(rendering, "1")
}
func testSetFirstKey() {
- let rendering = try! Template(string: "{{collection.first.key}}").render(boxedSet)
+ let rendering = try! Template(string: "{{collection.first.key}}").render(["collection": set])
XCTAssertEqual(rendering, "value")
}
func testSetLastKey() {
// There is no such thing as set.last
- let rendering = try! Template(string: "{{collection.last.key}}").render(boxedSet)
+ let rendering = try! Template(string: "{{collection.last.key}}").render(["collection": set])
XCTAssertEqual(rendering, "")
}
func testNSOrderedSetIsIterated() {
- let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(boxedNSOrderedSet)
+ let rendering = try! Template(string: "{{#collection}}{{key}}{{/collection}}").render(["collection": nsOrderedSet])
XCTAssertEqual(rendering, "value")
}
func testNSOrderedSetIsNotIteratedWithValueForKey() {
- let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(boxedNSOrderedSet)
+ let rendering = try! Template(string: "{{#collection.key}}{{.}}{{/collection.key}}").render(["collection": nsOrderedSet])
XCTAssertEqual(rendering, "")
}
func testNSOrderedSetCountKey() {
- let rendering = try! Template(string: "{{collection.count}}").render(boxedNSOrderedSet)
+ let rendering = try! Template(string: "{{collection.count}}").render(["collection": nsOrderedSet])
XCTAssertEqual(rendering, "1")
}
func testNSOrderedSetKeyFirst() {
- let rendering = try! Template(string: "{{collection.first.key}}").render(boxedNSOrderedSet)
+ let rendering = try! Template(string: "{{collection.first.key}}").render(["collection": nsOrderedSet])
XCTAssertEqual(rendering, "value")
}
func testNSOrderedSetLastKey() {
- let rendering = try! Template(string: "{{collection.last.key}}").render(boxedNSOrderedSet)
+ let rendering = try! Template(string: "{{collection.last.key}}").render(["collection": nsOrderedSet])
XCTAssertEqual(rendering, "value")
}
diff --git a/Tests/Public/HookFunctionTests.swift b/Tests/Public/HookFunctionTests.swift
index 226ee0b7..b338c28b 100644
--- a/Tests/Public/HookFunctionTests.swift
+++ b/Tests/Public/HookFunctionTests.swift
@@ -32,12 +32,12 @@ class HookFunctionTests: XCTestCase {
func testWillRenderFunctionIsNotTriggeredByText() {
var success = true
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
success = false
return box
}
let template = try! Template(string: "---")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
let rendering = try! template.render()
XCTAssertEqual(rendering, "---")
XCTAssertTrue(success)
@@ -50,7 +50,7 @@ class HookFunctionTests: XCTestCase {
}
let template = try! Template(string: "---")
- template.baseContext = template.baseContext.extendedContext(Box(didRender))
+ template.baseContext = template.baseContext.extendedContext(didRender)
let rendering = try! template.render()
XCTAssertEqual(rendering, "---")
XCTAssertTrue(success)
@@ -61,10 +61,10 @@ class HookFunctionTests: XCTestCase {
var preRenderingTagType: TagType?
var postRenderingValue: MustacheBox?
var postRenderingTagType: TagType?
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
preRenderingValue = box
preRenderingTagType = tag.type
- return Box(1)
+ return 1
}
let didRender = { (tag: Tag, box: MustacheBox, string: String?) -> Void in
postRenderingValue = box
@@ -73,7 +73,7 @@ class HookFunctionTests: XCTestCase {
let template = try! Template(string: "---{{foo}}---")
template.baseContext = template.baseContext.extendedContext(MustacheBox(willRender: willRender, didRender: didRender))
- let rendering = try! template.render(Box(["foo": "value"]))
+ let rendering = try! template.render(["foo": "value"])
XCTAssertEqual(rendering, "---1---")
XCTAssertEqual(preRenderingTagType!, TagType.variable)
@@ -85,7 +85,7 @@ class HookFunctionTests: XCTestCase {
func testSectionHooks() {
var preRenderingTagType: TagType?
var postRenderingTagType: TagType?
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
preRenderingTagType = tag.type
return box
}
@@ -107,13 +107,13 @@ class HookFunctionTests: XCTestCase {
var preRenderingTagTypes: [TagType] = []
var postRenderingValues: [MustacheBox] = []
var postRenderingTagTypes: [TagType] = []
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
preRenderingValues.append(box)
preRenderingTagTypes.append(tag.type)
if preRenderingValues.count == 1 {
- return Box(true)
+ return true
} else {
- return Box("observer")
+ return "observer"
}
}
let didRender = { (tag: Tag, box: MustacheBox, string: String?) -> Void in
@@ -141,17 +141,17 @@ class HookFunctionTests: XCTestCase {
func testObserverInterpretsRenderedValue() {
var willRenderCount = 0;
var renderedValue: MustacheBox? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
willRenderCount += 1
renderedValue = box
return box
}
- let filter = { (string: String?) -> MustacheBox in
- return Box(string?.uppercased())
+ let filter = { (string: String?) -> Any? in
+ return string?.uppercased()
}
var template = try! Template(string: "{{subject}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
var rendering = try! template.render()
@@ -160,16 +160,16 @@ class HookFunctionTests: XCTestCase {
XCTAssertTrue(renderedValue!.isEmpty)
template = try! Template(string: "{{subject}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": "foo"]))
+ rendering = try! template.render(["subject": "foo"])
XCTAssertEqual(rendering, "foo")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! String), "foo")
template = try! Template(string: "{{subject.foo}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
rendering = try! template.render()
@@ -178,46 +178,46 @@ class HookFunctionTests: XCTestCase {
XCTAssertTrue(renderedValue!.isEmpty)
template = try! Template(string: "{{subject.foo}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": "foo"]))
+ rendering = try! template.render(["subject": "foo"])
XCTAssertEqual(rendering, "")
XCTAssertEqual(willRenderCount, 1)
XCTAssertTrue(renderedValue!.isEmpty)
template = try! Template(string: "{{subject.foo}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": ["foo": "bar"]] as NSDictionary))
+ rendering = try! template.render(["subject": ["foo": "bar"]])
XCTAssertEqual(rendering, "bar")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! String), "bar")
template = try! Template(string: "{{filter(subject)}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["filter": Filter(filter)]))
+ rendering = try! template.render(["filter": Filter(filter)])
XCTAssertEqual(rendering, "")
XCTAssertEqual(willRenderCount, 1)
XCTAssertTrue(renderedValue!.isEmpty)
template = try! Template(string: "{{filter(subject)}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": "foo", "filter": Filter(filter)]))
+ rendering = try! template.render(["subject": "foo", "filter": Filter(filter)])
XCTAssertEqual(rendering, "FOO")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! String), "FOO")
template = try! Template(string: "{{filter(subject).length}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
willRenderCount = 0
renderedValue = nil
- rendering = try! template.render(Box(["subject": "foo", "filter": Filter(filter)]))
+ rendering = try! template.render(["subject": "foo", "filter": Filter(filter)])
XCTAssertEqual(rendering, "3")
XCTAssertEqual(willRenderCount, 1)
XCTAssertEqual((renderedValue!.value as! Int), 3)
@@ -228,17 +228,17 @@ class HookFunctionTests: XCTestCase {
let didRender = { (tag: Tag, box: MustacheBox, string: String?) in
recordedRendering = string
}
- let box = Box(["value": "<>"])
+ let value = ["value": "<>"]
var template = try! Template(string: "-{{value}}-")
- template.baseContext = template.baseContext.extendedContext(Box(didRender))
- var rendering = try! template.render(box)
+ template.baseContext = template.baseContext.extendedContext(didRender)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "-<>-")
XCTAssertEqual(recordedRendering!, "<>")
template = try! Template(string: "-{{{value}}}-")
- template.baseContext = template.baseContext.extendedContext(Box(didRender))
- rendering = try! template.render(box)
+ template.baseContext = template.baseContext.extendedContext(didRender)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "-<>-")
XCTAssertEqual(recordedRendering!, "<>")
}
@@ -250,12 +250,12 @@ class HookFunctionTests: XCTestCase {
}
let template = try! Template(string: "-{{.}}-")
- template.baseContext = template.baseContext.extendedContext(Box(didRender))
+ template.baseContext = template.baseContext.extendedContext(didRender)
failedRendering = false
do {
- _ = try template.render(Box({ (info: RenderingInfo) -> Rendering in
+ _ = try template.render({ (info: RenderingInfo) -> Rendering in
throw NSError(domain: "TagObserverError", code: 1, userInfo: nil)
- }))
+ })
XCTAssert(false)
} catch let error as NSError {
XCTAssertEqual(error.domain, "TagObserverError")
@@ -271,12 +271,12 @@ class HookFunctionTests: XCTestCase {
}
let template = try! Template(string: "-\n\n{{.}}-")
- template.baseContext = template.baseContext.extendedContext(Box(didRender))
+ template.baseContext = template.baseContext.extendedContext(didRender)
failedRendering = false
do {
- _ = try template.render(Box({ (info: RenderingInfo) -> Rendering in
+ _ = try template.render({ (info: RenderingInfo) -> Rendering in
throw CustomError.error
- }))
+ })
XCTAssert(false)
} catch CustomError.error {
XCTAssert(true)
@@ -292,7 +292,7 @@ class HookFunctionTests: XCTestCase {
var willRenderIndex1 = 0
var didRenderIndex1 = 0
- let willRender1 = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender1 = { (tag: Tag, box: MustacheBox) -> Any? in
if box.value as? String == "observed" {
willRenderIndex1 = willRenderIndex
willRenderIndex += 1
@@ -308,7 +308,7 @@ class HookFunctionTests: XCTestCase {
var willRenderIndex2 = 0
var didRenderIndex2 = 0
- let willRender2 = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender2 = { (tag: Tag, box: MustacheBox) -> Any? in
if box.value as? String == "observed" {
willRenderIndex2 = willRenderIndex
willRenderIndex += 1
@@ -324,7 +324,7 @@ class HookFunctionTests: XCTestCase {
var willRenderIndex3 = 0
var didRenderIndex3 = 0
- let willRender3 = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender3 = { (tag: Tag, box: MustacheBox) -> Any? in
if box.value as? String == "observed" {
willRenderIndex3 = willRenderIndex
willRenderIndex += 1
@@ -340,11 +340,11 @@ class HookFunctionTests: XCTestCase {
let template = try! Template(string: "{{#observer2}}{{#observer3}}{{observed}}{{/}}{{/}}")
template.baseContext = template.baseContext.extendedContext(MustacheBox(willRender: willRender1, didRender: didRender1))
- let box = Box([
+ let box: [String: Any] = [
"observer2": MustacheBox(willRender: willRender2, didRender: didRender2),
"observer3": MustacheBox(willRender: willRender3, didRender: didRender3),
"observed": "observed"
- ])
+ ]
_ = try! template.render(box)
XCTAssertEqual(willRenderIndex1, 2)
@@ -358,47 +358,47 @@ class HookFunctionTests: XCTestCase {
func testArrayOfWillRenderFunctionsInSectionTag() {
var willRenderCalled1 = false
- let willRender1 = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender1 = { (tag: Tag, box: MustacheBox) -> Any? in
willRenderCalled1 = true
return box
}
var willRenderCalled2 = false
- let willRender2 = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender2 = { (tag: Tag, box: MustacheBox) -> Any? in
willRenderCalled2 = true
return box
}
let template = try! Template(string: "{{#items}}{{.}}{{/items}}")
- let box = Box(["items": [willRender1, willRender2]])
- _ = try! template.render(box)
+ let value = ["items": [willRender1, willRender2]]
+ _ = try! template.render(value)
XCTAssertTrue(willRenderCalled1)
XCTAssertTrue(willRenderCalled2)
}
func testWillRenderFunctionCanProcessRenderFunction() {
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
- return Box({ (info) -> Rendering in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
+ return { (info: RenderingInfo) -> Rendering in
let rendering = try box.render(info)
return Rendering(rendering.string.uppercased(), rendering.contentType)
- })
+ }
}
var render = { (info: RenderingInfo) -> Rendering in
return Rendering("&you")
}
- var box = Box(["object": render, "observer": willRender])
+ var value: [String: Any] = ["object": render, "observer": willRender]
var template = try! Template(string: "{{# observer }}{{ object }}{{/ }}")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "&YOU")
render = { (info: RenderingInfo) -> Rendering in
return Rendering("&you", .html)
}
- box = Box(["object": render, "observer": willRender])
+ value = ["object": render, "observer": willRender]
template = try! Template(string: "{{# observer }}{{ object }}{{/ }}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "&YOU")
}
}
diff --git a/Tests/Public/KeyedSubscriptFunctionTests.swift b/Tests/Public/KeyedSubscriptFunctionTests.swift
index 1bdd0c79..eb2a1235 100644
--- a/Tests/Public/KeyedSubscriptFunctionTests.swift
+++ b/Tests/Public/KeyedSubscriptFunctionTests.swift
@@ -27,11 +27,11 @@ import Mustache
class KeyedSubscriptFunctionTests: XCTestCase {
func makeKeyedSubscriptFunction() -> KeyedSubscriptFunction {
- return { (key: String) -> MustacheBox in
+ return { (key: String) -> Any? in
if key == "self" {
return MustacheBox(keyedSubscript: self.makeKeyedSubscriptFunction())
} else {
- return Box(key)
+ return key
}
}
}
@@ -41,4 +41,4 @@ class KeyedSubscriptFunctionTests: XCTestCase {
let rendering = try! template.render(MustacheBox(keyedSubscript: makeKeyedSubscriptFunction()))
XCTAssertEqual(rendering, "a,b,c")
}
-}
\ No newline at end of file
+}
diff --git a/Tests/Public/LambdaTests.swift b/Tests/Public/LambdaTests.swift
index 2e29c87c..deef28de 100644
--- a/Tests/Public/LambdaTests.swift
+++ b/Tests/Public/LambdaTests.swift
@@ -33,7 +33,7 @@ class LambdaTests: XCTestCase {
let data = [
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "Hello, world!")
}
@@ -45,7 +45,7 @@ class LambdaTests: XCTestCase {
"planet": "world",
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "Hello, world!")
}
@@ -59,7 +59,7 @@ class LambdaTests: XCTestCase {
"planet": "world",
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "Hello, (|planet| => world)!")
}
@@ -71,7 +71,7 @@ class LambdaTests: XCTestCase {
let data = [
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "1 == 2 == 3")
}
@@ -82,7 +82,7 @@ class LambdaTests: XCTestCase {
let data = [
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "<>>")
}
@@ -99,7 +99,7 @@ class LambdaTests: XCTestCase {
let data = [
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "")
}
@@ -113,7 +113,7 @@ class LambdaTests: XCTestCase {
"planet": "Earth",
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "<-Earth->")
}
@@ -127,7 +127,7 @@ class LambdaTests: XCTestCase {
"planet": "Earth",
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "<-{{planet}} => Earth->")
}
@@ -140,7 +140,7 @@ class LambdaTests: XCTestCase {
let data = [
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "__FILE__ != __LINE__")
}
@@ -153,7 +153,7 @@ class LambdaTests: XCTestCase {
let data = [
"lambda": lambda,
]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "<>")
}
@@ -167,7 +167,7 @@ class LambdaTests: XCTestCase {
"lambda": lambda,
]
do {
- _ = try template.render(Box(data))
+ _ = try template.render(data)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
@@ -186,7 +186,7 @@ class LambdaTests: XCTestCase {
"lambda": lambda,
]
do {
- _ = try template.render(Box(data))
+ _ = try template.render(data)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.templateNotFound)
@@ -198,14 +198,14 @@ class LambdaTests: XCTestCase {
func testArity0LambdaInSectionTag() {
let lambda = Lambda { "success" }
let template = try! Template(string: "{{#lambda}}<{{.}}>{{/lambda}}")
- let rendering = try! template.render(Box(["lambda": lambda]))
+ let rendering = try! template.render(["lambda": lambda])
XCTAssertEqual(rendering, "")
}
func testArity1LambdaInVariableTag() {
let lambda = Lambda { (string) in string }
let template = try! Template(string: "<{{lambda}}>")
- let rendering = try! template.render(Box(["lambda": lambda]))
+ let rendering = try! template.render(["lambda": lambda])
XCTAssertEqual(rendering, "<(Lambda)>")
}
}
diff --git a/Tests/Public/MustacheBoxTests.swift b/Tests/Public/MustacheBoxTests.swift
index 3e4e04d4..35efab4d 100644
--- a/Tests/Public/MustacheBoxTests.swift
+++ b/Tests/Public/MustacheBoxTests.swift
@@ -37,7 +37,7 @@ class MustacheBoxTests: XCTestCase {
// This test should go elsewhere
func testBoolBoxing() {
let template = try! Template(string:"{{.}}:{{#.}}true{{/.}}{{^.}}false{{/.}}")
- XCTAssertEqual(try! template.render(Box(true)), "1:true")
- XCTAssertEqual(try! template.render(Box(false)), "0:false")
+ XCTAssertEqual(try! template.render(true), "1:true")
+ XCTAssertEqual(try! template.render(false), "0:false")
}
}
diff --git a/Tests/Public/ObjcKeyAccessTests.swift b/Tests/Public/ObjcKeyAccessTests.swift
index b9b8fff6..c7f2349f 100644
--- a/Tests/Public/ObjcKeyAccessTests.swift
+++ b/Tests/Public/ObjcKeyAccessTests.swift
@@ -39,7 +39,7 @@ class ObjcKeyAccessTests: XCTestCase {
XCTAssertEqual((object.value(forKey: "property") as! String), "property")
// test context
- let context = Context(Box(object))
+ let context = Context(object)
XCTAssertEqual((context.mustacheBoxForKey("property").value as! String), "property")
}
@@ -51,7 +51,7 @@ class ObjcKeyAccessTests: XCTestCase {
XCTAssertEqual((object.value(forKey: "method") as! String), "method")
// test context
- let context = Context(Box(object))
+ let context = Context(object)
XCTAssertTrue(context.mustacheBoxForKey("method").value == nil)
}
}
diff --git a/Tests/Public/RenderFunctionTests.swift b/Tests/Public/RenderFunctionTests.swift
index 6dc34b66..a4417007 100644
--- a/Tests/Public/RenderFunctionTests.swift
+++ b/Tests/Public/RenderFunctionTests.swift
@@ -34,7 +34,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("---")
}
- let rendering = try! Template(string: "{{.}}").render(Box(render))
+ let rendering = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(rendering, "---")
}
@@ -42,7 +42,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("---")
}
- let rendering = try! Template(string: "{{#.}}{{/.}}").render(Box(render))
+ let rendering = try! Template(string: "{{#.}}{{/.}}").render(render)
XCTAssertEqual(rendering, "---")
}
@@ -50,7 +50,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("---")
}
- let rendering = try! Template(string: "{{^.}}{{/.}}").render(Box(render))
+ let rendering = try! Template(string: "{{^.}}{{/.}}").render(render)
XCTAssertEqual(rendering, "")
}
@@ -58,7 +58,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&", .html)
}
- let rendering = try! Template(string: "{{.}}").render(Box(render))
+ let rendering = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(rendering, "&")
}
@@ -66,7 +66,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&", .html)
}
- let rendering = try! Template(string: "{{{.}}}").render(Box(render))
+ let rendering = try! Template(string: "{{{.}}}").render(render)
XCTAssertEqual(rendering, "&")
}
@@ -74,7 +74,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&")
}
- let rendering = try! Template(string: "{{.}}").render(Box(render))
+ let rendering = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(rendering, "&")
}
@@ -82,7 +82,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&")
}
- let rendering = try! Template(string: "{{{.}}}").render(Box(render))
+ let rendering = try! Template(string: "{{{.}}}").render(render)
XCTAssertEqual(rendering, "&")
}
@@ -90,7 +90,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&", .html)
}
- let rendering = try! Template(string: "{{#.}}{{/.}}").render(Box(render))
+ let rendering = try! Template(string: "{{#.}}{{/.}}").render(render)
XCTAssertEqual(rendering, "&")
}
@@ -98,7 +98,7 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&")
}
- let rendering = try! Template(string: "{{#.}}{{/.}}").render(Box(render))
+ let rendering = try! Template(string: "{{#.}}{{/.}}").render(render)
XCTAssertEqual(rendering, "&")
}
@@ -108,7 +108,7 @@ class RenderFunctionTests: XCTestCase {
throw NSError(domain: errorDomain, code: 0, userInfo: nil)
}
do {
- _ = try Template(string: "{{.}}").render(Box(render))
+ _ = try Template(string: "{{.}}").render(render)
XCTAssert(false)
} catch let error as NSError {
XCTAssertEqual(error.domain, errorDomain)
@@ -120,7 +120,7 @@ class RenderFunctionTests: XCTestCase {
throw CustomError.error
}
do {
- _ = try Template(string: "\n\n{{.}}").render(Box(render))
+ _ = try Template(string: "\n\n{{.}}").render(render)
XCTAssert(false)
} catch CustomError.error {
XCTAssert(true)
@@ -135,7 +135,7 @@ class RenderFunctionTests: XCTestCase {
throw NSError(domain: errorDomain, code: 0, userInfo: nil)
}
do {
- _ = try Template(string: "{{#.}}{{/.}}").render(Box(render))
+ _ = try Template(string: "{{#.}}{{/.}}").render(render)
XCTAssert(false)
} catch let error as NSError {
XCTAssertEqual(error.domain, errorDomain)
@@ -147,7 +147,7 @@ class RenderFunctionTests: XCTestCase {
throw CustomError.error
}
do {
- _ = try Template(string: "\n\n{{#.}}\n\n{{/.}}").render(Box(render))
+ _ = try Template(string: "\n\n{{#.}}\n\n{{/.}}").render(render)
XCTAssert(false)
} catch CustomError.error {
XCTAssert(true)
@@ -167,7 +167,7 @@ class RenderFunctionTests: XCTestCase {
}
return Rendering("")
}
- _ = try! Template(string: "{{.}}").render(Box(render))
+ _ = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(variableTagDetections, 1)
}
@@ -182,7 +182,7 @@ class RenderFunctionTests: XCTestCase {
}
return Rendering("")
}
- _ = try! Template(string: "{{#.}}{{/.}}").render(Box(render))
+ _ = try! Template(string: "{{#.}}{{/.}}").render(render)
XCTAssertEqual(sectionTagDetections, 1)
}
@@ -192,7 +192,7 @@ class RenderFunctionTests: XCTestCase {
innerTemplateString = info.tag.innerTemplateString
return Rendering("")
}
- _ = try! Template(string: "{{#.}}{{subject}}{{/.}}").render(Box(render))
+ _ = try! Template(string: "{{#.}}{{subject}}{{/.}}").render(render)
XCTAssertEqual(innerTemplateString!, "{{subject}}")
}
@@ -202,7 +202,7 @@ class RenderFunctionTests: XCTestCase {
innerTemplateString = info.tag.innerTemplateString
return Rendering("")
}
- _ = try! Template(string: "{{.}}").render(Box(render))
+ _ = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(innerTemplateString!, "")
}
@@ -213,8 +213,8 @@ class RenderFunctionTests: XCTestCase {
return tagRendering!
}
- let box = Box(["render": render, "subject": "-"])
- _ = try! Template(string: "{{#render}}{{subject}}={{subject}}{{/render}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "-"]
+ _ = try! Template(string: "{{#render}}{{subject}}={{subject}}{{/render}}").render(value)
XCTAssertEqual(tagRendering!.string, "-=-")
XCTAssertEqual(tagRendering!.contentType, ContentType.html)
}
@@ -226,7 +226,7 @@ class RenderFunctionTests: XCTestCase {
return tagRendering!
}
- _ = try! Template(string: "{{.}}").render(Box(render))
+ _ = try! Template(string: "{{.}}").render(render)
XCTAssertEqual(tagRendering!.string, "")
XCTAssertEqual(tagRendering!.contentType, ContentType.html)
}
@@ -238,7 +238,7 @@ class RenderFunctionTests: XCTestCase {
return tagRendering!
}
- _ = try! Template(string: "{{{.}}}").render(Box(render))
+ _ = try! Template(string: "{{{.}}}").render(render)
XCTAssertEqual(tagRendering!.string, "")
XCTAssertEqual(tagRendering!.contentType, ContentType.html)
@@ -249,8 +249,8 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return try altTemplate.render(info.context)
}
- let box = Box(["render": render, "subject": "-"])
- let rendering = try! Template(string: "{{render}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "-"]
+ let rendering = try! Template(string: "{{render}}").render(value)
XCTAssertEqual(rendering, "-")
}
@@ -259,67 +259,67 @@ class RenderFunctionTests: XCTestCase {
let render = { (info: RenderingInfo) -> Rendering in
return try altTemplate.render(info.context)
}
- let box = Box(["render": render, "subject": "-"])
- let rendering = try! Template(string: "{{#render}}{{/render}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "-"]
+ let rendering = try! Template(string: "{{#render}}{{/render}}").render(value)
XCTAssertEqual(rendering, "-")
}
func testRenderFunctionDoesNotAutomaticallyEntersVariableContextStack() {
- let keyedSubscript = { (key: String) -> MustacheBox in
- return Box("value")
+ let keyedSubscript = { (key: String) -> Any? in
+ return "value"
}
let render = { (info: RenderingInfo) -> Rendering in
return try Template(string:"key:{{key}}").render(info.context)
}
- let box = Box(["render": MustacheBox(keyedSubscript: keyedSubscript, render: render)])
- let rendering = try! Template(string: "{{render}}").render(box)
+ let value: [String: Any] = ["render": MustacheBox(keyedSubscript: keyedSubscript, render: render)]
+ let rendering = try! Template(string: "{{render}}").render(value)
XCTAssertEqual(rendering, "key:")
}
func testRenderFunctionDoesNotAutomaticallyEntersSectionContextStack() {
- let keyedSubscript = { (key: String) -> MustacheBox in
- return Box("value")
+ let keyedSubscript = { (key: String) -> Any? in
+ return "value"
}
let render = { (info: RenderingInfo) -> Rendering in
return try info.tag.render(info.context)
}
- let box = Box(["render": MustacheBox(keyedSubscript: keyedSubscript, render: render)])
- let rendering = try! Template(string: "{{#render}}key:{{key}}{{/render}}").render(box)
+ let value: [String: Any] = ["render": MustacheBox(keyedSubscript: keyedSubscript, render: render)]
+ let rendering = try! Template(string: "{{#render}}key:{{key}}{{/render}}").render(value)
XCTAssertEqual(rendering, "key:")
}
func testRenderFunctionCanExtendValueContextStackInVariableTag() {
let render = { (info: RenderingInfo) -> Rendering in
- let context = info.context.extendedContext(Box(["subject2": "+++"]))
+ let context = info.context.extendedContext(["subject2": "+++"])
let template = try! Template(string: "{{subject}}{{subject2}}")
return try template.render(context)
}
- let box = Box(["render": render, "subject": "---"])
- let rendering = try! Template(string: "{{render}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "---"]
+ let rendering = try! Template(string: "{{render}}").render(value)
XCTAssertEqual(rendering, "---+++")
}
func testRenderFunctionCanExtendValueContextStackInSectionTag() {
let render = { (info: RenderingInfo) -> Rendering in
- return try info.tag.render(info.context.extendedContext(Box(["subject2": "+++"])))
+ return try info.tag.render(info.context.extendedContext(["subject2": "+++"]))
}
- let box = Box(["render": render, "subject": "---"])
- let rendering = try! Template(string: "{{#render}}{{subject}}{{subject2}}{{/render}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "---"]
+ let rendering = try! Template(string: "{{#render}}{{subject}}{{subject2}}{{/render}}").render(value)
XCTAssertEqual(rendering, "---+++")
}
func testRenderFunctionCanExtendWillRenderStackInVariableTag() {
var tagWillRenderCount = 0
let render = { (info: RenderingInfo) -> Rendering in
- let context = info.context.extendedContext(Box({ (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let context = info.context.extendedContext({ (tag: Tag, box: MustacheBox) -> Any? in
tagWillRenderCount += 1
return box
- }))
+ })
let template = try! Template(string: "{{subject}}{{subject}}")
return try template.render(context)
}
- let box = Box(["render": render, "subject": "-"])
- let rendering = try! Template(string: "{{subject}}{{render}}{{subject}}{{subject}}{{subject}}{{subject}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "-"]
+ let rendering = try! Template(string: "{{subject}}{{render}}{{subject}}{{subject}}{{subject}}{{subject}}").render(value)
XCTAssertEqual(rendering, "-------")
XCTAssertEqual(tagWillRenderCount, 2)
}
@@ -327,24 +327,24 @@ class RenderFunctionTests: XCTestCase {
func testRenderFunctionCanExtendWillRenderStackInSectionTag() {
var tagWillRenderCount = 0
let render = { (info: RenderingInfo) -> Rendering in
- return try info.tag.render(info.context.extendedContext(Box({ (tag: Tag, box: MustacheBox) -> MustacheBox in
+ return try info.tag.render(info.context.extendedContext({ (tag: Tag, box: MustacheBox) -> Any? in
tagWillRenderCount += 1
return box
- })))
+ }))
}
- let box = Box(["render": render, "subject": "-"])
- let rendering = try! Template(string: "{{subject}}{{#render}}{{subject}}{{subject}}{{/render}}{{subject}}{{subject}}{{subject}}{{subject}}").render(box)
+ let value: [String: Any] = ["render": render, "subject": "-"]
+ let rendering = try! Template(string: "{{subject}}{{#render}}{{subject}}{{subject}}{{/render}}{{subject}}{{subject}}{{subject}}{{subject}}").render(value)
XCTAssertEqual(rendering, "-------")
XCTAssertEqual(tagWillRenderCount, 2)
}
func testRenderFunctionTriggersWillRenderFunctions() {
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
switch tag.type {
case .section:
return box
default:
- return Box("delegate")
+ return "delegate"
}
}
@@ -353,19 +353,19 @@ class RenderFunctionTests: XCTestCase {
}
let template = try! Template(string: "{{#render}}{{subject}}{{/render}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
- let box = Box(["render": render, "subject": "---"])
- let rendering = try! template.render(box)
+ template.baseContext = template.baseContext.extendedContext(willRender)
+ let value: [String: Any] = ["render": render, "subject": "---"]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "delegate")
}
func testRenderFunctionTriggersWillRenderFunctionsInAnotherTemplateFromVariableTag() {
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
switch tag.type {
case .section:
return box
default:
- return Box("delegate")
+ return "delegate"
}
}
@@ -375,19 +375,19 @@ class RenderFunctionTests: XCTestCase {
}
let template = try! Template(string: "{{render}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
- let box = Box(["render": render, "subject": "---"])
- let rendering = try! template.render(box)
+ template.baseContext = template.baseContext.extendedContext(willRender)
+ let value: [String: Any] = ["render": render, "subject": "---"]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "delegate")
}
func testRenderFunctionTriggersWillRenderFunctionsInAnotherTemplateFromSectionTag() {
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
switch tag.type {
case .section:
return box
default:
- return Box("delegate")
+ return "delegate"
}
}
@@ -397,9 +397,9 @@ class RenderFunctionTests: XCTestCase {
}
let template = try! Template(string: "{{#render}}{{/render}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
- let box = Box(["render": render, "subject": "---"])
- let rendering = try! template.render(box)
+ template.baseContext = template.baseContext.extendedContext(willRender)
+ let value: [String: Any] = ["render": render, "subject": "---"]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "delegate")
}
@@ -410,8 +410,8 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("2")
}
- let box = Box(["items": [render1, render2]])
- let rendering = try! Template(string: "{{#items}}{{/items}}").render(box)
+ let value: [String: Any] = ["items": [render1, render2]]
+ let rendering = try! Template(string: "{{#items}}{{/items}}").render(value)
XCTAssertEqual(rendering, "12")
}
@@ -422,8 +422,8 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("2")
}
- let box = Box(["items": [render1, render2]])
- let rendering = try! Template(string: "{{items}}").render(box)
+ let value: [String: Any] = ["items": [render1, render2]]
+ let rendering = try! Template(string: "{{items}}").render(value)
XCTAssertEqual(rendering, "12")
}
@@ -434,8 +434,8 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": [render1, render2]])
- let rendering = try! Template(string: "{{items}}").render(box)
+ let value: [String: Any] = ["items": [render1, render2]]
+ let rendering = try! Template(string: "{{items}}").render(value)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -446,8 +446,8 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": [render1, render2]])
- let rendering = try! Template(string: "{{{items}}}").render(box)
+ let value: [String: Any] = ["items": [render1, render2]]
+ let rendering = try! Template(string: "{{{items}}}").render(value)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -458,8 +458,8 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>")
}
- let box = Box(["items": [render1, render2]])
- let rendering = try! Template(string: "{{items}}").render(box)
+ let value: [String: Any] = ["items": [render1, render2]]
+ let rendering = try! Template(string: "{{items}}").render(value)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -470,8 +470,8 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>")
}
- let box = Box(["items": [render1, render2]])
- let rendering = try! Template(string: "{{{items}}}").render(box)
+ let value: [String: Any] = ["items": [render1, render2]]
+ let rendering = try! Template(string: "{{{items}}}").render(value)
XCTAssertEqual(rendering, "<1><2>")
}
@@ -482,9 +482,9 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": [render1, render2]])
+ let value: [String: Any] = ["items": [render1, render2]]
do {
- _ = try Template(string: "{{items}}").render(box)
+ _ = try Template(string: "{{items}}").render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -500,9 +500,9 @@ class RenderFunctionTests: XCTestCase {
let render2 = { (info: RenderingInfo) -> Rendering in
return Rendering("<2>", .html)
}
- let box = Box(["items": [render1, render2]])
+ let value: [String: Any] = ["items": [render1, render2]]
do {
- _ = try Template(string: "{{#items}}{{/items}}").render(box)
+ _ = try Template(string: "{{#items}}{{/items}}").render(value)
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError)
@@ -514,16 +514,16 @@ class RenderFunctionTests: XCTestCase {
func testDynamicPartial() {
let repository = TemplateRepository(templates: ["partial": "{{subject}}"])
let template = try! repository.template(named: "partial")
- let box = Box(["partial": template, "subject": "---"])
- let rendering = try! Template(string: "{{partial}}").render(box)
+ let value: [String: Any] = ["partial": template, "subject": "---"]
+ let rendering = try! Template(string: "{{partial}}").render(value)
XCTAssertEqual(rendering, "---")
}
func testDynamicPartialIsNotHTMLEscaped() {
let repository = TemplateRepository(templates: ["partial": "<{{subject}}>"])
let template = try! repository.template(named: "partial")
- let box = Box(["partial": template, "subject": "---"])
- let rendering = try! Template(string: "{{partial}}").render(box)
+ let value: [String: Any] = ["partial": template, "subject": "---"]
+ let rendering = try! Template(string: "{{partial}}").render(value)
XCTAssertEqual(rendering, "<--->")
}
@@ -535,7 +535,7 @@ class RenderFunctionTests: XCTestCase {
let data: [String: Any] = [
"layout": try! repository.template(named: "layout"),
"subject": "---"]
- let rendering = try! template.render(Box(data))
+ let rendering = try! template.render(data)
XCTAssertEqual(rendering, "[]")
}
@@ -573,9 +573,9 @@ class RenderFunctionTests: XCTestCase {
// let altTemplate = Template(string: "{{>partial}}")!
// return altTemplate.render(info.context, error: error)
// }
-// let box = Box(["render": Box(render), "subject": Box("-")])
+// let value: [String: Any] = ["render": render, "subject": "-"]
// let template = repository.template(named: "template")!
-// let rendering = template.render(box)!
+// let rendering = template.render(value)!
// XCTAssertEqual(rendering, "-")
// }
//
@@ -586,40 +586,40 @@ class RenderFunctionTests: XCTestCase {
// let repository2 = TemplateRepository(templates: [
// "template2": "{{ render }}",
// "partial": "partial2"])
-// let box = Box([
-// "template2": Box(repository2.template(named: "template2")!),
-// "render": Box({ (info: RenderingInfo) -> Rendering in
+// let value: [String: Any] = [
+// "template2": repository2.template(named: "template2")!,
+// "render": { (info: RenderingInfo) -> Rendering in
// let altTemplate = Template(string: "{{>partial}}")!
// return altTemplate.render(info.context, error: error)
-// })])
+// }]
// let template = repository1.template(named: "template1")!
-// let rendering = template.render(box)!
+// let rendering = template.render(value)!
// XCTAssertEqual(rendering, "partial1|partial2")
// }
//
// func testRenderFunctionInheritHTMLContentTypeOfCurrentlyRenderedTemplate() {
-// let box = Box([
-// "object": Box("&"),
-// "render": Box({ (info: RenderingInfo) -> Rendering in
+// let value: [String: Any] = [
+// "object": "&",
+// "render": { (info: RenderingInfo) -> Rendering in
// let altTemplate = Template(string: "{{ object }}")!
// return altTemplate.render(info.context, error: error)
-// })])
+// }]
//
// let template = Template(string: "{{%CONTENT_TYPE:HTML}}{{render}}")!
-// let rendering = template.render(box)!
+// let rendering = template.render(value)!
// XCTAssertEqual(rendering, "&")
// }
//
// func testRenderFunctionInheritTextContentTypeOfCurrentlyRenderedTemplate() {
-// let box = Box([
-// "object": Box("&"),
-// "render": Box({ (info: RenderingInfo) -> Rendering in
+// let value: [String: Any] = [
+// "object": "&",
+// "render": { (info: RenderingInfo) -> Rendering in
// let altTemplate = Template(string: "{{ object }}")!
// return altTemplate.render(info.context, error: error)
-// })])
+// }]
//
// let template = Template(string: "{{%CONTENT_TYPE:TEXT}}{{render}}")!
-// let rendering = template.render(box)!
+// let rendering = template.render(value)!
// XCTAssertEqual(rendering, "&")
// }
//
@@ -627,14 +627,14 @@ class RenderFunctionTests: XCTestCase {
// let repository = TemplateRepository(templates: [
// "templateHTML": "{{ render }}|{{> templateText }}",
// "templateText": "{{% CONTENT_TYPE:TEXT }}{{ render }}"])
-// let box = Box([
-// "value": Box("&"),
-// "render": Box({ (info: RenderingInfo) -> Rendering in
+// let value: [String: Any] = [
+// "value": "&",
+// "render": { (info: RenderingInfo) -> Rendering in
// let altTemplate = Template(string: "{{ value }}")!
// return altTemplate.render(info.context, error: error)
-// })])
+// }]
// let template = repository.template(named: "templateHTML")!
-// let rendering = template.render(box)!
+// let rendering = template.render(value)!
// XCTAssertEqual(rendering, "&|&")
// }
//
@@ -649,12 +649,12 @@ class RenderFunctionTests: XCTestCase {
// let altTemplate = Template(string: "{{{ value }}}")!
// return altTemplate.render(info.context, error: error)
// }
-// let box = Box([
-// "value": Box("&"),
-// "templateText": Box(repository2.template(named: "templateText")!),
-// "render": Box(render)])
+// let value: [String: Any] = [
+// "value": "&",
+// "templateText": repository2.template(named: "templateText")!,
+// "render": render]
// let template = repository1.template(named: "templateHTML")!
-// let rendering = template.render(box)!
+// let rendering = template.render(value)!
// XCTAssertEqual(rendering, "&|&")
// }
@@ -667,9 +667,9 @@ class RenderFunctionTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering("[2:\(rendering.string)]", rendering.contentType)
}
- let renders = [Box(render1), Box(render2), Box(true), Box(false)]
+ let renders: [Any] = [render1, render2, true, false]
let template = try! Template(string: "{{#items}}---{{/items}},{{#items}}{{#.}}---{{/.}}{{/items}}")
- let rendering = try! template.render(Box(["items":Box(renders)]))
+ let rendering = try! template.render(["items": renders])
XCTAssertEqual(rendering, "[1:---][2:---]------,[1:---][2:---]---")
}
}
diff --git a/Tests/Public/ServicesTests/EachFilterTests.swift b/Tests/Public/ServicesTests/EachFilterTests.swift
index 6d0075e8..424dbdfd 100644
--- a/Tests/Public/ServicesTests/EachFilterTests.swift
+++ b/Tests/Public/ServicesTests/EachFilterTests.swift
@@ -29,16 +29,16 @@ class EachFilterTests: XCTestCase {
func testEachFilterEnumeratesSet() {
let set = Set(["a", "b"])
let template = try! Template(string: "{{#each(set)}}({{@index}},{{.}}){{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["set": set]))
+ template.register(StandardLibrary.each, forKey: "each")
+ let rendering = try! template.render(["set": set])
XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].index(of: rendering) != nil)
}
func testEachFilterEnumeratesNSSet() {
let set = NSSet(objects: "a", "b")
let template = try! Template(string: "{{#each(set)}}({{@index}},{{.}}){{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["set": set]))
+ template.register(StandardLibrary.each, forKey: "each")
+ let rendering = try! template.render(["set": set])
XCTAssertTrue(["(0,a)(1,b)", "(0,b)(1,a)"].index(of: rendering) != nil)
}
@@ -47,10 +47,10 @@ class EachFilterTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering("<\(rendering.string)>", rendering.contentType)
}
- let box = Box(["array": [render]])
+ let value = ["array": [render]]
let template = try! Template(string: "{{#each(array)}}{{@index}}{{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(box)
+ template.register(StandardLibrary.each, forKey: "each")
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "<0>")
}
@@ -59,39 +59,39 @@ class EachFilterTests: XCTestCase {
let rendering = try! info.tag.render(info.context)
return Rendering("<\(rendering.string)>", rendering.contentType)
}
- let box = Box(["dictionary": ["a": render]])
+ let value = ["dictionary": ["a": render]]
let template = try! Template(string: "{{#each(dictionary)}}{{@key}}{{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(box)
+ template.register(StandardLibrary.each, forKey: "each")
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "")
}
func testEachFilterDoesNotMessWithItemValues() {
- let increment = Filter { (int: Int?) -> MustacheBox in
- return Box(int! + 1)
+ let increment = Filter { (int: Int?) -> Any? in
+ return int! + 1
}
let items = [1,2,3]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{increment(.)}}){{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- template.registerInBaseContext("increment", Box(increment))
- let rendering = try! template.render(Box(["items": items]))
+ template.register(StandardLibrary.each, forKey: "each")
+ template.register(increment, forKey: "increment")
+ let rendering = try! template.render(["items": items])
XCTAssertEqual(rendering, "(0,2)(1,3)(2,4)")
}
func testEachFilterDoesNotMessWithItemKeyedSubscriptFunction() {
let items = ["a","bb","ccc"]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{length}}){{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["items": items]))
+ template.register(StandardLibrary.each, forKey: "each")
+ let rendering = try! template.render(["items": items])
XCTAssertEqual(rendering, "(0,1)(1,2)(2,3)")
}
func testEachFilterDoesNotMessWithItemRenderFunction() {
let item = Lambda { "foo" }
- let items = [Box(item)]
+ let items = [item]
let template = try! Template(string: "{{#each(items)}}({{@index}},{{.}}){{/}}")
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- let rendering = try! template.render(Box(["items": items]))
+ template.register(StandardLibrary.each, forKey: "each")
+ let rendering = try! template.render(["items": items])
XCTAssertEqual(rendering, "(0,foo)")
}
}
diff --git a/Tests/Public/ServicesTests/NSFormatterTests.swift b/Tests/Public/ServicesTests/FormatterTests.swift
similarity index 76%
rename from Tests/Public/ServicesTests/NSFormatterTests.swift
rename to Tests/Public/ServicesTests/FormatterTests.swift
index 70d582e4..6ea83505 100644
--- a/Tests/Public/ServicesTests/NSFormatterTests.swift
+++ b/Tests/Public/ServicesTests/FormatterTests.swift
@@ -24,7 +24,7 @@
import XCTest
import Mustache
-class NSFormatterTests: XCTestCase {
+class FormatterTests: XCTestCase {
func testFormatterIsAFilterForProcessableValues() {
let percentFormatter = NumberFormatter()
@@ -36,8 +36,8 @@ class NSFormatterTests: XCTestCase {
// test filtering a number
let template = try! Template(string: "{{ percent(number) }}")
- let box = Box(["number": 0.5, "percent": percentFormatter])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["number": 0.5, "percent": percentFormatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "50%")
}
@@ -51,8 +51,8 @@ class NSFormatterTests: XCTestCase {
// test filtering a string
let template = try! Template(string: "{{ percent(string) }}")
- let box = Box(["string": "foo", "percent": percentFormatter])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["string": "foo", "percent": percentFormatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "")
}
@@ -62,8 +62,8 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{ number }} {{ number }}{{/ percent }}")
- let box = Box(["number": 0.5, "percent": percentFormatter])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["number": 0.5, "percent": percentFormatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "50% 50%")
}
@@ -73,8 +73,8 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{ value }}{{/ percent }}")
- let box = Box(["value": "foo", "percent": percentFormatter])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["value": "foo", "percent": percentFormatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "foo")
}
@@ -84,8 +84,8 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "{{# percent }}{{# number }}Number is {{ number }}.{{/ number }}{{/ percent }}")
- let box = Box(["number": 0.5, "percent": percentFormatter])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["number": 0.5, "percent": percentFormatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "Number is 50%.")
}
@@ -95,24 +95,24 @@ class NSFormatterTests: XCTestCase {
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
let template = try! Template(string: "NO is {{ NO }}. {{^ NO }}NO is false.{{/ NO }} percent(NO) is {{ percent(NO) }}. {{# percent(NO) }}percent(NO) is true.{{/ percent(NO) }} {{# percent }}{{^ NO }}NO is now {{ NO }} and is still false.{{/ NO }}{{/ percent }}")
- let box = Box(["number": 0.5, "NO": 0, "percent": percentFormatter])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["number": 0.5, "NO": 0, "percent": percentFormatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO is 0. NO is false. percent(NO) is 0%. percent(NO) is true. NO is now 0% and is still false.")
}
func testFormatterIsTruthy() {
let formatter = Formatter()
let template = try! Template(string: "{{# formatter }}Formatter is true.{{/ formatter }}{{^ formatter }}Formatter is false.{{/ formatter }}")
- let box = Box(["formatter": formatter])
- let rendering = try! template.render(box)
+ let value = ["formatter": formatter]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "Formatter is true.")
}
func testFormatterRendersSelfAsSomething() {
let formatter = Formatter()
let template = try! Template(string: "{{ formatter }}")
- let box = Box(["formatter": formatter])
- let rendering = try! template.render(box)
+ let value = ["formatter": formatter]
+ let rendering = try! template.render(value)
XCTAssertTrue(rendering.characters.count > 0)
}
@@ -124,14 +124,14 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": percentFormatter])
+ let value = ["format": percentFormatter]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -140,14 +140,14 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": percentFormatter, "value": NSNull()])
+ let value: [String: Any] = ["format": percentFormatter, "value": NSNull()]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -156,34 +156,34 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- var box = Box(["format": percentFormatter, "value": "1"])
+ var value: [String: Any] = ["format": percentFormatter, "value": "1"]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": percentFormatter, "value": "YES"])
+ value = ["format": percentFormatter, "value": "YES"]
template = try! Template(string: "<{{format(value)}}>")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": percentFormatter, "value": "foo"])
+ value = ["format": percentFormatter, "value": "foo"]
template = try! Template(string: "<{{format(value)}}>")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -195,14 +195,14 @@ class NSFormatterTests: XCTestCase {
percentFormatter.numberStyle = .percent
percentFormatter.locale = Locale(identifier: "en_US_POSIX")
- let box = Box(["format": percentFormatter, "value": Date()])
+ let value: [String: Any] = ["format": percentFormatter, "value": Date()]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -213,14 +213,14 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- let box = Box(["format": dateFormatter])
+ let value = ["format": dateFormatter]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -228,14 +228,14 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- let box = Box(["format": dateFormatter, "value": NSNull()])
+ let value: [String: Any] = ["format": dateFormatter, "value": NSNull()]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -243,34 +243,34 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- var box = Box(["format": dateFormatter, "value": "1"])
+ var value: [String: Any] = ["format": dateFormatter, "value": "1"]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": dateFormatter, "value": "YES"])
+ value = ["format": dateFormatter, "value": "YES"]
template = try! Template(string: "<{{format(value)}}>")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
- box = Box(["format": dateFormatter, "value": "foo"])
+ value = ["format": dateFormatter, "value": "foo"]
template = try! Template(string: "<{{format(value)}}>")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
@@ -281,14 +281,14 @@ class NSFormatterTests: XCTestCase {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
- let box = Box(["format": dateFormatter, "value": 0])
+ let value: [String: Any] = ["format": dateFormatter, "value": 0]
var template = try! Template(string: "<{{format(value)}}>")
- var rendering = try! template.render(box)
+ var rendering = try! template.render(value)
XCTAssertEqual(rendering, "<>")
template = try! Template(string: "{{#format(value)}}YES{{/}}{{^format(value)}}NO{{/}}")
- rendering = try! template.render(box)
+ rendering = try! template.render(value)
XCTAssertEqual(rendering, "NO")
}
}
diff --git a/Tests/Public/ServicesTests/LocalizerTests.swift b/Tests/Public/ServicesTests/LocalizerTests.swift
index 07c5260d..bb170c97 100644
--- a/Tests/Public/ServicesTests/LocalizerTests.swift
+++ b/Tests/Public/ServicesTests/LocalizerTests.swift
@@ -36,28 +36,28 @@ class LocalizerTests: XCTestCase {
func testLocalizer() {
let template = try! Template(string: "{{localize(string)}}")
- let box = Box(["localize": localizer, "string": "testable?"])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["localize": localizer, "string": "testable?"]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "YES")
}
func testLocalizerFromTable() {
let template = try! Template(string: "{{localize(string)}}")
let localizer = StandardLibrary.Localizer(bundle: localizableBundle, table: "Table")
- let box = Box(["localize": localizer, "string": "table_testable?"])
- let rendering = try! template.render(box)
+ let value: [String: Any] = ["localize": localizer, "string": "table_testable?"]
+ let rendering = try! template.render(value)
XCTAssertEqual(rendering, "YES")
}
func testLocalizerAsRenderingObjectWithoutArgumentDoesNotNeedPercentEscapedLocalizedString() {
var template = try! Template(string: "{{#localize}}%d{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
var rendering = try! template.render()
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%d", value: nil, table: nil), "ha ha percent d %d")
XCTAssertEqual(rendering, "ha ha percent d %d")
template = try! Template(string: "{{#localize}}%@{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
rendering = try! template.render()
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%@", value: nil, table: nil), "ha ha percent @ %@")
XCTAssertEqual(rendering, "ha ha percent @ %@")
@@ -65,29 +65,29 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsRenderingObjectWithoutArgumentNeedsPercentEscapedLocalizedString() {
var template = try! Template(string: "{{#localize}}%d {{foo}}{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- var rendering = try! template.render(Box(["foo": "bar"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ var rendering = try! template.render(["foo": "bar"])
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%%d %@", value: nil, table: nil), "ha ha percent d %%d %@")
XCTAssertEqual(rendering, "ha ha percent d %d bar")
template = try! Template(string: "{{#localize}}%@ {{foo}}{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- rendering = try! template.render(Box(["foo": "bar"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ rendering = try! template.render(["foo": "bar"])
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "%%@ %@", value: nil, table: nil), "ha ha percent @ %%@ %@")
XCTAssertEqual(rendering, "ha ha percent @ %@ bar")
}
func testLocalizerAsFilter() {
let template = try! Template(string: "{{localize(foo)}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- let rendering = try! template.render(Box(["foo": "bar"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ let rendering = try! template.render(["foo": "bar"])
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "bar", value: nil, table: nil), "translated_bar")
XCTAssertEqual(rendering, "translated_bar")
}
func testLocalizerAsRenderable() {
let template = try! Template(string: "{{#localize}}bar{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
let rendering = try! template.render()
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "bar", value: nil, table: nil), "translated_bar")
XCTAssertEqual(rendering, "translated_bar")
@@ -95,41 +95,41 @@ class LocalizerTests: XCTestCase {
func testLocalizerAsRenderableWithArgument() {
let template = try! Template(string: "{{#localize}}..{{foo}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- let rendering = try! template.render(Box(["foo": "bar"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ let rendering = try! template.render(["foo": "bar"])
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: "..%@..", value: nil, table: nil), "!!%@!!")
XCTAssertEqual(rendering, "!!bar!!")
}
func testLocalizerAsRenderableWithArgumentAndConditions() {
let template = try! Template(string: "{{#localize}}.{{foo}}.{{^false}}{{baz}}{{/}}.{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- let rendering = try! template.render(Box(["foo": "bar", "baz": "truc"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ let rendering = try! template.render(["foo": "bar", "baz": "truc"])
XCTAssertEqual(self.localizer.bundle.localizedString(forKey: ".%@.%@.", value: nil, table: nil), "!%@!%@!")
XCTAssertEqual(rendering, "!bar!truc!")
}
func testLocalizerRendersHTMLEscapedValuesOfHTMLTemplates() {
var template = try! Template(string: "{{#localize}}..{{foo}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- var rendering = try! template.render(Box(["foo": "&"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ var rendering = try! template.render(["foo": "&"])
XCTAssertEqual(rendering, "!!&!!")
template = try! Template(string: "{{#localize}}..{{{foo}}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- rendering = try! template.render(Box(["foo": "&"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ rendering = try! template.render(["foo": "&"])
XCTAssertEqual(rendering, "!!&!!")
}
func testLocalizerRendersUnescapedValuesOfTextTemplates() {
var template = try! Template(string: "{{% CONTENT_TYPE:TEXT }}{{#localize}}..{{foo}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- var rendering = try! template.render(Box(["foo": "&"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ var rendering = try! template.render(["foo": "&"])
XCTAssertEqual(rendering, "!!&!!")
template = try! Template(string: "{{% CONTENT_TYPE:TEXT }}{{#localize}}..{{{foo}}}..{{/}}")
- template.baseContext = template.baseContext.extendedContext(Box(["localize": localizer]))
- rendering = try! template.render(Box(["foo": "&"]))
+ template.baseContext = template.baseContext.extendedContext(["localize": localizer])
+ rendering = try! template.render(["foo": "&"])
XCTAssertEqual(rendering, "!!&!!")
}
}
diff --git a/Tests/Public/ServicesTests/LoggerTests.swift b/Tests/Public/ServicesTests/LoggerTests.swift
index 6e92d853..7ea88564 100644
--- a/Tests/Public/ServicesTests/LoggerTests.swift
+++ b/Tests/Public/ServicesTests/LoggerTests.swift
@@ -31,10 +31,10 @@ class LoggerTests : XCTestCase {
let logger = StandardLibrary.Logger { logMessages.append($0) }
let template = try! Template(string: "{{#people}}- {{name}} has a Mustache.\n{{/people}}")
- template.extendBaseContext(Box(logger))
+ template.extendBaseContext(logger)
- let data: NSDictionary = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
- _ = try! template.render(Box(data))
+ let data = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
+ _ = try! template.render(data)
XCTAssertEqual(logMessages.count, 5)
XCTAssertEqual(logMessages[0], "{{#people}} at line 1 will render [[\"name\":\"Frank Zappa\"],[\"name\":\"Charlie Chaplin\"],[\"name\":\"Albert Einstein\"]]")
@@ -49,10 +49,10 @@ class LoggerTests : XCTestCase {
let logger = StandardLibrary.Logger { logMessages.append($0) }
let template = try! Template(string: "{{#people}}{{#log}}- {{name}} has a Mustache.\n{{/log}}{{/people}}{{#log}}{{missing}}{{/log}}")
- template.registerInBaseContext("log", Box(logger))
+ template.register(logger, forKey: "log")
- let data: NSDictionary = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
- _ = try! template.render(Box(data))
+ let data = ["people": [["name": "Frank Zappa"], ["name": "Charlie Chaplin"], ["name": "Albert Einstein"]]]
+ _ = try! template.render(data)
XCTAssertEqual(logMessages.count, 4)
XCTAssertEqual(logMessages[0], "{{name}} at line 1 did render \"Frank Zappa\" as \"Frank Zappa\"")
diff --git a/Tests/Public/ServicesTests/StandardLibraryTests.swift b/Tests/Public/ServicesTests/StandardLibraryTests.swift
index 28cb7b3a..d301585c 100644
--- a/Tests/Public/ServicesTests/StandardLibraryTests.swift
+++ b/Tests/Public/ServicesTests/StandardLibraryTests.swift
@@ -27,58 +27,58 @@ import Mustache
class StandardLibraryTests: XCTestCase {
func testStandardLibraryHTMLEscapeDoesEscapeText() {
- let render = Box({ (info: RenderingInfo) -> Rendering in
+ let render = { (info: RenderingInfo) -> Rendering in
return Rendering("<")
- })
+ }
var template = try! Template(string: "{{# HTMLEscape }}{{ object }}{{/ }}")
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
- var rendering = try! template.render(Box(["object": render]))
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+ var rendering = try! template.render(["object": render])
XCTAssertEqual(rendering, "<")
template = try! Template(string: "{{# HTMLEscape }}{{{ object }}}{{/ }}")
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
- rendering = try! template.render(Box(["object": render]))
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+ rendering = try! template.render(["object": render])
XCTAssertEqual(rendering, "<")
}
func testStandardLibraryHTMLEscapeDoesEscapeHTML() {
- let render = Box({ (info: RenderingInfo) -> Rendering in
+ let render = { (info: RenderingInfo) -> Rendering in
return Rendering("
", .html)
- })
+ }
var template = try! Template(string: "{{# HTMLEscape }}{{ object }}{{/ }}")
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
- var rendering = try! template.render(Box(["object": render]))
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+ var rendering = try! template.render(["object": render])
XCTAssertEqual(rendering, "<br>")
template = try! Template(string: "{{# HTMLEscape }}{{{ object }}}{{/ }}")
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
- rendering = try! template.render(Box(["object": render]))
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+ rendering = try! template.render(["object": render])
XCTAssertEqual(rendering, "<br>")
}
func testStandardLibraryJavascriptEscapeDoesEscapeRenderFunction() {
- let render = Box({ (info: RenderingInfo) -> Rendering in
+ let render = { (info: RenderingInfo) -> Rendering in
return Rendering("\"double quotes\" and 'single quotes'")
- })
+ }
let template = try! Template(string: "{{# javascriptEscape }}{{ object }}{{/ }}")
- template.registerInBaseContext("javascriptEscape", Box(StandardLibrary.javascriptEscape))
+ template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
- let rendering = try! template.render(Box(["object": render]))
+ let rendering = try! template.render(["object": render])
XCTAssertEqual(rendering, "\\u0022double quotes\\u0022 and \\u0027single quotes\\u0027")
}
func testStandardLibraryURLEscapeDoesEscapeRenderFunctions() {
- let render = Box({ (info: RenderingInfo) -> Rendering in
+ let render = { (info: RenderingInfo) -> Rendering in
return Rendering("&")
- })
+ }
let template = try! Template(string: "{{# URLEscape }}{{ object }}{{/ }}")
- template.registerInBaseContext("URLEscape", Box(StandardLibrary.URLEscape))
+ template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
- let rendering = try! template.render(Box(["object": render]))
+ let rendering = try! template.render(["object": render])
XCTAssertEqual(rendering, "%26")
}
}
diff --git a/Tests/Public/SuitesTests/SuiteTestCase.swift b/Tests/Public/SuitesTests/SuiteTestCase.swift
index 5b22bc60..1f61fc07 100644
--- a/Tests/Public/SuitesTests/SuiteTestCase.swift
+++ b/Tests/Public/SuitesTests/SuiteTestCase.swift
@@ -69,17 +69,18 @@ class SuiteTestCase: XCTestCase {
for template in templates {
// Standard Library
- template.registerInBaseContext("each", Box(StandardLibrary.each))
- template.registerInBaseContext("zip", Box(StandardLibrary.zip))
- template.registerInBaseContext("localize", Box(StandardLibrary.Localizer(bundle: nil, table: nil)))
- template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
- template.registerInBaseContext("URLEscape", Box(StandardLibrary.URLEscape))
- template.registerInBaseContext("javascriptEscape", Box(StandardLibrary.javascriptEscape))
+ template.register(StandardLibrary.each, forKey: "each")
+ template.register(StandardLibrary.zip, forKey: "zip")
+ template.register(StandardLibrary.Localizer(bundle: nil, table: nil), forKey: "localize")
+ template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
+ template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
+ template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
// Support for filters.json
- template.registerInBaseContext("capitalized", Box(Filter({ (string: String?) -> MustacheBox in
- return Box(string?.capitalized)
- })))
+ let capitalized = Filter { (string: String?) -> Any? in
+ return string?.capitalized
+ }
+ template.register(capitalized, forKey: "capitalized")
testRendering(template)
}
@@ -92,7 +93,7 @@ class SuiteTestCase: XCTestCase {
var partialsDictionary: [String: String]? { return dictionary["partials"] as! [String: String]? }
var templateString: String? { return dictionary["template"] as! String? }
var templateName: String? { return dictionary["template_name"] as! String? }
- var renderedValue: MustacheBox { return Box(dictionary["data"] as? NSObject) }
+ var renderedValue: Any? { return dictionary["data"] }
var expectedRendering: String? { return dictionary["expected"] as! String? }
var expectedError: String? { return dictionary["expected_error"] as! String? }
diff --git a/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift b/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift
index 48ebe7ce..b6d24377 100644
--- a/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift
+++ b/Tests/Public/SuitesTests/twitter/hogan.js/HoganSuite.swift
@@ -42,7 +42,7 @@ class HoganSuite: SuiteTestCase {
]
let repo = TemplateRepository(templates: templates)
let template = try! repo.template(named: "template")
- let rendering = try! template.render(Box(["lambda": lambda]))
+ let rendering = try! template.render(["lambda": lambda])
XCTAssertEqual(rendering, "altered child1 - altered parent2")
}
@@ -57,7 +57,7 @@ class HoganSuite: SuiteTestCase {
]
let repo = TemplateRepository(templates: templates)
let template = try! repo.template(named: "template")
- let rendering = try! template.render(Box(["lambda": lambda]))
+ let rendering = try! template.render(["lambda": lambda])
XCTAssertEqual(rendering, "changed test2")
}
}
diff --git a/Tests/Public/TagTests/TagTests.swift b/Tests/Public/TagTests/TagTests.swift
index 355bc024..bd60a795 100644
--- a/Tests/Public/TagTests/TagTests.swift
+++ b/Tests/Public/TagTests/TagTests.swift
@@ -28,28 +28,28 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsTagToken() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
tagDescription = nil
var template = try! Template(string: "{{name}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: "{{name}}")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "{{#name}}{{/name}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: "{{#name}}")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "{{ name\t}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: "{{ name\t}}")
XCTAssertTrue(range != nil)
@@ -57,28 +57,28 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsLineNumber() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
tagDescription = nil
var template = try! Template(string: "{{name}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: "line 1")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "\n {{\nname}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: "line 2")
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(string: "\n\n {{#\nname}}\n\n{{/name}}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: "line 3")
XCTAssertTrue(range != nil)
@@ -86,7 +86,7 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsResourceBasedTemplatePath() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
@@ -95,14 +95,14 @@ class TagTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(bundle: bundle)
var template = try! templateRepository.template(named: "TagTests")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(named: "TagTests", bundle: bundle)
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
@@ -110,7 +110,7 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsURLBasedTemplatePath() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
@@ -119,14 +119,14 @@ class TagTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(baseURL: bundle.resourceURL!)
var template = try! templateRepository.template(named: "TagTests")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(URL: bundle.url(forResource: "TagTests", withExtension: "mustache")!)
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
@@ -134,7 +134,7 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsPathBasedTemplatePath() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
@@ -143,14 +143,14 @@ class TagTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(directoryPath: bundle.resourcePath!)
var template = try! templateRepository.template(named: "TagTests")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(path: bundle.path(forResource: "TagTests", ofType: "mustache")!)
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
@@ -158,7 +158,7 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsResourceBasedPartialPath() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
@@ -167,21 +167,21 @@ class TagTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(bundle: bundle)
var template = try! templateRepository.template(named: "TagTests_wrapper")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! templateRepository.template(string: "{{> TagTests }}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(named: "TagTests_wrapper", bundle: bundle)
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
@@ -189,7 +189,7 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsURLBasedPartialPath() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
@@ -198,21 +198,21 @@ class TagTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(baseURL: bundle.resourceURL!)
var template = try! templateRepository.template(named: "TagTests_wrapper")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! templateRepository.template(string: "{{> TagTests }}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(URL: bundle.url(forResource: "TagTests_wrapper", withExtension: "mustache")!)
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
@@ -220,7 +220,7 @@ class TagTests: XCTestCase {
func testTagDescriptionContainsPathBasedPartialPath() {
var tagDescription: String? = nil
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
tagDescription = tag.description
return box
}
@@ -229,21 +229,21 @@ class TagTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let templateRepository = TemplateRepository(directoryPath: bundle.resourcePath!)
var template = try! templateRepository.template(named: "TagTests_wrapper")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
var range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! templateRepository.template(string: "{{> TagTests }}")
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
tagDescription = nil
template = try! Template(path: bundle.path(forResource: "TagTests_wrapper", ofType: "mustache")!)
- template.baseContext = template.baseContext.extendedContext(Box(willRender))
+ template.baseContext = template.baseContext.extendedContext(willRender)
_ = try! template.render()
range = tagDescription?.range(of: bundle.path(forResource: "TagTests", ofType: "mustache")!)
XCTAssertTrue(range != nil)
diff --git a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift
index bc488397..dfe60726 100644
--- a/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift
+++ b/Tests/Public/TemplateRepositoryTests/TemplateRepositoryTests.swift
@@ -51,7 +51,7 @@ class TemplateRepositoryTests: XCTestCase {
func testTemplateRepositoryWithoutDataSourceCanLoadStringTemplate() {
let repo = TemplateRepository()
let template = try! repo.template(string:"{{.}}")
- let rendering = try! template.render(Box("success"))
+ let rendering = try! template.render("success")
XCTAssertEqual(rendering, "success")
}
@@ -60,7 +60,7 @@ class TemplateRepositoryTests: XCTestCase {
let repo = TemplateRepository(templates: templates)
let template1 = try! repo.template(named: "name")
- template1.registerInBaseContext("value", Box("foo"))
+ template1.register("foo", forKey: "value")
let rendering1 = try! template1.render()
let template2 = try! repo.template(named: "name")
diff --git a/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift b/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
index 9689f7f3..ecc74473 100644
--- a/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
+++ b/Tests/Public/TemplateTests/TemplateFromMethodsTests/TemplateFromMethodsTests.swift
@@ -27,11 +27,11 @@ import Mustache
class TemplateFromMethodsTests: XCTestCase {
func makeKeyedSubscriptFunction(_ string: String) -> KeyedSubscriptFunction {
- return { (key: String) -> MustacheBox in
+ return { (key: String) -> Any? in
if key == "string" {
- return Box(string)
+ return string
} else {
- return Box()
+ return nil
}
}
}
diff --git a/Tests/Public/TemplateTests/TemplateTests.swift b/Tests/Public/TemplateTests/TemplateTests.swift
index a2f76bda..6ca37654 100644
--- a/Tests/Public/TemplateTests/TemplateTests.swift
+++ b/Tests/Public/TemplateTests/TemplateTests.swift
@@ -34,12 +34,12 @@ class TemplateTests: XCTestCase {
func testTemplateExtendBaseContextWithValue() {
let template = try! Template(string: "{{name}}")
- template.extendBaseContext(Box(["name": "Arthur"]))
+ template.extendBaseContext(["name": "Arthur"])
var rendering = try! template.render()
XCTAssertEqual(rendering, "Arthur")
- rendering = try! template.render(Box(["name": "Bobby"]))
+ rendering = try! template.render(["name": "Bobby"])
XCTAssertEqual(rendering, "Bobby")
}
@@ -48,12 +48,12 @@ class TemplateTests: XCTestCase {
}
func testTemplateExtendBaseContextWithWillRenderFunction() {
- let willRender = { (tag: Tag, box: MustacheBox) -> MustacheBox in
- return Box("observer")
+ let willRender = { (tag: Tag, box: MustacheBox) -> Any? in
+ return "observer"
}
let template = try! Template(string: "{{name}}")
- template.extendBaseContext(Box(willRender))
+ template.extendBaseContext(willRender)
let rendering = try! template.render()
XCTAssertEqual(rendering, "observer")
}
diff --git a/Xcode/Mustache.xcodeproj/project.pbxproj b/Xcode/Mustache.xcodeproj/project.pbxproj
index 3b09511f..21153ee2 100644
--- a/Xcode/Mustache.xcodeproj/project.pbxproj
+++ b/Xcode/Mustache.xcodeproj/project.pbxproj
@@ -137,7 +137,7 @@
561E72AB1A8BDC6A004ED48B /* RenderFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A88EC71A066EC40055E40A /* RenderFunctionTests.swift */; };
561E72AC1A8BDC6A004ED48B /* EachFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B4B23A1A29E65E00BD4F8F /* EachFilterTests.swift */; };
561E72AD1A8BDC6A004ED48B /* LocalizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685DAB51A1A2074003E583B /* LocalizerTests.swift */; };
- 561E72AE1A8BDC6A004ED48B /* NSFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3741A1CC7B8000CE8CB /* NSFormatterTests.swift */; };
+ 561E72AE1A8BDC6A004ED48B /* FormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3741A1CC7B8000CE8CB /* FormatterTests.swift */; };
561E72AF1A8BDC6A004ED48B /* StandardLibraryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C8D6DB1A1DCB9500F106F8 /* StandardLibraryTests.swift */; };
561E72B01A8BDC6A004ED48B /* KeyedSubscriptFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698D0261A1626240039D564 /* KeyedSubscriptFunctionTests.swift */; };
561E72B11A8BDC6A004ED48B /* TagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B4019F1A1F643B002D5CD7 /* TagTests.swift */; };
@@ -215,7 +215,7 @@
566244B21AF201E7008BAD41 /* TemplateGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566244B01AF201E7008BAD41 /* TemplateGenerator.swift */; };
56660D6E1A9378B100E53CCF /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56660D6D1A9378B100E53CCF /* CoreFunctions.swift */; };
56660D6F1A9378B100E53CCF /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56660D6D1A9378B100E53CCF /* CoreFunctions.swift */; };
- 5674F3751A1CC7B8000CE8CB /* NSFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3741A1CC7B8000CE8CB /* NSFormatterTests.swift */; };
+ 5674F3751A1CC7B8000CE8CB /* FormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5674F3741A1CC7B8000CE8CB /* FormatterTests.swift */; };
5685DAB41A1A2029003E583B /* LocalizerTestsBundle in Resources */ = {isa = PBXBuildFile; fileRef = 5685DAB31A1A2029003E583B /* LocalizerTestsBundle */; };
5685DAB61A1A2074003E583B /* LocalizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685DAB51A1A2074003E583B /* LocalizerTests.swift */; };
569517021A8646CF00FF7D86 /* ObjcKeyAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569517011A8646CF00FF7D86 /* ObjcKeyAccessTests.swift */; };
@@ -273,6 +273,9 @@
56EE412A1DBB788D00CCA895 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE41291DBB788D00CCA895 /* Formatter.swift */; };
56EE412B1DBB788D00CCA895 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE41291DBB788D00CCA895 /* Formatter.swift */; };
56EE412C1DBB788D00CCA895 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE41291DBB788D00CCA895 /* Formatter.swift */; };
+ 56EE412E1DBBBE0B00CCA895 /* Fixit-1.1.0.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE412D1DBBBE0B00CCA895 /* Fixit-1.1.0.swift */; };
+ 56EE412F1DBBBE0B00CCA895 /* Fixit-1.1.0.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE412D1DBBBE0B00CCA895 /* Fixit-1.1.0.swift */; };
+ 56EE41301DBBBE0B00CCA895 /* Fixit-1.1.0.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56EE412D1DBBBE0B00CCA895 /* Fixit-1.1.0.swift */; };
56FC9C8A1A17CD0C0020AAF8 /* ConfigurationBaseContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC9C841A17CD0C0020AAF8 /* ConfigurationBaseContextTests.swift */; };
56FC9C8B1A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC9C851A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift */; };
56FC9C8C1A17CD0C0020AAF8 /* ConfigurationExtendBaseContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC9C861A17CD0C0020AAF8 /* ConfigurationExtendBaseContextTests.swift */; };
@@ -368,7 +371,7 @@
566244A61AF1653C008BAD41 /* HoganSuite */ = {isa = PBXFileReference; lastKnownFileType = folder; path = HoganSuite; sourceTree = ""; };
566244B01AF201E7008BAD41 /* TemplateGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateGenerator.swift; sourceTree = ""; };
56660D6D1A9378B100E53CCF /* CoreFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreFunctions.swift; sourceTree = ""; };
- 5674F3741A1CC7B8000CE8CB /* NSFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSFormatterTests.swift; sourceTree = ""; };
+ 5674F3741A1CC7B8000CE8CB /* FormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormatterTests.swift; sourceTree = ""; };
5685DAB31A1A2029003E583B /* LocalizerTestsBundle */ = {isa = PBXFileReference; lastKnownFileType = folder; path = LocalizerTestsBundle; sourceTree = ""; };
5685DAB51A1A2074003E583B /* LocalizerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizerTests.swift; sourceTree = ""; };
569517011A8646CF00FF7D86 /* ObjcKeyAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjcKeyAccessTests.swift; sourceTree = ""; };
@@ -413,6 +416,7 @@
56DFF1DC1C16029B000082A4 /* Mustache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mustache.h; sourceTree = ""; };
56EB0BE71BD0CB0D00A3DC55 /* LambdaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LambdaTests.swift; sourceTree = ""; };
56EE41291DBB788D00CCA895 /* Formatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = ""; };
+ 56EE412D1DBBBE0B00CCA895 /* Fixit-1.1.0.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Fixit-1.1.0.swift"; sourceTree = ""; };
56FC9C841A17CD0C0020AAF8 /* ConfigurationBaseContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationBaseContextTests.swift; sourceTree = ""; };
56FC9C851A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationContentTypeTests.swift; sourceTree = ""; };
56FC9C861A17CD0C0020AAF8 /* ConfigurationExtendBaseContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationExtendBaseContextTests.swift; sourceTree = ""; };
@@ -559,6 +563,7 @@
563AE1B319FBE4D90028C6C1 /* Parsing */,
563AE1D619FC4A360028C6C1 /* Rendering */,
563AE1D119FC45350028C6C1 /* Template */,
+ 56EE412D1DBBBE0B00CCA895 /* Fixit-1.1.0.swift */,
);
name = Sources;
path = ../Sources;
@@ -703,10 +708,10 @@
isa = PBXGroup;
children = (
56B4B23A1A29E65E00BD4F8F /* EachFilterTests.swift */,
+ 5674F3741A1CC7B8000CE8CB /* FormatterTests.swift */,
5685DAB51A1A2074003E583B /* LocalizerTests.swift */,
5685DAB31A1A2029003E583B /* LocalizerTestsBundle */,
5611BF971B333E1F005FA874 /* LoggerTests.swift */,
- 5674F3741A1CC7B8000CE8CB /* NSFormatterTests.swift */,
56C8D6DB1A1DCB9500F106F8 /* StandardLibraryTests.swift */,
);
path = ServicesTests;
@@ -1166,6 +1171,7 @@
09A89DFE1BF58560003A695E /* Context.swift in Sources */,
09A89E041BF58572003A695E /* Template.swift in Sources */,
09A89E021BF58569003A695E /* MustacheBox.swift in Sources */,
+ 56EE41301DBBBE0B00CCA895 /* Fixit-1.1.0.swift in Sources */,
09A89E051BF58572003A695E /* TemplateRepository.swift in Sources */,
09A89DE51BF58526003A695E /* Common.swift in Sources */,
09A89DE91BF58538003A695E /* Tag.swift in Sources */,
@@ -1237,6 +1243,7 @@
561E72951A8BDC4D004ED48B /* RenderingEngine.swift in Sources */,
560DE2641AC33D0D005C0312 /* Box.swift in Sources */,
56D204351B32ECF40031FEE7 /* Logger.swift in Sources */,
+ 56EE412F1DBBBE0B00CCA895 /* Fixit-1.1.0.swift in Sources */,
561E72891A8BDC4D004ED48B /* JavascriptEscapeHelper.swift in Sources */,
561E72811A8BDC4D004ED48B /* TemplateASTNode.swift in Sources */,
5607A3401C160216002364C1 /* GRMustacheKeyAccess.m in Sources */,
@@ -1284,7 +1291,7 @@
5611BF991B333E1F005FA874 /* LoggerTests.swift in Sources */,
561E729F1A8BDC6A004ED48B /* ContextRegisteredKeyTests.swift in Sources */,
561E72AC1A8BDC6A004ED48B /* EachFilterTests.swift in Sources */,
- 561E72AE1A8BDC6A004ED48B /* NSFormatterTests.swift in Sources */,
+ 561E72AE1A8BDC6A004ED48B /* FormatterTests.swift in Sources */,
561E72A61A8BDC6A004ED48B /* VariadicFilterTests.swift in Sources */,
566244A51AF164D5008BAD41 /* SuiteTestCase.swift in Sources */,
561E729E1A8BDC6A004ED48B /* ConfigurationTagDelimitersTests.swift in Sources */,
@@ -1325,6 +1332,7 @@
56BD79401A0E381900937A76 /* MustacheBox.swift in Sources */,
563AE1FB19FC5B860028C6C1 /* SectionTag.swift in Sources */,
560DE2631AC33D0D005C0312 /* Box.swift in Sources */,
+ 56EE412E1DBBBE0B00CCA895 /* Fixit-1.1.0.swift in Sources */,
56D204341B32EBA50031FEE7 /* Logger.swift in Sources */,
56BF06581A063DC800926BEB /* URLEscapeHelper.swift in Sources */,
563AE1C919FC38B80028C6C1 /* ExpressionParser.swift in Sources */,
@@ -1382,7 +1390,7 @@
56C8D6E81A1F1AD800F106F8 /* ReadMeTests.swift in Sources */,
56C8D6E11A1F1A4700F106F8 /* BoxTests.swift in Sources */,
569C423B1A87D03800748E98 /* ContextRegisteredKeyTests.swift in Sources */,
- 5674F3751A1CC7B8000CE8CB /* NSFormatterTests.swift in Sources */,
+ 5674F3751A1CC7B8000CE8CB /* FormatterTests.swift in Sources */,
56FC9C8E1A17CD0C0020AAF8 /* ContextValueForMustacheExpressionTests.swift in Sources */,
56FC9C8B1A17CD0C0020AAF8 /* ConfigurationContentTypeTests.swift in Sources */,
56FC9C8D1A17CD0C0020AAF8 /* ConfigurationTagDelimitersTests.swift in Sources */,
From 0340381bd318cfdfbcd103123cfee50fb43ea830 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 06:53:53 +0200
Subject: [PATCH 20/46] Discard again non-boxable values
---
Sources/Box.swift | 10 ++++------
Tests/Public/BoxValueTests.swift | 16 ----------------
2 files changed, 4 insertions(+), 22 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index 6da09d35..e74f9670 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -88,11 +88,8 @@ extension MustacheBox {
}
-/// Attempt to turn value into a box.
-///
-/// - parameter object: An object.
-/// - returns: A MustacheBox that wraps *object*.
-func Box(_ value: Any?) -> MustacheBox {
+/// TODO
+public func Box(_ value: Any?) -> MustacheBox {
guard let value = value else {
return EmptyBox
}
@@ -117,7 +114,8 @@ func Box(_ value: Any?) -> MustacheBox {
case let f as KeyedSubscriptFunction:
return MustacheBox(keyedSubscript: f)
default:
- return MustacheBox(value: value, boolValue: true)
+ NSLog("%@", "Mustache warning: \(String(reflecting: value)) of type \(type(of: value)) is not MustacheBoxable, Array, Set, Dictionary, and is discarded.")
+ return EmptyBox
}
}
diff --git a/Tests/Public/BoxValueTests.swift b/Tests/Public/BoxValueTests.swift
index f5892a8e..37ed02a3 100644
--- a/Tests/Public/BoxValueTests.swift
+++ b/Tests/Public/BoxValueTests.swift
@@ -35,12 +35,6 @@ class BoxValueTests: XCTestCase {
}
}
- struct Struct : CustomDebugStringConvertible {
- var debugDescription: String {
- return "Struct"
- }
- }
-
class BoxableClass : MustacheBoxable {
init() {
}
@@ -49,14 +43,6 @@ class BoxValueTests: XCTestCase {
}
}
- class Class : CustomDebugStringConvertible {
- init() {
- }
- var debugDescription: String {
- return "Class"
- }
- }
-
func assert(value: Any?, isBoxedAs: T.Type) {
let template = try! Template(string: "{{#test(value)}}success{{/}}")
let data: [String: Any] = [
@@ -68,9 +54,7 @@ class BoxValueTests: XCTestCase {
XCTAssertEqual(try! template.render(data), "success", "\(String(reflecting: value)) is not boxed as \(T.self)")
}
assert(value: BoxableStruct(), isBoxedAs: BoxableStruct.self)
- assert(value: Struct(), isBoxedAs: Struct.self)
assert(value: BoxableClass(), isBoxedAs: BoxableClass.self)
- assert(value: Class(), isBoxedAs: Class.self)
assert(value: NSObject(), isBoxedAs: NSObject.self)
assert(value: NSDate(), isBoxedAs: NSDate.self)
assert(value: Date(), isBoxedAs: Date.self)
From 42e0d2a211983a7bba13f5aac756fcc46acb021d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 06:54:02 +0200
Subject: [PATCH 21/46] Fix Logger initializer
---
Sources/Logger.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/Logger.swift b/Sources/Logger.swift
index b557f364..406ab815 100644
--- a/Sources/Logger.swift
+++ b/Sources/Logger.swift
@@ -54,7 +54,7 @@ extension StandardLibrary {
string with NSLog().
- returns: a Logger
*/
- public init(log: ((String) -> Void)? = nil) {
+ public init(_ log: ((String) -> Void)? = nil) {
if let log = log {
self.log = log
} else {
From 62725d81f46a39978b227e579fd3700e293e8269 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 06:54:30 +0200
Subject: [PATCH 22/46] Use String(reflecting:) when debugging strings
---
Sources/MustacheBox.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/MustacheBox.swift b/Sources/MustacheBox.swift
index 599ff769..64730be0 100644
--- a/Sources/MustacheBox.swift
+++ b/Sources/MustacheBox.swift
@@ -562,7 +562,7 @@ extension MustacheBox {
facets.append("[:]")
} else {
let items = dictionary.map { (key, box) in
- return "\(key.debugDescription):\(box.valueDescription)"
+ return "\(String(reflecting: key)):\(box.valueDescription)"
}.joined(separator: ",")
facets.append("[\(items)]")
}
From 9f2738fad1bb3a4977196999ce48f6a5dbf78291 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 06:54:34 +0200
Subject: [PATCH 23/46] Documentation
---
Docs/Guides/goodies.md | 28 ++--
Docs/Playground.playground/section-1.swift | 32 +---
README.md | 166 +++++++++++----------
3 files changed, 104 insertions(+), 122 deletions(-)
diff --git a/Docs/Guides/goodies.md b/Docs/Guides/goodies.md
index 85388f2a..9fbd5190 100644
--- a/Docs/Guides/goodies.md
+++ b/Docs/Guides/goodies.md
@@ -24,11 +24,11 @@ let percentFormatter = NumberFormatter()
percentFormatter.numberStyle = .percent
let template = try! Template(string: "{{ percent(x) }}")
-template.registerInBaseContext("percent", Box(percentFormatter))
+template.register(percentFormatter, forKey: "percent")
// Rendering: 50%
let data = ["x": 0.5]
-let rendering = try! template.render(Box(data))
+let rendering = try! template.render(data)
```
#### Formatting all values in a section
@@ -50,7 +50,7 @@ let percentFormatter = NumberFormatter()
percentFormatter.numberStyle = .percent
let template = try! Template(named: "Document")
-template.registerInBaseContext("percent", Box(percentFormatter))
+template.register(percentFormatter, forKey: "percent")
// Rendering:
//
@@ -63,7 +63,7 @@ let data = [
"daily": 1.5,
"weekly": 4,
]
-let rendering = try! template.render(Box(data))
+let rendering = try! template.render(data)
```
Variable tags buried inside inner sections are escaped as well, so that you can render loop and conditional sections. However, values that can't be formatted are left untouched:
@@ -95,7 +95,7 @@ Usage:
```swift
let template = ...
-template.registerInBaseContext("HTMLEscape", Box(StandardLibrary.HTMLEscape))
+template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
```
As a filter, `HTMLEscape` returns its argument, HTML-escaped.
@@ -132,7 +132,7 @@ Usage:
```swift
let template = ...
-template.registerInBaseContext("javascriptEscape", Box(StandardLibrary.javascriptEscape))
+template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
```
As a filter, `javascriptEscape` outputs a Javascript and JSON-savvy string:
@@ -176,7 +176,7 @@ Usage:
```swift
let template = ...
-template.registerInBaseContext("URLEscape", Box(StandardLibrary.URLEscape))
+template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
```
As a filter, `URLEscape` returns its argument, percent-escaped.
@@ -212,7 +212,7 @@ Usage:
```swift
let template = ...
-template.registerInBaseContext("each", Box(StandardLibrary.each))
+template.register(StandardLibrary.each, forKey: "each")
```
Iteration is natural to Mustache templates: `{{# users }}{{ name }}, {{/ users }}` renders "Alice, Bob, etc." when the `users` key is given a list of users.
@@ -269,7 +269,7 @@ Usage:
```swift
let template = ...
-template.registerInBaseContext("zip", Box(StandardLibrary.zip))
+template.register(StandardLibrary.zip, forKey: "zip")
```
The zip filter iterates several lists all at once. On each step, one object from each input list enters the rendering context, and makes its own keys available for rendering.
@@ -323,7 +323,7 @@ Usage:
let template = ...
let localizer = StandardLibrary.Localizer(bundle: nil, table: nil)
-template.registerInBaseContext("localize", Box(localizer))
+template.register(localizer, forKey: "localize")
```
#### Localizing a value
@@ -368,8 +368,8 @@ Usage:
```swift
let template = ...
-let logger = StandardLibrary.Logger()
-template.extendBaseContext(Box(logger))
+let logger = StandardLibrary.Logger { print($0) }
+template.extendBaseContext(logger)
```
`Logger` is a tool intended for debugging templates.
@@ -384,11 +384,11 @@ let template = try! Template(string: "{{name}} died at {{age}}.")
// Logs all tag renderings with NSLog():
let logger = StandardLibrary.Logger()
-template.extendBaseContext(Box(logger))
+template.extendBaseContext(logger)
// Render
let data = ["name": "Freddy Mercury", "age": 45]
-let rendering = try! template.render(Box(data))
+let rendering = try! template.render(data)
```
In the log:
diff --git a/Docs/Playground.playground/section-1.swift b/Docs/Playground.playground/section-1.swift
index 40ceee51..1151ae5b 100644
--- a/Docs/Playground.playground/section-1.swift
+++ b/Docs/Playground.playground/section-1.swift
@@ -2,31 +2,7 @@
import Mustache
-let percentFormatter = NumberFormatter()
-percentFormatter.numberStyle = .percent
-
-let template = try! Template(string: "{{# percent }}\nhourly: {{ hourly }}\ndaily: {{ daily }}\nweekly: {{ weekly }}\n {{/ percent }}")
-template.register(percentFormatter, forKey: "percent")
-template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
-template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
-template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
-template.register(StandardLibrary.each, forKey: "each")
-template.register(StandardLibrary.zip, forKey: "zip")
-
-let localizer = StandardLibrary.Localizer(bundle: nil, table: nil)
-template.register(localizer, forkey: "localize")
-let logger = StandardLibrary.Logger()
-template.extendBaseContext(Box(logger))
-
-// Rendering:
-//
-// hourly: 10%
-// daily: 150%
-// weekly: 400%
-
-let data = [
- "hourly": 0.1,
- "daily": 1.5,
- "weekly": 4,
-]
-let rendering = try! template.render(Box(data))
+// Renders "Hello Arthur"
+let template = try! Template(string: "Hello {{ name }}")
+let rendering = try! template.render(["name": "Arthur"])
+print(rendering)
diff --git a/README.md b/README.md
index be728a08..6caf2261 100644
--- a/README.md
+++ b/README.md
@@ -38,11 +38,10 @@ GRMustache extends the genuine Mustache language with built-in goodies and exten
Usage
-----
-The library is built around **three main APIs**:
+The library is built around **two main APIs**:
- The `Template(...)` constructor that loads a template.
-- The `Box(...)` function that makes your data able to feed templates.
-- The `Template.render(...)` method that renders.
+- The `Template.render(...)` method that renders your data.
`document.mustache`:
@@ -64,7 +63,7 @@ let template = try Template(named: "document")
// Let template format dates with `{{format(...)}}`
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
-template.registerInBaseContext("format", Box(dateFormatter))
+template.register(dateFormatter, forKey: "format")
// The rendered data
let data: [String: Any] = [
@@ -75,7 +74,7 @@ let data: [String: Any] = [
]
// The rendering: "Hello Arthur..."
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
@@ -167,7 +166,7 @@ Rendering templates:
Feeding templates:
-- [Boxing Values](#boxing-values)
+- [Values](#values)
- [Standard Swift Types Reference](#standard-swift-types-reference)
- [Custom Types](#custom-types)
- [Lambdas](#lambdas)
@@ -235,7 +234,7 @@ Not funny, but they happen. Standard errors of domain NSCocoaErrorDomain, etc. m
```swift
do {
let template = try Template(named: "Document")
- let rendering = try template.render(Box(data))
+ let rendering = try template.render(data)
} catch let error as MustacheError {
// Parse error at line 2 of template /path/to/template.mustache:
// Unclosed Mustache tag.
@@ -283,7 +282,7 @@ let template = try Template(string: "{{value}} - {{{value}}}")
// Mario & Luigi - Mario & Luigi
let data = ["value": "Mario & Luigi"]
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
@@ -315,10 +314,10 @@ For example:
let template = try Template(string: "<{{#value}}Truthy{{/value}}>")
// ""
-try template.render(Box(["value": true]))
+try template.render(["value": true])
// "<>"
-try template.render(Box([:])) // missing value
-try template.render(Box(["value": false])) // false boolean
+try template.render([:]) // missing value
+try template.render(["value": false]) // false boolean
```
@@ -571,11 +570,9 @@ let partial2 = try Template(string: "{{occupation}}")
// Two different renderings of the same template:
// "Georges Brassens"
-let data1: [String: Any] = ["user": user, "partial": partial1]
-try template.render(Box(data1))
+try template.render(["user": user, "partial": partial1])
// "Singer"
-let data2: [String: Any] = ["user": user, "partial": partial2]
-try template.render(Box(data2))
+try template.render(["user": user, "partial": partial2])
```
@@ -627,7 +624,7 @@ let data = [
"author": "John Doe"
]
]
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
The rendering is a full HTML page:
@@ -741,22 +738,32 @@ The context stack is usually initialized with the data you render your template
```swift
// The rendering starts with a context stack containing `data`
-template.render(Box(data))
+template.render(data)
```
Precisely speaking, a template has a *base context stack* on top of which the rendered data is added. This base context is always available whatever the rendered data. For example:
```swift
// The base context contains `baseData`
-template.extendBaseContext(Box(baseData))
+template.extendBaseContext(baseData)
// The rendering starts with a context stack containing `baseData` and `data`
-template.render(Box(data))
+template.render(data)
```
-The base context is usually a good place to register [filters](#filters).
+The base context is usually a good place to register [filters](#filters):
-See [Template.swift](Sources/Template.swift) for more information on the base context ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html)).
+```swift
+template.extendBaseContext(["each": StandardLibrary.each])
+```
+
+But you will generally register filters with the `register(:forKey:)` method, because it prevents the rendered data from overriding the name of the filter:
+
+```swift
+template.register(StandardLibrary.each, forKey: "each")
+```
+
+See [Template](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html) for more information on the base context.
### Expressions
@@ -798,26 +805,24 @@ There are four kinds of expressions:
[Filters](#filters) are introduced below.
-Boxing Values
--------------
+Values
+------
-In all examples above, all values rendered by templates were wrapped by the `Box()` function:
+Templates render values:
```swift
-template.render(Box(["name": "Luigi"]))
-template.render(Box(profile))
+template.render(["name": "Luigi"])
+template.render(profile)
```
-This boxing is required by the rendering engine. Some types can be boxed and rendered, some can not, and some require some help.
+You can feed templates with:
-The types that can feed templates are:
+- Values that adopt the `MustacheBoxable` protocol such as `String`, `Int`, `NSObject` and its subclasses (see [Standard Swift Types Reference](#standard-swift-types-reference) and [Custom Types](#custom-types))
-- All types conforming to `MustacheBoxable` such as `String`, `Int`, `NSObject` and its subclasses (see [Standard Swift Types Reference](#standard-swift-types-reference) and [Custom Types](#custom-types))
-
- Arrays, sets, and dictionaries (`[Any?]`, `Set`, `[AnyHashable: Any?]`, NSArray, NSSet and NSDictionary). *This does not include other sequences and collections, such as Swift ranges.*
-
+
- A few function types such as [filter functions](#filters), [lambdas](#lambdas), and other functions involved in [advanced boxes](#advanced-boxes).
-
+
- [Goodies](Docs/Guides/goodies.md) such as Foundation's formatters.
@@ -859,11 +864,11 @@ let percentFormatter = NumberFormatter()
percentFormatter.numberStyle = .percent
let template = try Template(string: "{{ percent(x) }}")
-template.registerInBaseContext("percent", Box(percentFormatter))
+template.register(percentFormatter, forKey: "percent")
// Rendering: 50%
let data = ["x": 0.5]
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
[More info on Formatter](Docs/Guides/goodies.md#formatter).
@@ -963,7 +968,7 @@ class Person : NSObject {
// Charlie Chaplin has a mustache.
let person = Person(name: "Charlie Chaplin")
let template = try Template(string: "{{name}} has a mustache.")
-let rendering = try template.render(Box(person))
+let rendering = try template.render(person)
```
When extracting values from your NSObject subclasses, GRMustache.swift uses the [Key-Value Coding](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html) method `valueForKey:`, as long as the key is "safe" (safe keys are the names of declared properties, including NSManagedObject attributes).
@@ -987,20 +992,22 @@ To let Mustache templates extract the `name` key out of a person so that they ca
```swift
extension Person : MustacheBoxable {
- // Here we simply feed templates with a dictionary:
+ // Feed templates with a dictionary:
var mustacheBox: MustacheBox {
return Box(["name": self.name])
}
}
```
-Now we can box and render users, arrays of users, dictionaries of users, etc:
+Your `mustacheBox` implementation will generally call the `Box` function on a regular [value](#values) that itself adopts the `MustacheBoxable` protocol (such as `String` or `Int`), or an array, a set, or a dictionary.
+
+Now we can render users, arrays of users, dictionaries of users, etc:
```swift
// Freddy Mercury has a mustache.
let person = Person(name: "Freddy Mercury")
let template = try Template(string: "{{name}} has a mustache.")
-let rendering = try template.render(Box(person))
+let rendering = try template.render(person)
```
Boxing a dictionary is an easy way to build a box. However there are many kinds of boxes: check the rest of this documentation.
@@ -1027,7 +1034,7 @@ let data: [String: Any] = [
"lastName": "Zappa",
"fullName": fullName,
"wrapped": wrapped]
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
Lambdas are a special case of custom rendering functions. The raw `RenderFunction` type gives you extra flexibility when you need to perform custom rendering. See [CoreFunctions.swift](Sources/CoreFunctions.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Typealiases.html)).
@@ -1045,7 +1052,7 @@ Generally speaking, using filters is a three-step process:
let uppercase = Filter(...)
// 2. Assign a name to your filter, and register it in a template:
-template.registerInBaseContext("uppercase", Box(uppercase))
+template.register(uppercase, forKey: "uppercase")
// 3. Render
template.render(...)
@@ -1071,21 +1078,21 @@ For example, here is a `square` filter which squares integers:
// square(n) evaluates to the square of the provided integer.
let square = Filter { (n: Int?) in
guard let n = n else {
- // No value, or not an integer: return the empty box.
+ // No value, or not an integer: return nil.
// We could throw an error as well.
- return Box()
+ return nil
}
- // Return a boxed result:
- return Box(n * n)
+ // Return the result
+ return n * n
}
// Register the square filter in our template:
let template = try Template(string: "{{n}} Ă— {{n}} = {{square(n)}}")
-template.registerInBaseContext("square", Box(square))
+template.register(square, forKey:"square")
// 10 Ă— 10 = 100
-let rendering = try template.render(Box(["n": 10]))
+let rendering = try template.render(["n": 10])
```
@@ -1098,11 +1105,11 @@ Filters can accept a precisely typed argument as above. You may prefer managing
let absFilter = Filter { (box: MustacheBox) in
switch box.value {
case let int as Int:
- return Box(abs(int))
+ return abs(int)
case let double as Double:
- return Box(abs(double))
+ return abs(double)
default:
- return Box()
+ return nil
}
}
```
@@ -1120,7 +1127,7 @@ let oneEveryTwoItems = Filter { (box: MustacheBox) in
// (Array, Set, NSArray, etc.).
guard let boxes = box.arrayValue else {
// No value, or not a collection: return the empty box
- return Box()
+ return nil
}
// Rebuild another array with even indexes:
@@ -1129,7 +1136,7 @@ let oneEveryTwoItems = Filter { (box: MustacheBox) in
result.append(box)
}
- return Box(result)
+ return result
}
// A template where the filter is used in a section, so that the items in the
@@ -1138,10 +1145,10 @@ let templateString = "{{# oneEveryTwoItems(items) }}<{{.}}>{{/ oneEveryTwoItems(
let template = try Template(string: templateString)
// Register the oneEveryTwoItems filter in our template:
-template.registerInBaseContext("oneEveryTwoItems", Box(oneEveryTwoItems))
+template.register(oneEveryTwoItems, forKey: "oneEveryTwoItems")
// <1><3><5><7><9>
-let rendering = try template.render(Box(["items": Array(1..<10)]))
+let rendering = try template.render(["items": Array(1..<10)])
```
@@ -1156,15 +1163,15 @@ let sum = VariadicFilter { (boxes: [MustacheBox]) in
for box in boxes {
sum += (box.value as? Int) ?? 0
}
- return Box(sum)
+ return sum
}
// Register the sum filter in our template:
let template = try Template(string: "{{a}} + {{b}} + {{c}} = {{ sum(a,b,c) }}")
-template.registerInBaseContext("sum", Box(sum))
+template.register(sum, forKey: "sum")
// 1 + 2 + 3 = 6
-let rendering = try template.render(Box(["a": 1, "b": 2, "c": 3]))
+let rendering = try template.render(["a": 1, "b": 2, "c": 3])
```
@@ -1180,11 +1187,11 @@ let percentFormatter = NumberFormatter()
percentFormatter.numberStyle = .percent
let template = try Template(string: "{{ percent(x) }}")
-template.registerInBaseContext("percent", Box(percentFormatter))
+template.register(percentFormatter, forKey: "percent")
// Rendering: 50%
let data = ["x": 0.5]
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
[More info on formatters](Docs/Guides/goodies.md#formatter).
@@ -1207,13 +1214,13 @@ let reverse = Filter { (rendering: Rendering) in
// Register the reverse filter in our template:
let template = try Template(string: "{{reverse(value)}}")
-template.registerInBaseContext("reverse", Box(reverse))
+template.register(reverse, forKey: "reverse")
// ohcuorG
-try template.render(Box(["value": "Groucho"]))
+try template.render(["value": "Groucho"])
// 321
-try template.render(Box(["value": 123]))
+try template.render(["value": 123])
```
Such filter does not quite process a raw string, as you have seen. It processes a `Rendering`, which is a flavored string, a string with its contentType (text or HTML).
@@ -1222,7 +1229,7 @@ This rendering will usually be text: simple values (ints, strings, etc.) render
```swift
// >lmth<
-try template.render(Box(["value": ""]))
+try template.render(["value": ""])
```
@@ -1251,11 +1258,11 @@ let pluralize = Filter { (count: Int?, info: RenderingInfo) in
// Register the pluralize filter in our template:
let templateString = "I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}."
let template = try Template(string: templateString)
-template.registerInBaseContext("pluralize", Box(pluralize))
+template.register(pluralize, forKey: "pluralize")
// I have 3 cats.
let data = ["cats": ["Kitty", "Pussy", "Melba"]]
-let rendering = try template.render(Box(data))
+let rendering = try template.render(data)
```
As those filters perform custom rendering, they are based on `RenderFunction`, just like [lambdas](#lambdas). Check the `RenderFunction` type in [CoreFunctions.swift](Sources/CoreFunctions.swift) for more information about the `RenderingInfo` and `Rendering` types ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Typealiases.html)).
@@ -1271,16 +1278,16 @@ Yet the library ships with a few built-in filters that don't quite fit any of th
Advanced Boxes
--------------
-The `MustacheBox` type that feeds templates is able to wrap many different behaviors. Let's review some of them:
+Values that feed templates are able of many different behaviors. Let's review some of them:
-- `Box(Bool)` returns a box that can trigger or prevent the rendering of sections:
+- `Bool` can trigger or prevent the rendering of sections:
```
{{# verified }}VERIFIED{{/ verified }}
{{^ verified }}NOT VERIFIED{{/ verified }}
```
-- `Box(Array)` returns a box that renders a section as many times as there are elements in the array, and exposes the `count`, `first`, and `last` keys:
+- Arrays render sections multiple times, and expose the `count`, `first`, and `last` keys:
```
You see {{ objects.count }} objects:
@@ -1289,7 +1296,7 @@ The `MustacheBox` type that feeds templates is able to wrap many different behav
{{/ objects }}
```
-- `Box(Dictionary)` returns a box that exposes all the dictionary keys:
+- Dictionaries expose all their keys:
```
{{# user }}
@@ -1298,7 +1305,7 @@ The `MustacheBox` type that feeds templates is able to wrap many different behav
{{/ user }}
```
-- `Box(NSObject)` returns a box that exposes all the object properties:
+- NSObject exposes all its properties:
```
{{# user }}
@@ -1307,16 +1314,13 @@ The `MustacheBox` type that feeds templates is able to wrap many different behav
{{/ user }}
```
-- `Box(Formatter)` returns a box that is able to format a single value, or all values inside a section ([more information](Docs/Guides/goodies.md#formatter)):
+- Foundation's Formatter is able to format values ([more information](Docs/Guides/goodies.md#formatter)):
```
{{ format(date) }}
- {{# format }}
- From {{ startDate }} to {{ endDate }}
- {{/ format }}
```
-- `Box(StandardLibrary.each)` returns a box that is able to define some extra keys when iterating an array ([more information](Docs/Guides/goodies.md#localizer)):
+- `StandardLibrary.each` is a filter that defines some extra keys when iterating an array ([more information](Docs/Guides/goodies.md#each)):
```
{{# each(items) }}
@@ -1324,7 +1328,9 @@ The `MustacheBox` type that feeds templates is able to wrap many different behav
{{/}}
```
-This variety of behaviors is available through public APIs. Before we dig into them, let's describe in detail the rendering of the `{{ F(A) }}` tag, and shed some light on the available customizations:
+This variety of behaviors is made possible by the `MustacheBox` type. Whenever a value, array, filter, etc. feeds a template, it is turned into a box that interact with the rendering engine.
+
+Let's describe in detail the rendering of the `{{ F(A) }}` tag, and shed some light on the available customizations:
1. The `A` and `F` expressions are evaluated: the rendering engine looks in the [context stack](#the-context-stack) for boxes that return a non-empty box for the keys "A" and "F". The key-extraction service is provided by a customizable `KeyedSubscriptFunction`.
@@ -1350,7 +1356,7 @@ This variety of behaviors is available through public APIs. Before we dig into t
This one is used by [Localizer](Docs/Guides/goodies.md#localizer) and [Logger](Docs/Guides/goodies.md#logger) goodies.
-All those customizable properties are not exposed through a Box() function, but instead gathered by the low-level MustacheBox initializer:
+All those customizable properties are exposed in the low-level MustacheBox initializer:
```swift
// MustacheBox initializer
@@ -1376,7 +1382,7 @@ We'll below describe each of them individually, even though you can provide seve
// Renders "1"
let template = try Template(string: "{{a}}")
- try template.render(Box(["a": aBox]))
+ try template.render(["a": aBox])
```
- `boolValue`
@@ -1416,12 +1422,12 @@ We'll below describe each of them individually, even though you can provide seve
```swift
let box = MustacheBox(filter: Filter { (x: Int?) in
- return Box(x! * x!)
+ return x! * x!
})
// Renders "100"
let template = try Template(string:"{{square(x)}}")
- try template.render(Box(["square": box, "x": Box(10)]))
+ try template.render(["square": box, "x": Box(10)])
```
- `render`
@@ -1454,7 +1460,7 @@ We'll below describe each of them individually, even though you can provide seve
```swift
let box = MustacheBox(willRender: { (tag: Tag, box: MustacheBox) in
- return Box("baz")
+ return "baz"
})
// Renders "baz baz"
From fc86e805d2eeea1b4c8876220216ce64ab715f58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 06:56:05 +0200
Subject: [PATCH 24/46] Documentation
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6caf2261..38916d54 100644
--- a/README.md
+++ b/README.md
@@ -812,7 +812,7 @@ Templates render values:
```swift
template.render(["name": "Luigi"])
-template.render(profile)
+template.render(Profile(named: "Luigi"))
```
You can feed templates with:
From 9f57161bcf2c8b5bd46f7e911b3c4bafe219631f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 07:01:24 +0200
Subject: [PATCH 25/46] Documentation
---
README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 38916d54..df401fe1 100644
--- a/README.md
+++ b/README.md
@@ -1280,11 +1280,11 @@ Advanced Boxes
Values that feed templates are able of many different behaviors. Let's review some of them:
-- `Bool` can trigger or prevent the rendering of sections:
+- Bool can trigger or prevent the rendering of sections:
```
- {{# verified }}VERIFIED{{/ verified }}
- {{^ verified }}NOT VERIFIED{{/ verified }}
+ {{# isVerified }}VERIFIED{{/ isVerified }}
+ {{^ isVerified }}NOT VERIFIED{{/ isVerified }}
```
- Arrays render sections multiple times, and expose the `count`, `first`, and `last` keys:
From f3aba5aa9d7e3c8eda63429e8eac70f77d45853a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 07:18:42 +0200
Subject: [PATCH 26/46] Documentation
---
Sources/Common.swift | 71 ++++++++++++++++----------------------------
1 file changed, 26 insertions(+), 45 deletions(-)
diff --git a/Sources/Common.swift b/Sources/Common.swift
index 9ddf89fb..462f64e8 100644
--- a/Sources/Common.swift
+++ b/Sources/Common.swift
@@ -21,39 +21,29 @@
// THE SOFTWARE.
-// =============================================================================
-// MARK: - ContentType
-
-/**
-GRMustache distinguishes Text from HTML.
-
-Content type applies to both *templates*, and *renderings*:
-
-- In a HTML template, `{{name}}` tags escape Text renderings, but do not escape
-HTML renderings.
-
-- In a Text template, `{{name}}` tags do not escape anything.
-
-The content type of a template comes from `Configuration.contentType` or
-`{{% CONTENT_TYPE:... }}` pragma tags. See the documentation of
-`Configuration.contentType` for a full discussion.
-
-The content type of rendering is discussed with the `Rendering` type.
-
-See also:
-
-- Configuration.contentType
-- Rendering
-*/
+/// GRMustache distinguishes Text from HTML.
+///
+/// Content type applies to both *templates*, and *renderings*:
+///
+/// - In a HTML template, `{{name}}` tags escape Text renderings, but do not
+/// escape HTML renderings.
+///
+/// - In a Text template, `{{name}}` tags do not escape anything.
+///
+/// The content type of a template comes from `Configuration.contentType` or
+/// `{{% CONTENT_TYPE:... }}` pragma tags. See the documentation of
+/// `Configuration.contentType` for a full discussion.
+///
+/// The content type of rendering is discussed with the `Rendering` type.
+///
+/// - seealso: Configuration.contentType
+/// - seealso: Rendering
public enum ContentType {
case text
case html
}
-// =============================================================================
-// MARK: - Errors
-
/// The errors thrown by Mustache.swift
public struct MustacheError : Error {
@@ -160,27 +150,18 @@ extension MustacheError : CustomStringConvertible {
}
-// =============================================================================
-// MARK: - Tag delimiters
-
-/**
-A pair of tag delimiters, such as `("{{", "}}")`.
-
-:see Configuration.tagDelimiterPair
-:see Tag.tagDelimiterPair
-*/
+/// A pair of tag delimiters, such as `("{{", "}}")`.
+///
+/// - seealso: Configuration.tagDelimiterPair
+/// - seealso: Tag.tagDelimiterPair
public typealias TagDelimiterPair = (String, String)
-// =============================================================================
-// MARK: - HTML escaping
-
-/**
-HTML-escapes a string by replacing `<`, `> `, `&`, `'` and `"` with HTML entities.
-
-- parameter string: A string.
-- returns: The HTML-escaped string.
-*/
+/// HTML-escapes a string by replacing `<`, `> `, `&`, `'` and `"` with
+/// HTML entities.
+///
+/// - parameter string: A string.
+/// - returns: The HTML-escaped string.
public func escapeHTML(_ string: String) -> String {
let escapeTable: [Character: String] = [
"<": "<",
From e2141aa2f596d596fe32f611e83023ccc93edf46 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 07:43:43 +0200
Subject: [PATCH 27/46] Documentation
---
Sources/Expression.swift | 14 ++-
Sources/SectionTag.swift | 6 +-
Sources/Tag.swift | 173 ++++++++++++++++++--------------------
Sources/TemplateAST.swift | 38 +++------
Sources/VariableTag.swift | 4 +-
5 files changed, 102 insertions(+), 133 deletions(-)
diff --git a/Sources/Expression.swift b/Sources/Expression.swift
index da35658f..dadbff2c 100644
--- a/Sources/Expression.swift
+++ b/Sources/Expression.swift
@@ -23,10 +23,8 @@
import Foundation
-/**
-The type for expressions that appear in tags: `name`, `user.name`,
-`uppercase(user.name)`, etc.
-*/
+/// The type for expressions that appear in tags: `name`, `user.name`,
+/// `uppercase(user.name)`, etc.
enum Expression {
// {{ . }}
@@ -42,11 +40,9 @@ enum Expression {
indirect case filter(filterExpression: Expression, argumentExpression: Expression, partialApplication: Bool)
}
-/**
-Expression conforms to Equatable so that the Compiler can check that section
-tags have matching openings and closings: {{# person }}...{{/ person }} is OK
-but {{# foo }}...{{/ bar }} is not.
-*/
+/// Expression conforms to Equatable so that the Compiler can check that section
+/// tags have matching openings and closings: {{# person }}...{{/ person }} is
+/// OK but {{# foo }}...{{/ bar }} is not.
extension Expression: Equatable {
}
diff --git a/Sources/SectionTag.swift b/Sources/SectionTag.swift
index d3914d40..13a52f08 100644
--- a/Sources/SectionTag.swift
+++ b/Sources/SectionTag.swift
@@ -23,10 +23,8 @@
import Foundation
-/**
-A SectionTag represents a regular or inverted section tag such as
-{{#section}}...{{/section}} or {{^section}}...{{/section}}.
-*/
+/// A SectionTag represents a regular or inverted section tag such as
+/// {{#section}}...{{/section}} or {{^section}}...{{/section}}.
final class SectionTag: LocatedTag {
let openingToken: TemplateToken
let innerTemplateAST: TemplateAST
diff --git a/Sources/Tag.swift b/Sources/Tag.swift
index 298e7f76..06e8f966 100644
--- a/Sources/Tag.swift
+++ b/Sources/Tag.swift
@@ -26,10 +26,8 @@ import Foundation
// =============================================================================
// MARK: - TagType
-/**
-The type of a tag, variable or section. See the documentation of `Tag` for more
-information.
-*/
+/// The type of a tag, variable or section. See the documentation of `Tag` for
+/// more information.
public enum TagType {
/// The type of tags such as `{{name}}` and `{{{body}}}`.
@@ -43,22 +41,18 @@ public enum TagType {
// =============================================================================
// MARK: - Tag
-/**
-Tag instances represent Mustache tags that render values:
-
-- variable tags: `{{name}}` and `{{{body}}}`
-- section tags: `{{#user}}...{{/user}}`
-
-You may meet the Tag class when you implement your own `RenderFunction`,
-`WillRenderFunction` or `DidRenderFunction`, or filters that perform custom
-rendering (see `FilterFunction`).
-
-See also:
-
-- RenderFunction
-- WillRenderFunction
-- DidRenderFunction
-*/
+/// Tag instances represent Mustache tags that render values:
+///
+/// - variable tags: `{{name}}` and `{{{body}}}`
+/// - section tags: `{{#user}}...{{/user}}`
+///
+/// You may meet the Tag class when you implement your own `RenderFunction`,
+/// `WillRenderFunction` or `DidRenderFunction`, or filters that perform custom
+/// rendering (see `FilterFunction`).
+///
+/// - seealso: RenderFunction
+/// - seealso: WillRenderFunction
+/// - seealso: DidRenderFunction
public protocol Tag: class, CustomStringConvertible {
// IMPLEMENTATION NOTE
@@ -66,86 +60,79 @@ public protocol Tag: class, CustomStringConvertible {
// Tag is a class-only protocol so that the Swift compiler does not crash
// when compiling the `tag` property of RenderingInfo.
- /**
- The type of the tag: variable or section:
-
- let render: RenderFunction = { (info: RenderingInfo) in
- switch info.tag.type {
- case .Variable:
- return Rendering("variable")
- case .Section:
- return Rendering("section")
- }
- }
-
- let template = try! Template(string: "{{object}}, {{#object}}...{{/object}}")
-
- // Renders "variable, section"
- try! template.render(Box(["object": Box(render)]))
- */
+ /// The type of the tag: variable or section:
+ ///
+ /// let render: RenderFunction = { (info: RenderingInfo) in
+ /// switch info.tag.type {
+ /// case .variable:
+ /// return Rendering("variable")
+ /// case .section:
+ /// return Rendering("section")
+ /// }
+ /// }
+ ///
+ /// let template = try! Template(string: "{{object}}, {{#object}}...{{/object}}")
+ ///
+ /// // Renders "variable, section"
+ /// try! template.render(["object": Box(render)])
var type: TagType { get }
- /**
- The literal and unprocessed inner content of the tag.
-
- A section tag such as `{{# person }}Hello {{ name }}!{{/ person }}` returns
- "Hello {{ name }}!".
-
- Variable tags such as `{{ name }}` have no inner content: their inner
- template string is the empty string.
- // {{# pluralize(count) }}...{{/ }} renders the plural form of the section
- // content if the `count` argument is greater than 1.
- let pluralize = Filter { (count: Int?, info: RenderingInfo) in
-
- // Pluralize the inner content of the section tag:
- var string = info.tag.innerTemplateString
- if count > 1 {
- string += "s" // naive
- }
-
- return Rendering(string)
- }
-
- let template = try! Template(string: "I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.")
- template.registerInBaseContext("pluralize", Box(pluralize))
-
- // Renders "I have 3 cats."
- let data = ["cats": ["Kitty", "Pussy", "Melba"]]
- try! template.render(Box(data))
- */
+ /// The literal and unprocessed inner content of the tag.
+ ///
+ /// A section tag such as `{{# person }}Hello {{ name }}!{{/ person }}`
+ /// returns "Hello {{ name }}!".
+ ///
+ /// Variable tags such as `{{ name }}` have no inner content: their inner
+ /// template string is the empty string.
+ ///
+ /// // {{# pluralize(count) }}...{{/ }} renders the plural form of the section
+ /// // content if the `count` argument is greater than 1.
+ /// let pluralize = Filter { (count: Int?, info: RenderingInfo) in
+ ///
+ /// // Pluralize the inner content of the section tag:
+ /// var string = info.tag.innerTemplateString
+ /// if let count = count, count > 1 {
+ /// string += "s" // naive
+ /// }
+ ///
+ /// return Rendering(string)
+ /// }
+ ///
+ /// let template = try! Template(string: "I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.")
+ /// template.register(pluralize, forKey: "pluralize")
+ ///
+ /// // Renders "I have 3 cats."
+ /// let data = ["cats": ["Kitty", "Pussy", "Melba"]]
+ /// try! template.render(data)
var innerTemplateString: String { get }
/// The delimiters of the tag.
var tagDelimiterPair: TagDelimiterPair { get }
- /**
- Returns the rendering of the tag's inner content. All inner tags are
- evaluated with the provided context.
-
- This method does not return a String, but a Rendering value that wraps both
- the rendered string and its content type (HTML or Text).
-
- The contentType is HTML, unless specified otherwise by `Configuration`, or
- a `{{% CONTENT_TYPE:TEXT }}` pragma tag.
-
- // The strong RenderFunction below wraps a section in a HTML tag.
- let strong: RenderFunction = { (info: RenderingInfo) -> Rendering in
- let rendering = try info.tag.render(info.context)
- return Rendering("\(rendering.string)", .HTML)
- }
-
- let template = try! Template(string: "{{#strong}}Hello {{name}}{{/strong}}")
- template.registerInBaseContext("strong", Box(strong))
-
- // Renders "Hello Arthur"
- try! template.render(Box(["name": Box("Arthur")]))
-
- - parameter context: The context stack for evaluating mustache tags.
- - parameter error: If there is a problem rendering the tag, throws an
- error that describes the problem.
- - returns: The rendering of the tag.
-
- */
+ /// Returns the rendering of the tag's inner content. All inner tags are
+ /// evaluated with the provided context.
+ ///
+ /// This method does not return a String, but a Rendering value that wraps
+ /// both the rendered string and its content type (HTML or Text).
+ ///
+ /// The contentType is HTML, unless specified otherwise by `Configuration`,
+ /// or a `{{% CONTENT_TYPE:TEXT }}` pragma tag.
+ ///
+ /// // The strong RenderFunction below wraps a section in a HTML tag.
+ /// let strong: RenderFunction = { (info: RenderingInfo) -> Rendering in
+ /// let rendering = try info.tag.render(info.context)
+ /// return Rendering("\(rendering.string)", .html)
+ /// }
+ ///
+ /// let template = try! Template(string: "{{#strong}}Hello {{name}}{{/strong}}")
+ /// template.register(strong, forKey: "strong")
+ ///
+ /// // Renders "Hello Arthur"
+ /// try! template.render(["name": Box("Arthur")])
+ ///
+ /// - parameter context: The context stack for evaluating mustache tags.
+ /// - throws: An eventual rendering error.
+ /// - returns: The rendering of the tag.
func render(_ context: Context) throws -> Rendering
}
diff --git a/Sources/TemplateAST.swift b/Sources/TemplateAST.swift
index 7f6a4e37..72a15b16 100644
--- a/Sources/TemplateAST.swift
+++ b/Sources/TemplateAST.swift
@@ -21,20 +21,18 @@
// THE SOFTWARE.
-/**
-The abstract syntax tree of a template
-*/
+/// The abstract syntax tree of a template
final class TemplateAST {
- // A template AST can be "defined" or "undefined".
- //
- // Undefined template ASTs are used when parsing templates which embed a
- // partial tag which refers to themselves. The compiler would emit a
- // PartialNode which contains a reference to an undefined (yet) template
- // AST. At the end of the compilation the undefined template AST would
- // become defined.
- //
- // See TemplateRepository.templateAST(named:relativeToTemplateID:error:).
+ /// A template AST can be "defined" or "undefined".
+ ///
+ /// Undefined template ASTs are used when parsing templates which embed a
+ /// partial tag which refers to themselves. The compiler would emit a
+ /// PartialNode which contains a reference to an undefined (yet) template
+ /// AST. At the end of the compilation the undefined template AST would
+ /// become defined.
+ ///
+ /// See TemplateRepository.templateAST(named:relativeToTemplateID:error:).
enum `Type` {
case undefined
case defined(nodes: [TemplateASTNode], contentType: ContentType)
@@ -46,23 +44,17 @@ final class TemplateAST {
}
- /**
- Returns an undefined TemplateAST.
- */
+ /// Creates an undefined TemplateAST.
convenience init() {
self.init(type: Type.undefined)
}
- /**
- Returns a defined TemplateAST.
- */
+ /// Creates a defined TemplateAST.
convenience init(nodes: [TemplateASTNode], contentType: ContentType) {
self.init(type: Type.defined(nodes: nodes, contentType: contentType))
}
- /**
- Returns nil if the template AST is undefined.
- */
+ /// Nil if the template AST is undefined.
var nodes: [TemplateASTNode]! {
switch type {
case .undefined:
@@ -72,9 +64,7 @@ final class TemplateAST {
}
}
- /**
- Returns nil if the template AST is undefined.
- */
+ /// Nil if the template AST is undefined.
var contentType: ContentType! {
switch type {
case .undefined:
diff --git a/Sources/VariableTag.swift b/Sources/VariableTag.swift
index dd910f3d..b79c2c43 100644
--- a/Sources/VariableTag.swift
+++ b/Sources/VariableTag.swift
@@ -23,9 +23,7 @@
import Foundation
-/**
-VariableTag represents a variable tag such as {{name}} or {{{name}}}.
-*/
+/// VariableTag represents a variable tag such as {{name}} or {{{name}}}.
final class VariableTag: LocatedTag {
let token: TemplateToken
let contentType: ContentType
From bc0d50aaa6688917510c39bc69cf870af381e0ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 08:06:57 +0200
Subject: [PATCH 28/46] Documentation
---
Sources/Configuration.swift | 508 +++++++++++++++++-------------------
1 file changed, 240 insertions(+), 268 deletions(-)
diff --git a/Sources/Configuration.swift b/Sources/Configuration.swift
index 24ad3bc6..18b83f7f 100644
--- a/Sources/Configuration.swift
+++ b/Sources/Configuration.swift
@@ -21,80 +21,74 @@
// THE SOFTWARE.
-/**
-Configuration exposes properties that affect both the parsing and the rendering
-of Mustache templates.
-
-
-### What can be configured
-
-Configuration covers:
-
-- **Content type**: HTML templates escape rendered strings, while Text templates
- do not. Text templates are HTML-escaped as a whole when included in HTML
- templates.
-
-- **Context stack**: values stored in a Configuration's context are readily
- available to templates.
-
-- **Tag delimiters**: default Mustache delimiters are `{{` and `}}`. These are
- configurable.
-
-
-### Usage
-
-You setup a configuration *before* loading templates:
-
- // Template loaded later will not HTML-escape the rendered strings:
- Mustache.DefaultConfiguration.contentType = .Text
-
- // A text template
- let template = try! Template(string: "...")
-
-
-### Configuration levels
-
-There are three levels of configuration:
-
-`Mustache.DefaultConfiguration` is a global variable that applies by default:
-
- Mustache.DefaultConfiguration.contentType = .Text
-
- // A text template
- let template = try! Template(named: "Document")
-
-`TemplateRepository.configuration` only applies to templates loaded from the
-template repository:
-
- let repository = TemplateRepository(directoryPath: "/path/to/templates")
- repository.configuration.contentType = .Text
-
- // A text template
- let template = try! repository.template(named: "Document")
-
-Templates can also be configured individually. See the documentation of each
-Configuration method for more details.
-*/
+/// Configuration exposes properties that affect both the parsing and the
+/// rendering of Mustache templates.
+///
+///
+/// ### What can be configured
+///
+/// Configuration covers:
+///
+/// - **Content type**: HTML templates escape rendered strings, while Text
+/// templates do not. Text templates are HTML-escaped as a whole when included
+/// in HTML templates.
+///
+/// - **Context stack**: values stored in a Configuration's context are readily
+/// available to templates.
+///
+/// - **Tag delimiters**: default Mustache delimiters are `{{` and `}}`. These
+/// are configurable.
+///
+///
+/// ### Usage
+///
+/// You setup a configuration *before* loading templates:
+///
+/// // Template loaded later will not HTML-escape the rendered strings:
+/// Mustache.DefaultConfiguration.contentType = .text
+///
+/// // A text template
+/// let template = try! Template(string: "...")
+///
+///
+/// ### Configuration levels
+///
+/// There are three levels of configuration:
+///
+/// `Mustache.DefaultConfiguration` is a global variable that applies
+/// by default:
+///
+/// Mustache.DefaultConfiguration.contentType = .text
+///
+/// // A text template
+/// let template = try! Template(named: "Document")
+///
+/// `TemplateRepository.configuration` only applies to templates loaded from the
+/// template repository:
+///
+/// let repository = TemplateRepository(directoryPath: "/path/to/templates")
+/// repository.configuration.contentType = .text
+///
+/// // A text template
+/// let template = try! repository.template(named: "Document")
+///
+/// Templates can also be configured individually. See the documentation of each
+/// Configuration method for more details.
public struct Configuration {
// =========================================================================
// MARK: - Factory Configuration
- /**
- Returns a factory configuration.
-
- Its contentType is HTML, baseContext empty, tag delimiters `{{` and `}}`.
-
- For example:
-
- // Make sure the template repository uses factory configuration,
- // regardless of changes made to `Mustache.DefaultConfiguration`:
-
- let repository = TemplateRepository(directoryPath: "/path/to/templates")
- repository.configuration = Configuration()
-
-
- */
+ /// Creates a factory configuration.
+ ///
+ /// Its contentType is HTML, baseContext empty, tag delimiters `{{` and `}}`.
+ ///
+ /// For example:
+ ///
+ /// // Make sure the template repository uses factory configuration,
+ /// // regardless of changes made to `Mustache.DefaultConfiguration`:
+ /// let repository = TemplateRepository(directoryPath: "/path/to/templates")
+ /// repository.configuration = Configuration()
public init() {
contentType = .html
baseContext = Context()
@@ -105,185 +99,172 @@ public struct Configuration {
// =========================================================================
// MARK: - Content Type
- /**
- The content type of strings rendered by templates built with this
- configuration.
-
- It affects the HTML-escaping of your data:
-
- - The `.HTML` content type has templates render HTML. This is the default
- behavior. HTML template escape the input of variable tags such as
- `{{name}}`. Use triple mustache tags `{{{content}}}` in order to avoid the
- HTML-escaping.
-
- - The `.Text` content type has templates render text. They do not
- HTML-escape their input: `{{name}}` and `{{{name}}}` have identical,
- non-escaped, renderings.
-
- GRMustache safely keeps track of the content type of templates: should a
- HTML template embed a text template, the content of the text template would
- be HTML-escaped, as a whole.
-
- Setting the contentType of a configuration affects the contentType of all
- templates loaded afterwards:
-
- // Globally, with Mustache.DefaultConfiguration:
-
- Mustache.DefaultConfiguration.contentType = .Text
- let textTemplate = try! Template(named: "Script")
-
- // Locally, using a TemplateRepository:
-
- let repository = TemplateRepository(bundle: NSBundle.mainBundle())
- repository.configuration.contentType = .HTML
- let HTMLTemplate = try! repository.template(named: "HTMLDocument")
-
- In order to set the content type of an individual templates, use pragma tags
- right in the content of your templates:
-
- - `{{% CONTENT_TYPE:TEXT }}` turns a template into a text template.
- - `{{% CONTENT_TYPE:HTML }}` turns a template into a HTML template.
-
- For example:
-
- {{! This template renders a bash script. }}
- {{% CONTENT_TYPE:TEXT }}
- export LANG={{ENV.LANG}}
- ...
-
- These pragmas must be found early in the template (before any value tag).
- Should several pragmas be found in a template content, the last one wins.
- */
+ /// The content type of strings rendered by templates built with this
+ /// configuration.
+ ///
+ /// It affects the HTML-escaping of your data:
+ ///
+ /// - The `.html` content type has templates render HTML. This is the
+ /// default behavior. HTML template escape the input of variable tags such
+ /// as `{{name}}`. Use triple mustache tags `{{{content}}}` in order to
+ /// avoid HTML-escaping.
+ ///
+ /// - The `.text` content type has templates render text. They do not
+ /// HTML-escape their input: `{{name}}` and `{{{name}}}` have identical,
+ /// non-escaped, renderings.
+ ///
+ /// GRMustache safely keeps track of the content type of templates: should a
+ /// HTML template embed a text template, the content of the text template
+ /// would be HTML-escaped, as a whole.
+ ///
+ /// Setting the contentType of a configuration affects the contentType of
+ /// all templates loaded afterwards:
+ ///
+ /// // Globally, with Mustache.DefaultConfiguration:
+ ///
+ /// Mustache.DefaultConfiguration.contentType = .text
+ /// let textTemplate = try! Template(named: "Script")
+ ///
+ /// // Locally, using a TemplateRepository:
+ ///
+ /// let repository = TemplateRepository(bundle: Bundle.main)
+ /// repository.configuration.contentType = .html
+ /// let HTMLTemplate = try! repository.template(named: "HTMLDocument")
+ ///
+ /// In order to set the content type of an individual templates, use pragma tags
+ /// right in the content of your templates:
+ ///
+ /// - `{{% CONTENT_TYPE:TEXT }}` turns a template into a text template.
+ /// - `{{% CONTENT_TYPE:HTML }}` turns a template into a HTML template.
+ ///
+ /// For example:
+ ///
+ /// {{! This template renders a bash script. }}
+ /// {{% CONTENT_TYPE:TEXT }}
+ /// export LANG={{ENV.LANG}}
+ /// ...
+ ///
+ /// These pragmas must be found early in the template (before any value
+ /// tag). Should several pragmas be found in a template content, the last
+ /// one wins.
public var contentType: ContentType
// =========================================================================
// MARK: - Context Stack
- /**
- The base context for templates rendering. All templates built with this
- configuration can access values stored in the base context.
-
- The default base context is empty.
-
- You can set it to some custom context, or extend it with the
- `extendBaseContext` and `registerInBaseContext` methods.
-
- // Globally, with Mustache.DefaultConfiguration:
-
- Mustache.DefaultConfiguration.baseContext = Context(Box(["foo": "bar"]))
-
- // "bar"
- let template1 = try! Template(string: "{{foo}}")
- try! template1.render()
-
- // Locally, using a TemplateRepository:
-
- let repository = TemplateRepository(bundle: NSBundle.mainBundle())
- repository.configuration.baseContext = Context(Box(["foo": "bar"]))
-
- // "bar"
- let template2 = try! repository.template(string: "{{foo}}")
- try! template2.render()
-
- The base context can also be set for individual templates:
-
- let template3 = try! Template(string: "{{foo}}")
- template3.baseContext = Context(Box(["foo": "bar"]))
-
- // "bar"
- try! template3.render()
-
- See also:
-
- - extendBaseContext
- - registerInBaseContext
- */
+ /// The base context for templates rendering. All templates built with this
+ /// configuration can access values stored in the base context.
+ ///
+ /// The default base context is empty.
+ ///
+ /// You can set it to some custom context, or extend it with the
+ /// `extendBaseContext(_)` and `register(_:forKey:)` methods.
+ ///
+ /// // Globally, with Mustache.DefaultConfiguration:
+ ///
+ /// Mustache.DefaultConfiguration.baseContext = Context(["foo": "bar"])
+ ///
+ /// // "bar"
+ /// let template1 = try! Template(string: "{{foo}}")
+ /// try! template1.render()
+ ///
+ /// // Locally, using a TemplateRepository:
+ ///
+ /// let repository = TemplateRepository(bundle: Bundle.main)
+ /// repository.configuration.baseContext = Context(["foo": "bar"])
+ ///
+ /// // "bar"
+ /// let template2 = try! repository.template(string: "{{foo}}")
+ /// try! template2.render()
+ ///
+ /// The base context can also be set for individual templates:
+ ///
+ /// let template3 = try! Template(string: "{{foo}}")
+ /// template3.baseContext = Context(["foo": "bar"])
+ ///
+ /// // "bar"
+ /// try! template3.render()
+ ///
+ /// - seealso: extendBaseContext(_)
+ /// - seealso: register(_:forKey:)
public var baseContext: Context
- /**
- Extends the base context with the provided boxed value. All templates built
- with this configuration can access its keys.
-
- // Globally, with Mustache.DefaultConfiguration:
-
- Mustache.DefaultConfiguration.extendBaseContext(Box(["foo": "bar"]))
-
- // "bar"
- let template1 = try! Template(string: "{{foo}}")
- try! template1.render()
-
- // Locally, using a TemplateRepository:
-
- let repository = TemplateRepository(bundle: NSBundle.mainBundle())
- repository.configuration.extendBaseContext(Box(["foo": "bar"]))
-
- // "bar"
- let template2 = try! repository.template(string: "{{foo}}")
- try! template2.render()
-
- The base context can also be extended for individual templates:
-
- let template3 = try! Template(string: "{{foo}}")
- template3.extendBaseContext(Box(["foo": "bar"]))
-
- // "bar"
- try! template3.render()
-
- - parameter box: The box pushed on the top of the context stack.
-
- See also:
-
- - baseContext
- - registerInBaseContext
- */
+ /// Extends the base context with the provided value. All templates built
+ /// with this configuration can access the value.
+ ///
+ /// // Globally, with Mustache.DefaultConfiguration:
+ ///
+ /// Mustache.DefaultConfiguration.extendBaseContext(["foo": "bar"])
+ ///
+ /// // "bar"
+ /// let template1 = try! Template(string: "{{foo}}")
+ /// try! template1.render()
+ ///
+ /// // Locally, using a TemplateRepository:
+ ///
+ /// let repository = TemplateRepository(bundle: Bundle.main)
+ /// repository.configuration.extendBaseContext(["foo": "bar"])
+ ///
+ /// // "bar"
+ /// let template2 = try! repository.template(string: "{{foo}}")
+ /// try! template2.render()
+ ///
+ /// The base context can also be extended for individual templates:
+ ///
+ /// let template3 = try! Template(string: "{{foo}}")
+ /// template3.extendBaseContext(["foo": "bar"])
+ ///
+ /// // "bar"
+ /// try! template3.render()
+ ///
+ /// - parameter value: The value pushed on the top of the context stack.
+ ///
+ /// - seealso: baseContext
+ /// - seealso: register(_:forKey:)
public mutating func extendBaseContext(_ value: Any?) {
baseContext = baseContext.extendedContext(value)
}
- /**
- Registers a key in the base context. All renderings will be able to access
- the provided box through this key.
-
- Registered keys are looked up first when evaluating Mustache tags.
-
- // Globally, with Mustache.DefaultConfiguration:
-
- Mustache.DefaultConfiguration.registerInBaseContext("foo", Box("bar"))
-
- // Renders "bar"
- let template1 = try! Template(string: "{{foo}}")
- try! template1.render()
-
- // Renders "bar" again, because the registered key "foo" has priority.
- try! template1.render(Box(["foo": "qux"]))
-
- // Locally, using a TemplateRepository:
-
- let repository = TemplateRepository(bundle: NSBundle.mainBundle())
- repository.configuration.registerInBaseContext("foo", Box("bar"))
-
- // "bar"
- let template2 = try! repository.template(string: "{{foo}}")
- try! template2.render()
-
- Keys can also be registered in the base context of individual templates:
-
- let template3 = try! Template(string: "{{foo}}")
- template3.registerInBaseContext("foo", Box("bar"))
-
- // "bar"
- try! template3.render()
-
-
- - parameter key: An identifier.
- - parameter box: The box registered for *key*.
-
- See also:
-
- - baseContext
- - extendBaseContext
- */
+ /// Registers a key/value pair in the base context. All renderings will be
+ /// able to access the provided value through this key.
+ ///
+ /// Registered keys are looked up first when evaluating Mustache tags.
+ ///
+ /// // Globally, with Mustache.DefaultConfiguration:
+ ///
+ /// Mustache.DefaultConfiguration.register("bar", forKey: "foo")
+ ///
+ /// // Renders "bar"
+ /// let template1 = try! Template(string: "{{foo}}")
+ /// try! template1.render()
+ ///
+ /// // Renders "bar" again, because the registered value "bar" has priority.
+ /// try! template1.render(["foo": "qux"])
+ ///
+ /// // Locally, using a TemplateRepository:
+ ///
+ /// let repository = TemplateRepository(bundle: Bundle.main)
+ /// repository.configuration.register("bar", forKey: "foo")
+ ///
+ /// // "bar"
+ /// let template2 = try! repository.template(string: "{{foo}}")
+ /// try! template2.render()
+ ///
+ /// Keys can also be registered in the base context of individual templates:
+ ///
+ /// let template3 = try! Template(string: "{{foo}}")
+ /// template3.register("bar", forKey: "foo")
+ ///
+ /// // "bar"
+ /// try! template3.render()
+ ///
+ ///
+ /// - parameter key: An identifier.
+ /// - parameter value: The value registered for *key*.
+ ///
+ /// - seealso: baseContext
+ /// - seealso: extendBaseContext(_)
public mutating func register(_ value: Any?, forKey key: String) {
baseContext = baseContext.extendedContext(withRegisteredValue: value, forKey: key)
}
@@ -292,43 +273,34 @@ public struct Configuration {
// =========================================================================
// MARK: - Tag delimiters
- /**
- The delimiters for Mustache tags. All templates built with this
- configuration are parsed using those delimiters.
-
- The default value is `("{{", "}}")`.
-
- Setting the tagDelimiterPair of a configuration affects all templates loaded
- afterwards:
-
- // Globally, with Mustache.DefaultConfiguration:
-
- Mustache.DefaultConfiguration.tagDelimiterPair = ("<%", "%>")
- let template1 = try! Template(string: "<% name %>)
-
- // Locally, using a TemplateRepository:
-
- let repository = TemplateRepository()
- repository.configuration.tagDelimiterPair = ("[[", "]]")
- let HTMLTemplate = try! repository.template(string: "[[ name ]]")
-
- You can also change the delimiters right in your templates using a "Set
- Delimiter tag": `{{=[[ ]]=}}` changes delimiters to `[[` and `]]`.
- */
+ /// The delimiters for Mustache tags. All templates built with this
+ /// configuration are parsed using those delimiters.
+ ///
+ /// The default value is `("{{", "}}")`.
+ ///
+ /// Setting the tagDelimiterPair of a configuration affects all templates
+ /// loaded afterwards:
+ ///
+ /// // Globally, with Mustache.DefaultConfiguration:
+ ///
+ /// Mustache.DefaultConfiguration.tagDelimiterPair = ("<%", "%>")
+ /// let template = try! Template(string: "<% name %>)
+ ///
+ /// // Locally, using a TemplateRepository:
+ ///
+ /// let repository = TemplateRepository()
+ /// repository.configuration.tagDelimiterPair = ("[[", "]]")
+ /// let template = try! repository.template(string: "[[ name ]]")
+ ///
+ /// You can also change the delimiters right in your templates using a "Set
+ /// Delimiter tag": `{{=[[ ]]=}}` changes delimiters to `[[` and `]]`.
public var tagDelimiterPair: TagDelimiterPair
-
}
// =============================================================================
// MARK: - Default Configuration
-/**
-The default configuration that is used unless specified otherwise by a
-`TemplateRepository`.
-
-See also:
-
-- TemplateRepository
-*/
+/// The default configuration that is used unless specified otherwise by a
+/// TemplateRepository.
public var DefaultConfiguration = Configuration()
From b53f8cc6b668f1339ccfb9f5399b080750675a83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 09:06:15 +0200
Subject: [PATCH 29/46] Documentation
---
Sources/Formatter.swift | 108 ++++++++++++++++++----------------------
1 file changed, 49 insertions(+), 59 deletions(-)
diff --git a/Sources/Formatter.swift b/Sources/Formatter.swift
index db554315..65827b78 100644
--- a/Sources/Formatter.swift
+++ b/Sources/Formatter.swift
@@ -24,67 +24,57 @@
import Foundation
-/**
-GRMustache lets you use `NSFormatter` to format your values.
-*/
-
+/// Formatter can format template values.
extension Formatter {
- /**
- `NSFormatter` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- formatter.mustacheBox // Valid, but discouraged
- Box(formatter) // Preferred
-
-
- `NSFormatter` can be used as a filter:
-
- let percentFormatter = NSNumberFormatter()
- percentFormatter.numberStyle = .PercentStyle
-
- var template = try! Template(string: "{{ percent(x) }}")
- template.register(percentFormatter, forKey: "percent")
-
- // Renders "50%"
- try! template.render(Box(["x": 0.5]))
-
-
- `NSFormatter` can also format all variable tags in a Mustache section:
-
- template = try! Template(string:
- "{{# percent }}" +
- "{{#ingredients}}" +
- "- {{name}} ({{proportion}})\n" +
- "{{/ingredients}}" +
- "{{/percent}}")
- template.register(percentFormatter, forKey: "percent")
-
- // - bread (50%)
- // - ham (35%)
- // - butter (15%)
- var data = [
- "ingredients":[
- ["name": "bread", "proportion": 0.5],
- ["name": "ham", "proportion": 0.35],
- ["name": "butter", "proportion": 0.15]]]
- try! template.render(Box(data))
-
- As seen in the example above, variable tags buried inside inner sections are
- escaped as well, so that you can render loop and conditional sections.
- However, values that can't be formatted are left untouched.
-
- Precisely speaking, "values that can't be formatted" are the ones for which
- the `-[NSFormatter stringForObjectValue:]` method return nil, as stated by
- NSFormatter documentation https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSFormatter_Class/index.html#//apple_ref/occ/instm/NSFormatter/stringForObjectValue:
-
- Typically, `NSNumberFormatter` only formats numbers, and `NSDateFormatter`,
- dates: you can safely mix various data types in a section controlled by
- those well-behaved formatters.
- */
+ /// Formatter adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ /// Formatter can be used as a filter:
+ ///
+ /// let percentFormatter = NumberFormatter()
+ /// percentFormatter.numberStyle = .percent
+ ///
+ /// var template = try! Template(string: "{{ percent(x) }}")
+ /// template.register(percentFormatter, forKey: "percent")
+ ///
+ /// // Renders "50%"
+ /// try! template.render(["x": 0.5])
+ ///
+ ///
+ /// Formatter can also format all variable tags in a Mustache section:
+ ///
+ /// template = try! Template(string:
+ /// "{{# percent }}" +
+ /// "{{#ingredients}}" +
+ /// "- {{name}} ({{proportion}})\n" +
+ /// "{{/ingredients}}" +
+ /// "{{/percent}}")
+ /// template.register(percentFormatter, forKey: "percent")
+ ///
+ /// // - bread (50%)
+ /// // - ham (35%)
+ /// // - butter (15%)
+ /// var data = [
+ /// "ingredients":[
+ /// ["name": "bread", "proportion": 0.5],
+ /// ["name": "ham", "proportion": 0.35],
+ /// ["name": "butter", "proportion": 0.15]]]
+ /// try! template.render(data)
+ ///
+ /// As seen in the example above, variable tags buried inside inner sections
+ /// are escaped as well, so that you can render loop and conditional
+ /// sections. However, values that can't be formatted are left untouched.
+ ///
+ /// Precisely speaking, "values that can't be formatted" are the ones that
+ /// have the `string(for:)` method return nil, as stated by
+ /// [NSFormatter documentation](https://developer.apple.com/reference/foundation/formatter/1415993-string).
+ ///
+ /// Typically, `NumberFormatter` only formats numbers, and `DateFormatter`,
+ /// dates: you can safely mix various data types in a section controlled by
+ /// those well-behaved formatters.
open override var mustacheBox: MustacheBox {
// Return a multi-facetted box, because NSFormatter interacts in
// various ways with Mustache rendering.
From e29862d81293acc1c6cc09c9d71ee34a4636c4dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 09:35:18 +0200
Subject: [PATCH 30/46] Documentation
---
Sources/HTMLEscapeHelper.swift | 8 +--
Sources/JavascriptEscapeHelper.swift | 10 +--
Sources/Localizer.swift | 103 ++++++++++++---------------
Sources/Logger.swift | 67 ++++++++---------
4 files changed, 83 insertions(+), 105 deletions(-)
diff --git a/Sources/HTMLEscapeHelper.swift b/Sources/HTMLEscapeHelper.swift
index 29f54605..29761267 100644
--- a/Sources/HTMLEscapeHelper.swift
+++ b/Sources/HTMLEscapeHelper.swift
@@ -40,7 +40,7 @@ final class HTMLEscapeHelper : MustacheBoxable {
}
// This function is used for evaluating `HTMLEscape(x)` expressions.
- fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
+ private func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(escapeHTML(rendering.string), rendering.contentType)
}
@@ -49,16 +49,16 @@ final class HTMLEscapeHelper : MustacheBoxable {
//
// It is activated as soon as the formatter enters the context stack, when
// used in a section {{# HTMLEscape }}...{{/ HTMLEscape }}.
- fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
+ private func willRender(_ tag: Tag, box: MustacheBox) -> Any? {
switch tag.type {
case .variable:
// {{ value }}
// We don't know if the box contains a String, so let's escape its
// rendering.
- return Box({ (info: RenderingInfo) -> Rendering in
+ return { (info: RenderingInfo) -> Rendering in
let rendering = try box.render(info)
return try self.filter(rendering)
- })
+ }
case .section:
// {{# value }}...{{/ value }}
// {{^ value }}...{{/ value }}
diff --git a/Sources/JavascriptEscapeHelper.swift b/Sources/JavascriptEscapeHelper.swift
index cf854dfc..140cf04a 100644
--- a/Sources/JavascriptEscapeHelper.swift
+++ b/Sources/JavascriptEscapeHelper.swift
@@ -40,7 +40,7 @@ final class JavascriptEscapeHelper : MustacheBoxable {
}
// This function is used for evaluating `javascriptEscape(x)` expressions.
- fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
+ private func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(JavascriptEscapeHelper.escapeJavascript(rendering.string), rendering.contentType)
}
@@ -49,21 +49,21 @@ final class JavascriptEscapeHelper : MustacheBoxable {
//
// It is activated as soon as the formatter enters the context stack, when
// used in a section {{# javascriptEscape }}...{{/ javascriptEscape }}.
- fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
+ private func willRender(_ tag: Tag, box: MustacheBox) -> Any? {
switch tag.type {
case .variable:
// We don't know if the box contains a String, so let's escape its
// rendering.
- return Box({ (info: RenderingInfo) -> Rendering in
+ return { (info: RenderingInfo) -> Rendering in
let rendering = try box.render(info)
return try self.filter(rendering)
- })
+ }
case .section:
return box
}
}
- fileprivate class func escapeJavascript(_ string: String) -> String {
+ private class func escapeJavascript(_ string: String) -> String {
// This table comes from https://github.com/django/django/commit/8c4a525871df19163d5bfdf5939eff33b544c2e2#django/template/defaultfilters.py
//
// Quoting Malcolm Tredinnick:
diff --git a/Sources/Localizer.swift b/Sources/Localizer.swift
index cfc2787f..4f800ce2 100644
--- a/Sources/Localizer.swift
+++ b/Sources/Localizer.swift
@@ -23,36 +23,34 @@ import Foundation
extension StandardLibrary {
- /**
- StandardLibrary.Localizer provides localization of Mustache sections or
- data.
-
- let localizer = StandardLibrary.Localizer(bundle: nil, table: nil)
- template.registerInBaseContext("localize", Box(localizer))
-
- ### Localizing data:
-
- `{{ localize(greeting) }}` renders `NSLocalizedString(@"Hello", nil)`,
- assuming the `greeting` key resolves to the `Hello` string.
-
- ### Localizing sections:
-
- `{{#localize}}Hello{{/localize}}` renders `NSLocalizedString(@"Hello", nil)`.
-
- ### Localizing sections with arguments:
-
- `{{#localize}}Hello {{name}}{{/localize}}` builds the format string
- `Hello %@`, localizes it with NSLocalizedString, and finally
- injects the name with `String(format:...)`.
-
- ### Localize sections with arguments and conditions:
-
- `{{#localize}}Good morning {{#title}}{{title}}{{/title}} {{name}}{{/localize}}`
- build the format string `Good morning %@" or @"Good morning %@ %@`,
- depending on the presence of the `title` key. It then injects the name, or
- both title and name, with `String(format:...)`, to build the final
- rendering.
- */
+ /// StandardLibrary.Localizer provides localization of Mustache sections
+ /// or data.
+ ///
+ /// let localizer = StandardLibrary.Localizer(bundle: nil, table: nil)
+ /// template.register(localizer, forKey: "localize")
+ ///
+ /// ### Localizing data:
+ ///
+ /// `{{ localize(greeting) }}` renders `NSLocalizedString("Hello", comment: "")`,
+ /// assuming the `greeting` key resolves to the `Hello` string.
+ ///
+ /// ### Localizing sections:
+ ///
+ /// `{{#localize}}Hello{{/localize}}` renders `NSLocalizedString("Hello", comment: "")`.
+ ///
+ /// ### Localizing sections with arguments:
+ ///
+ /// `{{#localize}}Hello {{name}}{{/localize}}` builds the format string
+ /// `Hello %@`, localizes it with NSLocalizedString, and finally
+ /// injects the name with `String(format:...)`.
+ ///
+ /// ### Localize sections with arguments and conditions:
+ ///
+ /// `{{#localize}}Good morning {{#title}}{{title}}{{/title}} {{name}}{{/localize}}`
+ /// build the format string `Good morning %@" or @"Good morning %@ %@`,
+ /// depending on the presence of the `title` key. It then injects the name, or
+ /// both title and name, with `String(format:...)`, to build the final
+ /// rendering.
public final class Localizer : MustacheBoxable {
/// The bundle
public let bundle: Bundle
@@ -60,30 +58,21 @@ extension StandardLibrary {
/// The table
public let table: String?
- /**
- Returns a Localizer.
-
- - parameter bundle: The bundle where to look for localized strings. If
- nil, the main bundle is used.
- - parameter table: The table where to look for localized strings. If
- nil, the default `Localizable.strings` is used.
- - returns: A new Localizer.
- */
- public init(bundle: Bundle?, table: String?) {
+ /// Creates a Localizer.
+ ///
+ /// - parameter bundle: The bundle where to look for localized strings.
+ /// If nil, the main bundle is used.
+ /// - parameter table: The table where to look for localized strings.
+ /// If nil, the default `Localizable.strings` is used.
+ public init(bundle: Bundle? = nil, table: String? = nil) {
self.bundle = bundle ?? Bundle.main
self.table = table
}
- /**
- `Localizer` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- localizer.mustacheBox // Valid, but discouraged
- Box(localizer) // Preferred
- */
+ /// `Localizer` adopts the `MustacheBoxable` protocol so that it can
+ /// feed Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
public var mustacheBox: MustacheBox {
// Return a multi-facetted box, because Localizer interacts in
// various ways with Mustache rendering.
@@ -108,15 +97,15 @@ extension StandardLibrary {
// =====================================================================
// MARK: - Not public
- fileprivate var formatArguments: [String]?
+ private var formatArguments: [String]?
// This function is used for evaluating `localize(x)` expressions.
- fileprivate func filter(_ rendering: Rendering) throws -> Rendering {
+ private func filter(_ rendering: Rendering) throws -> Rendering {
return Rendering(localizedStringForKey(rendering.string), rendering.contentType)
}
// This functionis used to render a {{# localize }}Hello{{/ localize }} section.
- fileprivate func render(_ info: RenderingInfo) throws -> Rendering {
+ private func render(_ info: RenderingInfo) throws -> Rendering {
// Perform a first rendering of the section tag, that will turn
// variable tags into a custom placeholder.
@@ -209,7 +198,7 @@ extension StandardLibrary {
return rendering
}
- fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
+ private func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
switch tag.type {
case .variable:
// {{ value }}
@@ -232,7 +221,7 @@ extension StandardLibrary {
}
}
- fileprivate func didRender(_ tag: Tag, box: MustacheBox, string: String?) {
+ private func didRender(_ tag: Tag, box: MustacheBox, string: String?) {
switch tag.type {
case .variable:
// {{ value }}
@@ -252,11 +241,11 @@ extension StandardLibrary {
}
}
- fileprivate func localizedStringForKey(_ key: String) -> String {
+ private func localizedStringForKey(_ key: String) -> String {
return bundle.localizedString(forKey: key, value:"", table:table)
}
- fileprivate func stringWithFormat(format: String, argumentsArray args:[String]) -> String {
+ private func stringWithFormat(format: String, argumentsArray args:[String]) -> String {
switch args.count {
case 0:
return String(format: format)
diff --git a/Sources/Logger.swift b/Sources/Logger.swift
index 406ab815..c6ea6613 100644
--- a/Sources/Logger.swift
+++ b/Sources/Logger.swift
@@ -23,37 +23,32 @@ import Foundation
extension StandardLibrary {
- /**
- StandardLibrary.Logger is a tool intended for debugging templates.
-
- It logs the rendering of variable and section tags such as `{{name}}` and
- `{{#name}}...{{/name}}`.
-
- To activate logging, add a Logger to the base context of a template:
-
- let template = try! Template(string: "{{name}} died at {{age}}.")
-
- // Logs all tag renderings with NSLog():
- let logger = StandardLibrary.Logger()
- template.extendBaseContext(Box(logger))
-
- // Render
- let data = ["name": "Freddy Mercury", "age": 45]
- let rendering = try! template.render(Box(data))
-
- // In NSLog:
- // {{name}} at line 1 did render "Freddy Mercury" as "Freddy Mercury"
- // {{age}} at line 1 did render 45 as "45"
- */
+ /// StandardLibrary.Logger is a tool intended for debugging templates.
+ ///
+ /// It logs the rendering of variable and section tags such as `{{name}}`
+ /// and `{{#name}}...{{/name}}`.
+ ///
+ /// To activate logging, add a Logger to the base context of a template:
+ ///
+ /// let template = try! Template(string: "{{name}} died at {{age}}.")
+ ///
+ /// // Logs all tag renderings with print:
+ /// let logger = StandardLibrary.Logger() { print($0) }
+ /// template.extendBaseContext(logger)
+ ///
+ /// // Render
+ /// let data = ["name": "Freddy Mercury", "age": 45]
+ /// let rendering = try! template.render(data)
+ ///
+ /// // Prints:
+ /// // {{name}} at line 1 did render "Freddy Mercury" as "Freddy Mercury"
+ /// // {{age}} at line 1 did render 45 as "45"
public final class Logger : MustacheBoxable {
- /**
- Returns a Logger.
-
- - parameter log: A closure that takes a String. Default one logs that
- string with NSLog().
- - returns: a Logger
- */
+ /// Creates a Logger.
+ ///
+ /// - parameter log: A closure that takes a String. Default one logs that
+ /// string with NSLog().
public init(_ log: ((String) -> Void)? = nil) {
if let log = log {
self.log = log
@@ -62,16 +57,10 @@ extension StandardLibrary {
}
}
- /**
- Logger adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- localizer.mustacheBox // Valid, but discouraged
- Box(localizer) // Preferred
- */
+ /// Logger adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
public var mustacheBox: MustacheBox {
return MustacheBox(
willRender: { (tag, box) in
From 58e420b1fd56b149b38980c70c243fcc17342792 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 09:46:40 +0200
Subject: [PATCH 31/46] Documentation
---
Sources/StandardLibrary.swift | 360 ++++++++++++++++------------------
1 file changed, 174 insertions(+), 186 deletions(-)
diff --git a/Sources/StandardLibrary.swift b/Sources/StandardLibrary.swift
index 623c756f..ce254507 100644
--- a/Sources/StandardLibrary.swift
+++ b/Sources/StandardLibrary.swift
@@ -21,201 +21,189 @@
// THE SOFTWARE.
-/**
-The StandardLibrary exposes built-in goodies.
-*/
+/// The StandardLibrary exposes built-in goodies.
public struct StandardLibrary {
- /**
- As a filter, `HTMLEscape` returns its argument, HTML-escaped.
-
-
- {{ HTMLEscape(content) }}
-
-
- When used in a section, `HTMLEscape` escapes all inner variable tags in a section:
-
- {{# HTMLEscape }}
- {{ firstName }}
- {{ lastName }}
- {{/ HTMLEscape }}
-
- Variable tags buried inside inner sections are escaped as well, so that you
- can render loop and conditional sections:
-
- {{# HTMLEscape }}
- {{# items }}
- {{ name }}
- {{/ items }}
- {{/ HTMLEscape }}
-
- ### Usage
-
- let template = ...
- template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
- */
+ /// As a filter, `HTMLEscape` returns its argument, HTML-escaped.
+ ///
+ ///
+ /// {{ HTMLEscape(content) }}
+ ///
+ ///
+ /// When used in a section, `HTMLEscape` escapes all inner variable tags in a section:
+ ///
+ /// {{# HTMLEscape }}
+ /// {{ firstName }}
+ /// {{ lastName }}
+ /// {{/ HTMLEscape }}
+ ///
+ /// Variable tags buried inside inner sections are escaped as well, so that
+ /// you can render loop and conditional sections:
+ ///
+ /// {{# HTMLEscape }}
+ /// {{# items }}
+ /// {{ name }}
+ /// {{/ items }}
+ /// {{/ HTMLEscape }}
+ ///
+ /// ### Usage
+ ///
+ /// let template = ...
+ /// template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
public static let HTMLEscape: MustacheBoxable = HTMLEscapeHelper()
- /**
- As a filter, `URLEscape` returns its argument, percent-escaped.
-
- ...
-
- When used in a section, `URLEscape` escapes all inner variable tags in a
- section:
-
- {{# URLEscape }}
- ...
- {{/ URLEscape }}
-
- Variable tags buried inside inner sections are escaped as well, so that you
- can render loop and conditional sections:
-
- {{# URLEscape }}
- ...
- {{/ URLEscape }}
-
- ### Usage
-
- let template = ...
- template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
- */
+ /// As a filter, `URLEscape` returns its argument, percent-escaped.
+ ///
+ /// ...
+ ///
+ /// When used in a section, `URLEscape` escapes all inner variable tags in a
+ /// section:
+ ///
+ /// {{# URLEscape }}
+ /// ...
+ /// {{/ URLEscape }}
+ ///
+ /// Variable tags buried inside inner sections are escaped as well, so that
+ /// you can render loop and conditional sections:
+ ///
+ /// {{# URLEscape }}
+ /// ...
+ /// {{/ URLEscape }}
+ ///
+ /// ### Usage
+ ///
+ /// let template = ...
+ /// template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
public static let URLEscape: MustacheBoxable = URLEscapeHelper()
- /**
- As a filter, `javascriptEscape` outputs a Javascript and JSON-savvy string:
-
-
-
- When used in a section, `javascriptEscape` escapes all inner variable tags
- in a section:
-
-
-
- Variable tags buried inside inner sections are escaped as well, so that you
- can render loop and conditional sections:
-
-
-
- ### Usage
-
- let template = ...
- template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
- */
+ /// As a filter, `javascriptEscape` outputs a Javascript and JSON-savvy string:
+ ///
+ ///
+ ///
+ /// When used in a section, `javascriptEscape` escapes all inner variable
+ /// tags in a section:
+ ///
+ ///
+ ///
+ /// Variable tags buried inside inner sections are escaped as well, so that
+ /// you can render loop and conditional sections:
+ ///
+ ///
+ ///
+ /// ### Usage
+ ///
+ /// let template = ...
+ /// template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
public static let javascriptEscape: MustacheBoxable = JavascriptEscapeHelper()
- /**
- Iteration is natural to Mustache templates:
- `{{# users }}{{ name }}, {{/ users }}` renders "Alice, Bob, etc." when the
- `users` key is given a list of users.
-
- The `each` filter gives you some extra keys:
-
- - `@index` contains the 0-based index of the item (0, 1, 2, etc.)
- - `@indexPlusOne` contains the 1-based index of the item (1, 2, 3, etc.)
- - `@indexIsEven` is true if the 0-based index is even.
- - `@first` is true for the first item only.
- - `@last` is true for the last item only.
-
- Given the following template:
-
- One line per user:
- {{# each(users) }}
- - {{ @index }}: {{ name }}
- {{/}}
-
- Comma-separated user names:
- {{# each(users) }}{{ name }}{{^ @last }}, {{/}}{{/}}.
-
- The rendering reads:
-
- One line per user:
- - 0: Alice
- - 1: Bob
- - 2: Craig
-
- Comma-separated user names: Alice, Bob, Craig.
-
- When provided with a dictionary, `each` iterates each key/value pair of the
- dictionary, stores the key in `@key`, and sets the value as the current
- context:
-
- {{# each(dictionary) }}
- - {{ @key }}: {{.}}
- {{/}}
-
- Renders:
-
- - name: Alice
- - score: 200
- - level: 5
-
- The other positional keys `@index`, `@first`, etc. are still available when
- iterating dictionaries.
-
- ### Usage
-
- let template = ...
- template.register(StandardLibrary.each, forKey: "each")
- */
+ /// Iteration is natural to Mustache templates:
+ /// `{{# users }}{{ name }}, {{/ users }}` renders "Alice, Bob, etc." when the
+ /// `users` key is given a list of users.
+ ///
+ /// The `each` filter gives you some extra keys:
+ ///
+ /// - `@index` contains the 0-based index of the item (0, 1, 2, etc.)
+ /// - `@indexPlusOne` contains the 1-based index of the item (1, 2, 3, etc.)
+ /// - `@indexIsEven` is true if the 0-based index is even.
+ /// - `@first` is true for the first item only.
+ /// - `@last` is true for the last item only.
+ ///
+ /// Given the following template:
+ ///
+ /// One line per user:
+ /// {{# each(users) }}
+ /// - {{ @index }}: {{ name }}
+ /// {{/}}
+ ///
+ /// Comma-separated user names:
+ /// {{# each(users) }}{{ name }}{{^ @last }}, {{/}}{{/}}.
+ ///
+ /// The rendering reads:
+ ///
+ /// One line per user:
+ /// - 0: Alice
+ /// - 1: Bob
+ /// - 2: Craig
+ ///
+ /// Comma-separated user names: Alice, Bob, Craig.
+ ///
+ /// When provided with a dictionary, `each` iterates each key/value pair of the
+ /// dictionary, stores the key in `@key`, and sets the value as the current
+ /// context:
+ ///
+ /// {{# each(dictionary) }}
+ /// - {{ @key }}: {{.}}
+ /// {{/}}
+ ///
+ /// Renders:
+ ///
+ /// - name: Alice
+ /// - score: 200
+ /// - level: 5
+ ///
+ /// The other positional keys `@index`, `@first`, etc. are still available when
+ /// iterating dictionaries.
+ ///
+ /// ### Usage
+ ///
+ /// let template = ...
+ /// template.register(StandardLibrary.each, forKey: "each")
public static let each = EachFilter
- /**
- The zip filter iterates several lists all at once. On each step, one object
- from each input list enters the rendering context, and makes its own keys
- available for rendering.
-
- Given the Mustache template:
-
- {{# zip(users, teams, scores) }}
- - {{ name }} ({{ team }}): {{ score }} points
- {{/}}
-
- The following JSON input:
-
- {
- "users": [
- { "name": "Alice" },
- { "name": "Bob" },
- ],
- "teams": [
- { "team": "iOS" },
- { "team": "Android" },
- ],
- "scores": [
- { "score": 100 },
- { "score": 200 },
- ]
- }
-
- The rendering is:
-
- - Alice (iOS): 100 points
- - Bob (Android): 200 points
-
- In the example above, the first step has consumed (Alice, iOS and 100), and
- the second one (Bob, Android and 200).
-
- The zip filter renders a section as many times as there are elements in the
- longest of its argument: exhausted lists simply do not add anything to the
- rendering context.
-
- ### Usage
-
- let template = ...
- template.register(StandardLibrary.zip, forKey: "zip")
- */
+ /// The zip filter iterates several lists all at once. On each step, one object
+ /// from each input list enters the rendering context, and makes its own keys
+ /// available for rendering.
+ ///
+ /// Given the Mustache template:
+ ///
+ /// {{# zip(users, teams, scores) }}
+ /// - {{ name }} ({{ team }}): {{ score }} points
+ /// {{/}}
+ ///
+ /// The following JSON input:
+ ///
+ /// {
+ /// "users": [
+ /// { "name": "Alice" },
+ /// { "name": "Bob" },
+ /// ],
+ /// "teams": [
+ /// { "team": "iOS" },
+ /// { "team": "Android" },
+ /// ],
+ /// "scores": [
+ /// { "score": 100 },
+ /// { "score": 200 },
+ /// ]
+ /// }
+ ///
+ /// The rendering is:
+ ///
+ /// - Alice (iOS): 100 points
+ /// - Bob (Android): 200 points
+ ///
+ /// In the example above, the first step has consumed (Alice, iOS and 100), and
+ /// the second one (Bob, Android and 200).
+ ///
+ /// The zip filter renders a section as many times as there are elements in the
+ /// longest of its argument: exhausted lists simply do not add anything to the
+ /// rendering context.
+ ///
+ /// ### Usage
+ ///
+ /// let template = ...
+ /// template.register(StandardLibrary.zip, forKey: "zip")
public static let zip = ZipFilter
}
From 74f6c2737456c253ec1639b20784100fde81956a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 10:02:56 +0200
Subject: [PATCH 32/46] Documentation
---
Sources/Box.swift | 245 +++++++++++++++++-----------------
Sources/URLEscapeHelper.swift | 7 +-
Sources/ZipFilter.swift | 21 ++-
3 files changed, 138 insertions(+), 135 deletions(-)
diff --git a/Sources/Box.swift b/Sources/Box.swift
index e74f9670..819d115e 100644
--- a/Sources/Box.swift
+++ b/Sources/Box.swift
@@ -26,8 +26,7 @@ import Foundation
/// The MustacheBoxable protocol gives any type the ability to feed Mustache
/// templates.
///
-/// It is adopted by the standard types Bool, Int, UInt, Double, String, and
-/// NSObject.
+/// It is adopted by the standard types Bool, Int, Double, String, NSObject...
///
/// Your own types can conform to it as well, so that they can feed templates:
///
@@ -35,20 +34,14 @@ import Foundation
///
/// let profile = ...
/// let template = try! Template(named: "Profile")
-/// let rendering = try! template.render(Box(profile))
+/// let rendering = try! template.render(profile)
public protocol MustacheBoxable {
- /// You should not directly call the `mustacheBox` property. Always use the
- /// `Box()` function instead:
- ///
- /// value.mustacheBox // Valid, but discouraged
- /// Box(value) // Preferred
- ///
/// Returns a `MustacheBox` that describes how your type interacts with the
/// rendering engine.
///
/// You can for example box another value that is already boxable, such as
- /// dictionaries:
+ /// a dictionary:
///
/// struct Person {
/// let firstName: String
@@ -71,10 +64,7 @@ public protocol MustacheBoxable {
///
/// // Renders "Tom Selleck"
/// let template = try! Template(string: "{{person.fullName}}")
- /// try! template.render(Box(["person": Box(person)]))
- ///
- /// However, there are multiple ways to build a box, several `Box()`
- /// functions. See their documentations.
+ /// try! template.render(["person": person])
var mustacheBox: MustacheBox { get }
}
@@ -88,7 +78,128 @@ extension MustacheBox {
}
-/// TODO
+/// Returns a MustacheBox that allows *value* to feed Mustache templates.
+///
+/// The returned box depends on the type of the value:
+///
+///
+/// ## MustacheBoxable
+///
+/// For values that adopt the MustacheBoxable protocol, the `Box` function
+/// returns their mustacheBox property.
+///
+///
+/// ## Arrays
+///
+/// Arrays can feed Mustache templates.
+///
+/// let array = [1,2,3]
+///
+/// // Renders "123"
+/// let template = try! Template(string: "{{#array}}{{.}}{{/array}}")
+/// try! template.render(["array": array])
+///
+///
+/// ### Rendering
+///
+/// - `{{array}}` renders the concatenation of the array items.
+///
+/// - `{{#array}}...{{/array}}` renders as many times as there are items in
+/// `array`, pushing each item on its turn on the top of the context stack.
+///
+/// - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
+///
+///
+/// ### Keys exposed to templates
+///
+/// An array can be queried for the following keys:
+///
+/// - `count`: number of elements in the array
+/// - `first`: the first object in the array
+/// - `last`: the last object in the array
+///
+/// Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders
+/// once, if and only if `array` is not empty.
+///
+///
+/// ## Sets
+///
+/// Sets can feed Mustache templates.
+///
+/// let set:Set = [1,2,3]
+///
+/// // Renders "132", or "231", etc.
+/// let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
+/// try! template.render(["set": set])
+///
+///
+/// ### Rendering
+///
+/// - `{{set}}` renders the concatenation of the set items.
+///
+/// - `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
+/// pushing each item on its turn on the top of the context stack.
+///
+/// - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
+///
+///
+/// ### Keys exposed to templates
+///
+/// A set can be queried for the following keys:
+///
+/// - `count`: number of elements in the set
+/// - `first`: the first object in the set
+///
+/// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once,
+/// if and only if `set` is not empty.
+///
+///
+/// ## Dictionaries
+///
+/// A dictionary can feed Mustache templates.
+///
+/// let dictionary: [String: String] = [
+/// "firstName": "Freddy",
+/// "lastName": "Mercury"]
+///
+/// // Renders "Freddy Mercury"
+/// let template = try! Template(string: "{{firstName}} {{lastName}}")
+/// let rendering = try! template.render(dictionary)
+///
+///
+/// ### Rendering
+///
+/// - `{{dictionary}}` renders the built-in Swift String Interpolation of the
+/// dictionary.
+///
+/// - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
+/// context stack, and renders the section once.
+///
+/// - `{{^dictionary}}...{{/dictionary}}` does not render.
+///
+///
+/// In order to iterate over the key/value pairs of a dictionary, use the `each`
+/// filter from the Standard Library:
+///
+/// // Register StandardLibrary.each for the key "each":
+/// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
+/// template.register(StandardLibrary.each, forKey: "each")
+///
+/// // Renders ""
+/// let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"]
+/// let rendering = try! template.render(["dictionary": dictionary])
+///
+///
+/// ## FilterFunction, RenderFunction, WillRenderFunction, DidRenderFunction, KeyedSubscriptFunction
+///
+/// Those functions are boxed as customized boxes.
+///
+/// ## Other Values
+///
+/// Other values (including nil) are discarded as empty boxes.
+///
+/// - parameter value: A value.
+/// - returns: A MustacheBox.
public func Box(_ value: Any?) -> MustacheBox {
guard let value = value else {
return EmptyBox
@@ -217,39 +328,6 @@ private func concatenateRenderings(array: [Any?], info: RenderingInfo) throws ->
extension MustacheBox {
- /// Arrays can feed Mustache templates.
- ///
- /// let array = [1,2,3]
- ///
- /// // Renders "123"
- /// let template = try! Template(string: "{{#array}}{{.}}{{/array}}")
- /// try! template.render(Box(["array": Box(array)]))
- ///
- ///
- /// ### Rendering
- ///
- /// - `{{array}}` renders the concatenation of the array items.
- ///
- /// - `{{#array}}...{{/array}}` renders as many times as there are items in
- /// `array`, pushing each item on its turn on the top of the context stack.
- ///
- /// - `{{^array}}...{{/array}}` renders if and only if `array` is empty.
- ///
- ///
- /// ### Keys exposed to templates
- ///
- /// An array can be queried for the following keys:
- ///
- /// - `count`: number of elements in the array
- /// - `first`: the first object in the array
- /// - `last`: the last object in the array
- ///
- /// Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders
- /// once, if and only if `array` is not empty.
- ///
- ///
- /// - parameter array: An array of boxable values.
- /// - returns: A MustacheBox that wraps *array*.
convenience init(array: [Any?]) {
self.init(
converter: MustacheBox.Converter(arrayValue: { array.map { Box($0) } }),
@@ -287,38 +365,6 @@ extension MustacheBox {
})
}
- /// Sets can feed Mustache templates.
- ///
- /// let set:Set = [1,2,3]
- ///
- /// // Renders "132", or "231", etc.
- /// let template = try! Template(string: "{{#set}}{{.}}{{/set}}")
- /// try! template.render(Box(["set": Box(set)]))
- ///
- ///
- /// ### Rendering
- ///
- /// - `{{set}}` renders the concatenation of the set items.
- ///
- /// - `{{#set}}...{{/set}}` renders as many times as there are items in `set`,
- /// pushing each item on its turn on the top of the context stack.
- ///
- /// - `{{^set}}...{{/set}}` renders if and only if `set` is empty.
- ///
- ///
- /// ### Keys exposed to templates
- ///
- /// A set can be queried for the following keys:
- ///
- /// - `count`: number of elements in the set
- /// - `first`: the first object in the set
- ///
- /// Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once,
- /// if and only if `set` is not empty.
- ///
- ///
- /// - parameter set: A set.
- /// - returns: A MustacheBox that wraps *set*.
convenience init(set: Set) {
self.init(
converter: MustacheBox.Converter(arrayValue: { set.map({ Box($0) }) }),
@@ -351,41 +397,6 @@ extension MustacheBox {
)
}
- /// A dictionary can feed Mustache templates.
- ///
- /// let dictionary: [String: String] = [
- /// "firstName": "Freddy",
- /// "lastName": "Mercury"]
- ///
- /// // Renders "Freddy Mercury"
- /// let template = try! Template(string: "{{firstName}} {{lastName}}")
- /// let rendering = try! template.render(Box(dictionary))
- ///
- ///
- /// ### Rendering
- ///
- /// - `{{dictionary}}` renders the built-in Swift String Interpolation of the
- /// dictionary.
- ///
- /// - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the
- /// context stack, and renders the section once.
- ///
- /// - `{{^dictionary}}...{{/dictionary}}` does not render.
- ///
- ///
- /// In order to iterate over the key/value pairs of a dictionary, use the `each`
- /// filter from the Standard Library:
- ///
- /// // Register StandardLibrary.each for the key "each":
- /// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
- /// template.register(StandardLibrary.each, forKey: "each")
- ///
- /// // Renders ""
- /// let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"]
- /// let rendering = try! template.render(Box(["dictionary": dictionary]))
- ///
- /// - parameter dictionary: A dictionary
- /// - returns: A MustacheBox that wraps *dictionary*.
convenience init(dictionary: [AnyHashable: Any?]) {
self.init(
converter: MustacheBox.Converter(dictionaryValue: {
@@ -410,8 +421,4 @@ extension MustacheBox {
}
}
-//public func Box() -> MustacheBox {
-// return EmptyBox
-//}
-
let EmptyBox = MustacheBox()
diff --git a/Sources/URLEscapeHelper.swift b/Sources/URLEscapeHelper.swift
index 11e0096c..d07589b8 100644
--- a/Sources/URLEscapeHelper.swift
+++ b/Sources/URLEscapeHelper.swift
@@ -49,16 +49,15 @@ final class URLEscapeHelper : MustacheBoxable {
//
// It is activated as soon as the formatter enters the context stack, when
// used in a section {{# URLEscape }}...{{/ URLEscape }}.
- fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> MustacheBox {
+ fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> Any? {
switch tag.type {
case .variable:
// We don't know if the box contains a String, so let's escape its
// rendering.
- return Box({ (info: RenderingInfo) -> Rendering in
+ return { (info: RenderingInfo) -> Rendering in
let rendering = try box.render(info)
return try self.filter(rendering)
-
- })
+ }
case .section:
return box
}
diff --git a/Sources/ZipFilter.swift b/Sources/ZipFilter.swift
index 16bb16df..325af4ff 100644
--- a/Sources/ZipFilter.swift
+++ b/Sources/ZipFilter.swift
@@ -25,19 +25,19 @@ import Foundation
let ZipFilter = VariadicFilter { (boxes) in
- // Turn collection arguments into generators. Generators can be iterated
+ // Turn collection arguments into iterators. Iterators can be iterated
// all together, and this is what we need.
//
// Other kinds of arguments generate an error.
- var zippedGenerators: [AnyIterator] = []
+ var zippedIterators: [AnyIterator] = []
for box in boxes {
if box.isEmpty {
// Missing collection does not provide anything
} else if let array = box.arrayValue {
// Array
- zippedGenerators.append(AnyIterator(array.makeIterator()))
+ zippedIterators.append(AnyIterator(array.makeIterator()))
} else {
// Error
throw MustacheError(kind: .renderError, message: "Non-enumerable argument in zip filter: `\(box.value)`")
@@ -51,22 +51,22 @@ let ZipFilter = VariadicFilter { (boxes) in
while true {
- // Extract from all generators the boxes that should enter the rendering
+ // Extract from all iterators the boxes that should enter the rendering
// context at each iteration.
//
// Given the [1,2,3], [a,b,c] input collections, those boxes would be
// [1,a] then [2,b] and finally [3,c].
var zippedBoxes: [MustacheBox] = []
- for generator in zippedGenerators {
- var generator = generator
- if let box = generator.next() {
+ for iterator in zippedIterators {
+ var iterator = iterator
+ if let box = iterator.next() {
zippedBoxes.append(box)
}
}
- // All generators have been enumerated: stop
+ // All iterators have been enumerated: stop
if zippedBoxes.isEmpty {
break;
@@ -84,8 +84,5 @@ let ZipFilter = VariadicFilter { (boxes) in
renderFunctions.append(renderFunction)
}
-
- // Return a box of those boxed render functions
-
- return Box(renderFunctions.map(Box))
+ return renderFunctions
}
From 88584509d9f9118fc86aec08619e843a919a8924 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 10:23:24 +0200
Subject: [PATCH 33/46] Documentation
---
Sources/Context.swift | 207 ++++++++----------
Sources/ExpressionInvocation.swift | 4 +-
Sources/Fixit-1.1.0.swift | 13 ++
Sources/MustacheBox.swift | 4 +-
Tests/Public/BoxTests.swift | 2 +-
Tests/Public/ContextTests/ContextTests.swift | 6 +-
...ntextValueForMustacheExpressionTests.swift | 12 +-
Tests/Public/ObjcKeyAccessTests.swift | 4 +-
8 files changed, 120 insertions(+), 132 deletions(-)
diff --git a/Sources/Context.swift b/Sources/Context.swift
index c494b368..4bda5262 100644
--- a/Sources/Context.swift
+++ b/Sources/Context.swift
@@ -23,69 +23,57 @@
import Foundation
-/**
-A Context represents a state of the Mustache "context stack".
-
-The context stack grows and shrinks as the Mustache engine enters and leaves
-Mustache sections.
-
-The top of the context stack is called the "current context". It is the value
-rendered by the `{{.}}` tag:
-
- // Renders "Kitty, Pussy, Melba, "
- let template = try! Template(string: "{{#cats}}{{.}}, {{/cats}}")
- try! template.render(Box(["cats": ["Kitty", "Pussy", "Melba"]]))
-
-Key lookup starts with the current context and digs down the stack until if
-finds a value:
-
- // Renders ", , "
- let template = try! Template(string: "{{#children}}<{{name}}>, {{/children}}")
- let data = [
- "name": "parent",
- "children": [
- ["name": "child"],
- [:] // a child without a name
- ]
- ]
- try! template.render(Box(data))
-
-See also:
-
-- Configuration
-- TemplateRepository
-- RenderFunction
-*/
+/// A Context represents a state of the Mustache "context stack".
+///
+/// The context stack grows and shrinks as the Mustache engine enters and leaves
+/// Mustache sections.
+///
+/// The top of the context stack is called the "current context". It is the
+/// value rendered by the `{{.}}` tag:
+///
+/// // Renders "Kitty, Pussy, Melba, "
+/// let template = try! Template(string: "{{#cats}}{{.}}, {{/cats}}")
+/// try! template.render(["cats": ["Kitty", "Pussy", "Melba"]])
+///
+/// Key lookup starts with the current context and digs down the stack until if
+/// finds a value:
+///
+/// // Renders ", , "
+/// let template = try! Template(string: "{{#children}}<{{name}}>, {{/children}}")
+/// let data: [String: Any] = [
+/// "name": "parent",
+/// "children": [
+/// ["name": "child"],
+/// [:] // a child without a name
+/// ]
+/// ]
+/// try! template.render(data)
+///
+/// - seealso: Configuration
+/// - seealso: TemplateRepository
+/// - seealso: RenderFunction
final public class Context {
// =========================================================================
// MARK: - Creating Contexts
- /**
- Builds an empty Context.
- */
+ /// Creates an empty Context.
public convenience init() {
self.init(type: .root)
}
- /**
- Builds a context that contains the provided box.
-
- - parameter box: A box.
- - returns: A new context that contains *box*.
- */
+ /// Creates a context that contains the provided value.
+ ///
+ /// - parameter value: A value.
public convenience init(_ value: Any?) {
self.init(type: .box(box: Box(value), parent: Context()))
}
- /**
- Builds a context with a registered key. Registered keys are looked up first
- when evaluating Mustache tags.
-
- - parameter key: An identifier.
- - parameter box: A box.
- - returns: A new context with *box* registered for *key*.
- */
+ /// Creates a context with a registered key. Registered keys are looked up
+ /// first when evaluating Mustache tags.
+ ///
+ /// - parameter key: An identifier.
+ /// - parameter value: A value.
public convenience init(registeredKey key: String, value: Any?) {
self.init(type: .root, registeredKeysContext: Context([key: value]))
}
@@ -94,27 +82,21 @@ final public class Context {
// =========================================================================
// MARK: - Deriving New Contexts
- /**
- Returns a new context with the provided box pushed at the top of the context
- stack.
-
- - parameter box: A box.
- - returns: A new context with *box* pushed at the top of the stack.
- */
-
+ /// Creates a new context with the provided value pushed at the top of the
+ /// context stack.
+ ///
+ /// - parameter value: A value.
+ /// - returns: A new context with *value* pushed at the top of the stack.
public func extendedContext(_ value: Any?) -> Context {
return Context(type: .box(box: Box(value), parent: self), registeredKeysContext: registeredKeysContext)
}
- /**
- Returns a new context with the provided box at the top of the context stack.
- Registered keys are looked up first when evaluating Mustache tags.
-
- - parameter key: An identifier.
- - parameter box: A box.
- - returns: A new context with *box* registered for *key*.
- */
-
+ /// Creates a new context with the provided value registered for *key*.
+ /// Registered keys are looked up first when evaluating Mustache tags.
+ ///
+ /// - parameter key: An identifier.
+ /// - parameter value: A value.
+ /// - returns: A new context with *value* registered for *key*.
public func extendedContext(withRegisteredValue value: Any?, forKey key: String) -> Context {
let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext([key: value])
return Context(type: self.type, registeredKeysContext: registeredKeysContext)
@@ -124,10 +106,8 @@ final public class Context {
// =========================================================================
// MARK: - Fetching Values from the Context Stack
- /**
- Returns the top box of the context stack, the one that would be rendered by
- the `{{.}}` tag.
- */
+ /// The top box of the context stack, the one that would be rendered by
+ /// the `{{.}}` tag.
public var topBox: MustacheBox {
switch type {
case .root:
@@ -139,33 +119,31 @@ final public class Context {
}
}
- /**
- Returns the boxed value stored in the context stack for the given key.
-
- The following search pattern is used:
-
- 1. If the key is "registered", returns the registered box for that key.
-
- 2. Otherwise, searches the context stack for a box that has a non-empty
- box for the key (see `InspectFunction`).
-
- 3. If none of the above situations occurs, returns the empty box.
-
- let data = ["name": "Groucho Marx"]
- let context = Context(Box(data))
-
- // "Groucho Marx"
- context.mustacheBoxForKey("name").value
-
- If you want the value for a full Mustache expression such as `user.name` or
- `uppercase(user.name)`, use the `mustacheBoxForExpression` method.
-
- - parameter key: A key.
- - returns: The MustacheBox for *key*.
- */
- public func mustacheBoxForKey(_ key: String) -> MustacheBox {
+ /// Returns the boxed value stored in the context stack for the given key.
+ ///
+ /// The following search pattern is used:
+ ///
+ /// 1. If the key is "registered", returns the registered box for that key.
+ ///
+ /// 2. Otherwise, searches the context stack for a box that has a non-empty
+ /// box for the key (see `KeyedSubscriptFunction`).
+ ///
+ /// 3. If none of the above situations occurs, returns the empty box.
+ ///
+ /// let data = ["name": "Groucho Marx"]
+ /// let context = Context(data)
+ ///
+ /// // "Groucho Marx"
+ /// context.mustacheBox(forKey: "name").value
+ ///
+ /// If you want the value for a full Mustache expression such as `user.name` or
+ /// `uppercase(user.name)`, use the `mustacheBox(forExpression:)` method.
+ ///
+ /// - parameter key: A key.
+ /// - returns: The MustacheBox for *key*.
+ public func mustacheBox(forKey key: String) -> MustacheBox {
if let registeredKeysContext = registeredKeysContext {
- let box = registeredKeysContext.mustacheBoxForKey(key)
+ let box = registeredKeysContext.mustacheBox(forKey: key)
if !box.isEmpty {
return box
}
@@ -175,33 +153,30 @@ final public class Context {
case .root:
return EmptyBox
case .box(box: let box, parent: let parent):
- let innerBox = box.mustacheBoxForKey(key)
+ let innerBox = box.mustacheBox(forKey: key)
if innerBox.isEmpty {
- return parent.mustacheBoxForKey(key)
+ return parent.mustacheBox(forKey: key)
} else {
return innerBox
}
case .partialOverride(partialOverride: _, parent: let parent):
- return parent.mustacheBoxForKey(key)
+ return parent.mustacheBox(forKey: key)
}
}
- /**
- Evaluates a Mustache expression such as `name`, or `uppercase(user.name)`.
-
- let data = ["person": ["name": "Albert Einstein"]]
- let context = Context(Box(data))
-
- // "Albert Einstein"
- try! context.mustacheBoxForExpression("person.name").value
-
- - parameter string: The expression string.
- - parameter error: If there is a problem parsing or evaluating the
- expression, throws an error that describes the problem.
-
- - returns: The value of the expression.
- */
- public func mustacheBoxForExpression(_ string: String) throws -> MustacheBox {
+ /// Evaluates a Mustache expression such as `name`,
+ /// or `uppercase(user.name)`.
+ ///
+ /// let data = ["person": ["name": "Albert Einstein"]]
+ /// let context = Context(data)
+ ///
+ /// // "Albert Einstein"
+ /// try! context.mustacheBoxForExpression("person.name").value
+ ///
+ /// - parameter string: The expression string.
+ /// - throws: MustacheError
+ /// - returns: The value of the expression.
+ public func mustacheBox(forExpression string: String) throws -> MustacheBox {
let parser = ExpressionParser()
var empty = false
let expression = try parser.parse(string, empty: &empty)
diff --git a/Sources/ExpressionInvocation.swift b/Sources/ExpressionInvocation.swift
index 975880e7..a06da269 100644
--- a/Sources/ExpressionInvocation.swift
+++ b/Sources/ExpressionInvocation.swift
@@ -40,12 +40,12 @@ struct ExpressionInvocation {
case .identifier(let identifier):
// {{ identifier }}
- return context.mustacheBoxForKey(identifier)
+ return context.mustacheBox(forKey: identifier)
case .scoped(let baseExpression, let identifier):
// {{ .identifier }}
- return try evaluate(context: context, expression: baseExpression).mustacheBoxForKey(identifier)
+ return try evaluate(context: context, expression: baseExpression).mustacheBox(forKey: identifier)
case .filter(let filterExpression, let argumentExpression, let partialApplication):
// {{ () }}
diff --git a/Sources/Fixit-1.1.0.swift b/Sources/Fixit-1.1.0.swift
index 2cb4a902..3a9e8433 100644
--- a/Sources/Fixit-1.1.0.swift
+++ b/Sources/Fixit-1.1.0.swift
@@ -25,3 +25,16 @@ extension Template {
@available(*, unavailable, renamed:"register(_:forKey:)")
public func registerInBaseContext(_ key: String, _ value: Any?) { }
}
+
+extension Context {
+ @available(*, unavailable, renamed:"mustacheBox(forKey:)")
+ public func mustacheBoxForKey(_ key: String) -> MustacheBox { return EmptyBox }
+
+ @available(*, unavailable, renamed:"mustacheBox(forExpression:)")
+ public func mustacheBoxForExpression(_ string: String) throws -> MustacheBox { return EmptyBox }
+}
+
+extension MustacheBox {
+ @nonobjc @available(*, unavailable, renamed:"mustacheBox(forKey:)")
+ public func mustacheBoxForKey(_ key: String) -> MustacheBox { return EmptyBox }
+}
diff --git a/Sources/MustacheBox.swift b/Sources/MustacheBox.swift
index 64730be0..964def6b 100644
--- a/Sources/MustacheBox.swift
+++ b/Sources/MustacheBox.swift
@@ -144,12 +144,12 @@ final public class MustacheBox : NSObject {
Extracts a key out of a box.
let box = Box(["firstName": "Arthur"])
- box.mustacheBoxForKey("firstName").value // "Arthur"
+ box.mustacheBox(forKey: "firstName").value // "Arthur"
- parameter key: A key.
- returns: The MustacheBox for *key*.
*/
- public func mustacheBoxForKey(_ key: String) -> MustacheBox {
+ public func mustacheBox(forKey key: String) -> MustacheBox {
guard let keyedSubscript = keyedSubscript else {
return EmptyBox
}
diff --git a/Tests/Public/BoxTests.swift b/Tests/Public/BoxTests.swift
index a6a9a543..c4b403e5 100644
--- a/Tests/Public/BoxTests.swift
+++ b/Tests/Public/BoxTests.swift
@@ -1066,7 +1066,7 @@ class BoxTests: XCTestCase {
let array = NSArray(object: Class())
let context = Context(array)
- let box = try! context.mustacheBoxForExpression("first.foo")
+ let box = try! context.mustacheBox(forExpression: "first.foo")
XCTAssertEqual((box.value as! String), "foo")
}
diff --git a/Tests/Public/ContextTests/ContextTests.swift b/Tests/Public/ContextTests/ContextTests.swift
index 28fbf458..40ae78ba 100644
--- a/Tests/Public/ContextTests/ContextTests.swift
+++ b/Tests/Public/ContextTests/ContextTests.swift
@@ -78,12 +78,12 @@ class ContextTests: XCTestCase {
let context = Context(["name": "name1", "a": ["name": "name2"]])
// '.' is an expression, not a key
- XCTAssertTrue(context.mustacheBoxForKey(".").isEmpty)
+ XCTAssertTrue(context.mustacheBox(forKey: ".").isEmpty)
// 'name' is a key
- XCTAssertEqual((context.mustacheBoxForKey("name").value as! String), "name1")
+ XCTAssertEqual((context.mustacheBox(forKey: "name").value as! String), "name1")
// 'a.name' is an expression, not a key
- XCTAssertTrue(context.mustacheBoxForKey("a.name").isEmpty)
+ XCTAssertTrue(context.mustacheBox(forKey: "a.name").isEmpty)
}
}
diff --git a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
index 9bc240e2..9f1cbf4e 100644
--- a/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
+++ b/Tests/Public/ContextTests/ContextValueForMustacheExpressionTests.swift
@@ -28,21 +28,21 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
func testImplicitIteratorExpression() {
let context = Context("success")
- let box = try! context.mustacheBoxForExpression(".")
+ let box = try! context.mustacheBox(forExpression: ".")
let string = box.value as? String
XCTAssertEqual(string!, "success")
}
func testIdentifierExpression() {
let context = Context(["name": "success"])
- let box = try! context.mustacheBoxForExpression("name")
+ let box = try! context.mustacheBox(forExpression: "name")
let string = box.value as? String
XCTAssertEqual(string!, "success")
}
func testScopedExpression() {
let context = Context(["a": ["name": "success"]])
- let box = try! context.mustacheBoxForExpression("a.name")
+ let box = try! context.mustacheBox(forExpression: "a.name")
let string = box.value as? String
XCTAssertEqual(string!, "success")
}
@@ -52,7 +52,7 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
return string!.uppercased()
})
let context = Context(["name": "success", "f": filter])
- let box = try! context.mustacheBoxForExpression("f(name)")
+ let box = try! context.mustacheBox(forExpression: "f(name)")
let string = box.value as? String
XCTAssertEqual(string!, "SUCCESS")
}
@@ -60,7 +60,7 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
func testParseError() {
let context = Context()
do {
- _ = try context.mustacheBoxForExpression("a.")
+ _ = try context.mustacheBox(forExpression: "a.")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.parseError) // Invalid expression
@@ -72,7 +72,7 @@ class ContextValueForMustacheExpressionTests: XCTestCase {
func testRenderError() {
let context = Context()
do {
- _ = try context.mustacheBoxForExpression("f(x)")
+ _ = try context.mustacheBox(forExpression: "f(x)")
XCTFail("Expected MustacheError")
} catch let error as MustacheError {
XCTAssertEqual(error.kind, MustacheError.Kind.renderError) // Missing filter
diff --git a/Tests/Public/ObjcKeyAccessTests.swift b/Tests/Public/ObjcKeyAccessTests.swift
index c7f2349f..c6e45845 100644
--- a/Tests/Public/ObjcKeyAccessTests.swift
+++ b/Tests/Public/ObjcKeyAccessTests.swift
@@ -40,7 +40,7 @@ class ObjcKeyAccessTests: XCTestCase {
// test context
let context = Context(object)
- XCTAssertEqual((context.mustacheBoxForKey("property").value as! String), "property")
+ XCTAssertEqual((context.mustacheBox(forKey: "property").value as! String), "property")
}
func testMethodsAreUnsafeAndNotAvailable() {
@@ -52,6 +52,6 @@ class ObjcKeyAccessTests: XCTestCase {
// test context
let context = Context(object)
- XCTAssertTrue(context.mustacheBoxForKey("method").value == nil)
+ XCTAssertTrue(context.mustacheBox(forKey: "method").value == nil)
}
}
From e7a0aca2c59ec24dc79cd58ac6321647bb29962b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 10:52:45 +0200
Subject: [PATCH 34/46] Documentation
---
Sources/CoreFunctions.swift | 989 +++++++++++++++++-------------------
1 file changed, 474 insertions(+), 515 deletions(-)
diff --git a/Sources/CoreFunctions.swift b/Sources/CoreFunctions.swift
index 3e9fa4fe..0cca3731 100644
--- a/Sources/CoreFunctions.swift
+++ b/Sources/CoreFunctions.swift
@@ -27,124 +27,107 @@ import Foundation
// =============================================================================
// MARK: - KeyedSubscriptFunction
-/**
-`KeyedSubscriptFunction` is used by the Mustache rendering engine whenever it
-has to resolve identifiers in expressions such as `{{ name }}` or
-`{{ user.name }}`. Subscript functions turn those identifiers into
-`MustacheBox`, the type that wraps rendered values.
-
-All types that expose keys to Mustache templates provide such a subscript
-function by conforming to the `MustacheBoxable` protocol. This is the case of
-built-in types such as NSObject, that uses `valueForKey:` in order to expose
-its properties; String, which exposes its "length"; collections, which expose
-keys like "count", "first", etc. etc.
-
- var box = Box("string")
- box = box["length"] // Evaluates the KeyedSubscriptFunction
- box.value // 6
-
- box = Box(["a", "b", "c"])
- box = box["first"] // Evaluates the KeyedSubscriptFunction
- box.value // "a"
-
-Your can build boxes that hold a custom subscript function. This is a rather
-advanced usage, only supported with the low-level function
-`func Box(boolValue:value:keyedSubscript:filter:render:willRender:didRender:) -> MustacheBox`.
-
- // A KeyedSubscriptFunction that turns "key" into "KEY":
- let keyedSubscript: KeyedSubscriptFunction = { (key: String) -> Any? in
- return Box(key.uppercaseString)
- }
-
- // Render "FOO & BAR"
- let template = try! Template(string: "{{foo}} & {{bar}}")
- let box = Box(keyedSubscript: keyedSubscript)
- try! template.render(box)
-
-
-### Missing keys vs. missing values.
-
-`KeyedSubscriptFunction` returns a non-optional `MustacheBox`.
-
-In order to express "missing key", and have Mustache rendering engine dig deeper
-in the context stack in order to resolve a key, return the empty box `Box()`.
-
-In order to express "missing value", and prevent the rendering engine from
-digging deeper, return `Box(NSNull())`.
-*/
+/// KeyedSubscriptFunction is used by the Mustache rendering engine whenever
+/// it has to resolve identifiers in expressions such as `{{ name }}` or
+/// `{{ user.name }}`. Subscript functions turn those identifiers into values.
+///
+/// All types that expose keys to Mustache templates provide such a subscript
+/// function by conforming to the `MustacheBoxable` protocol. This is the case
+/// of built-in types such as NSObject, that uses `valueForKey:` in order to
+/// expose its properties; String, which exposes its "length"; collections,
+/// which expose keys like "count", "first", etc. etc.
+///
+/// Your can build boxes that hold a custom subscript function. This is a rather
+/// advanced usage, supported by the low-level initializer
+/// `MustacheBox(boolValue:value:keyedSubscript:filter:render:willRender:didRender:) -> MustacheBox`.
+///
+/// // A KeyedSubscriptFunction that turns "key" into "KEY":
+/// let keyedSubscript: KeyedSubscriptFunction = { (key: String) -> Any? in
+/// return key.uppercased()
+/// }
+///
+/// // Render "FOO & BAR"
+/// let template = try! Template(string: "{{foo}} & {{bar}}")
+/// let box = MustacheBox(keyedSubscript: keyedSubscript)
+/// try! template.render(box)
+///
+///
+/// ### Missing keys vs. missing values.
+///
+/// In order to express "missing key", and have Mustache rendering engine dig
+/// deeper in the context stack in order to resolve a key, return `nil`.
+///
+/// In order to express "missing value", and prevent the rendering engine from
+/// digging deeper, return `NSNull()`.
public typealias KeyedSubscriptFunction = (_ key: String) -> Any?
// =============================================================================
// MARK: - FilterFunction
-/**
-`FilterFunction` is the core type that lets GRMustache evaluate filtered
-expressions such as `{{ uppercase(string) }}`.
-
-To build a filter, you use the `Filter()` function. It takes a function as an
-argument. For example:
-
- let increment = Filter { (x: Int?) in
- return Box(x! + 1)
- }
-
-To let a template use a filter, register it:
-
- let template = try! Template(string: "{{increment(x)}}")
- template.registerInBaseContext("increment", Box(increment))
-
- // "2"
- try! template.render(Box(["x": 1]))
-
-`Filter()` can take several types of functions, depending on the type of filter
-you want to build. The example above processes `Int` values. There are three
-types of filters:
-
-- Values filters:
-
- - `(MustacheBox) throws -> MustacheBox`
- - `(T?) throws -> MustacheBox` (Generic)
- - `([MustacheBox]) throws -> MustacheBox` (Multiple arguments)
-
-- Pre-rendering filters:
-
- - `(Rendering) throws -> Rendering`
-
-- Custom rendering filters:
-
- - `(MustacheBox, RenderingInfo) throws -> Rendering`
- - `(T?, RenderingInfo) throws -> Rendering` (Generic)
-
-See the documentation of the `Filter()` functions.
-*/
+/// `FilterFunction` is the core type that lets GRMustache evaluate filtered
+/// expressions such as `{{ uppercase(string) }}`.
+///
+/// You don't use this type directly, and instead build filters with the
+/// `Filter()` function. For example:
+///
+/// let increment = Filter { (x: Int?) in
+/// return x! + 1
+/// }
+///
+/// To let a template use a filter, register it:
+///
+/// let template = try! Template(string: "{{increment(x)}}")
+/// template.register(increment, forKey: "increment")
+///
+/// // "2"
+/// try! template.render(["x": 1])
+///
+/// `Filter()` can take several types of functions, depending on the type of
+/// filter you want to build. The example above processes `Int` values. There
+/// are three types of filters:
+///
+/// - Values filters:
+///
+/// - `(MustacheBox) throws -> Any?`
+/// - `(T?) throws -> Any?` (Generic)
+/// - `([MustacheBox]) throws -> Any?` (Multiple arguments)
+///
+/// - Pre-rendering filters:
+///
+/// - `(Rendering) throws -> Rendering`
+///
+/// - Custom rendering filters:
+///
+/// - `(MustacheBox, RenderingInfo) throws -> Rendering`
+/// - `(T?, RenderingInfo) throws -> Rendering` (Generic)
+///
+/// See the documentation of the `Filter()` functions.
public typealias FilterFunction = (_ box: MustacheBox, _ partialApplication: Bool) throws -> Any?
// -----------------------------------------------------------------------------
// MARK: - Values Filters
-/**
-Builds a filter that takes a single argument.
-
-For example, here is the trivial `identity` filter:
-
- let identity = Filter { (box: MustacheBox) in
- return box
- }
-
- let template = try! Template(string: "{{identity(a)}}, {{identity(b)}}")
- template.registerInBaseContext("identity", Box(identity))
-
- // "foo, 1"
- try! template.render(Box(["a": "foo", "b": 1]))
-
-If the template provides more than one argument, the filter returns a
-MustacheError of type RenderError.
-
-- parameter filter: A function `(MustacheBox) throws -> MustacheBox`.
-- returns: A FilterFunction.
-*/
+/// Creates a filter that takes a single argument.
+///
+/// For example, here is the trivial `identity` filter:
+///
+/// let identity = Filter { (box: MustacheBox) in
+/// return box
+/// }
+///
+/// let template = try! Template(string: "{{identity(a)}}, {{identity(b)}}")
+/// template.register(identity, forKey: "identity")
+///
+/// // "foo, 1"
+/// try! template.render(["a": "foo", "b": 1])
+///
+/// If the template provides more than one argument, the filter returns a
+/// MustacheError.renderError.
+///
+/// - parameter filter: A function `(MustacheBox) throws -> Any?`.
+/// - returns: A FilterFunction.
public func Filter(_ filter: @escaping (MustacheBox) throws -> Any?) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
@@ -155,30 +138,28 @@ public func Filter(_ filter: @escaping (MustacheBox) throws -> Any?) -> FilterFu
}
}
-/**
-Builds a filter that takes a single argument of type `T?`.
-
-For example:
-
- let increment = Filter { (x: Int?) in
- return Box(x! + 1)
- }
-
- let template = try! Template(string: "{{increment(x)}}")
- template.registerInBaseContext("increment", Box(increment))
-
- // "2"
- try! template.render(Box(["x": 1]))
-
-The argument is converted to `T` using the built-in `as?` operator before being
-given to the filter.
-
-If the template provides more than one argument, the filter returns a
-MustacheError of type RenderError.
-
-- parameter filter: A function `(T?) throws -> MustacheBox`.
-- returns: A FilterFunction.
-*/
+/// Creates a filter that takes a single argument of type `T?`.
+///
+/// For example:
+///
+/// let increment = Filter { (x: Int?) in
+/// return x! + 1
+/// }
+///
+/// let template = try! Template(string: "{{increment(x)}}")
+/// template.register(increment, forKey: "increment")
+///
+/// // "2"
+/// try! template.render(["x": 1])
+///
+/// The argument is converted to `T` using the built-in `as?` operator before
+/// being given to the filter.
+///
+/// If the template provides more than one argument, the filter returns a
+/// MustacheError.renderError.
+///
+/// - parameter filter: A function `(T?) throws -> Any?`.
+/// - returns: A FilterFunction.
public func Filter(_ filter: @escaping (T?) throws -> Any?) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
@@ -189,28 +170,26 @@ public func Filter(_ filter: @escaping (T?) throws -> Any?) -> FilterFunction
}
}
-/**
-Returns a filter than accepts any number of arguments.
-
-For example:
-
- // `sum(x, ...)` evaluates to the sum of provided integers
- let sum = VariadicFilter { (boxes: [MustacheBox]) in
- // Extract integers out of input boxes, assuming zero for non numeric values
- let integers = boxes.map { (box) in (box.value as? Int) ?? 0 }
- let sum = integers.reduce(0, combine: +)
- return Box(sum)
- }
-
- let template = try! Template(string: "{{ sum(a,b,c) }}")
- template.registerInBaseContext("sum", Box(sum))
-
- // Renders "6"
- try! template.render(Box(["a": 1, "b": 2, "c": 3]))
-
-- parameter filter: A function `([MustacheBox]) throws -> MustacheBox`.
-- returns: A FilterFunction.
-*/
+/// Creates a filter than accepts any number of arguments.
+///
+/// For example:
+///
+/// // `sum(x, ...)` evaluates to the sum of provided integers
+/// let sum = VariadicFilter { (boxes: [MustacheBox]) in
+/// // Extract integers out of input boxes, assuming zero for non numeric values
+/// let integers = boxes.map { (box) in (box.value as? Int) ?? 0 }
+/// let sum = integers.reduce(0, combine: +)
+/// return sum
+/// }
+///
+/// let template = try! Template(string: "{{ sum(a,b,c) }}")
+/// template.register(sum, forKey: "sum")
+///
+/// // Renders "6"
+/// try! template.render(["a": 1, "b": 2, "c": 3])
+///
+/// - parameter filter: A function `([MustacheBox]) throws -> Any?`.
+/// - returns: A FilterFunction.
public func VariadicFilter(_ filter: @escaping ([MustacheBox]) throws -> Any?) -> FilterFunction {
// f(a,b,c) is implemented as f(a)(b)(c).
@@ -240,38 +219,36 @@ public func VariadicFilter(_ filter: @escaping ([MustacheBox]) throws -> Any?) -
// -----------------------------------------------------------------------------
// MARK: - Pre-Rendering Filters
-/**
-Builds a filter that performs post rendering.
-
-`Rendering` is a type that wraps a rendered String, and its content type (HTML
-or Text). This filter turns a rendering in another one:
-
- // twice filter renders its argument twice:
- let twice = Filter { (rendering: Rendering) in
- return Rendering(rendering.string + rendering.string, rendering.contentType)
- }
-
- let template = try! Template(string: "{{ twice(x) }}")
- template.registerInBaseContext("twice", Box(twice))
-
- // Renders "foofoo", "123123"
- try! template.render(Box(["x": "foo"]))
- try! template.render(Box(["x": 123]))
-
-When this filter is executed, eventual HTML-escaping performed by the rendering
-engine has not happened yet: the rendering argument may contain raw text. This
-allows you to chain pre-rendering filters without mangling HTML entities.
-
-- parameter filter: A function `(Rendering) throws -> Rendering`.
-- returns: A FilterFunction.
-*/
+/// Creates a filter that performs pre-rendering.
+///
+/// `Rendering` is a type that wraps a rendered String, and its content type
+/// (HTML or Text). This filter turns a rendering in another one:
+///
+/// // twice filter renders its argument twice:
+/// let twice = Filter { (rendering: Rendering) in
+/// return Rendering(rendering.string + rendering.string, rendering.contentType)
+/// }
+///
+/// let template = try! Template(string: "{{ twice(x) }}")
+/// template.register(twice, forKey: "twice")
+///
+/// // Renders "foofoo", "123123"
+/// try! template.render(["x": "foo"])
+/// try! template.render(["x": 123])
+///
+/// When this filter is executed, eventual HTML-escaping performed by the
+/// rendering engine has not happened yet: the rendering argument may contain
+/// raw text. This allows you to chain pre-rendering filters without mangling
+/// HTML entities.
+///
+/// - parameter filter: A function `(Rendering) throws -> Rendering`.
+/// - returns: A FilterFunction.
public func Filter(_ filter: @escaping (Rendering) throws -> Rendering) -> FilterFunction {
return { (box: MustacheBox, partialApplication: Bool) in
guard !partialApplication else {
// This is a single-argument filter: we do not wait for another one.
throw MustacheError(kind: .renderError, message: "Too many arguments")
}
- // Box a RenderFunction
return { (info: RenderingInfo) in
return try filter(box.render(info))
}
@@ -282,70 +259,64 @@ public func Filter(_ filter: @escaping (Rendering) throws -> Rendering) -> Filte
// -----------------------------------------------------------------------------
// MARK: - Custom Rendering Filters
-/**
-Builds a filter that takes a single argument and performs custom rendering.
-
-See the documentation of the `RenderFunction` type for a detailed discussion of
-the `RenderingInfo` and `Rendering` types.
-
-For an example of such a filter, see the documentation of
-`func Filter(filter: (T?, RenderingInfo) throws -> Rendering) -> FilterFunction`.
-This example processes `T?` instead of `MustacheBox`, but the idea is the same.
-
-- parameter filter: A function `(MustacheBox, RenderingInfo) throws -> Rendering`.
-- returns: A FilterFunction.
-*/
+/// Creates a filter that takes a single argument and performs custom rendering.
+///
+/// See the documentation of the `RenderFunction` type for a detailed discussion
+/// of the `RenderingInfo` and `Rendering` types.
+///
+/// For an example of such a filter, see the documentation of
+/// `func Filter(filter: (T?, RenderingInfo) throws -> Rendering) -> FilterFunction`.
+/// This example processes `T?` instead of `MustacheBox`, but the idea is the same.
+///
+/// - parameter filter: A function `(MustacheBox, RenderingInfo) throws -> Rendering`.
+/// - returns: A FilterFunction.
public func Filter(_ filter: @escaping (MustacheBox, RenderingInfo) throws -> Rendering) -> FilterFunction {
return Filter { (box: MustacheBox) in
- // Box a RenderFunction
- return Box { (info: RenderingInfo) in
+ return { (info: RenderingInfo) in
return try filter(box, info)
}
}
}
-/**
-Builds a filter that takes a single argument of type `T?` and performs custom
-rendering.
-
-For example:
-
- // {{# pluralize(count) }}...{{/ }} renders the plural form of the section
- // content if the `count` argument is greater than 1.
- let pluralize = Filter { (count: Int?, info: RenderingInfo) in
-
- // Pluralize the inner content of the section tag:
- var string = info.tag.innerTemplateString
- if count > 1 {
- string += "s" // naive pluralization
- }
-
- return Rendering(string)
- }
-
- let template = try! Template(string: "I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.")
- template.registerInBaseContext("pluralize", Box(pluralize))
-
- // Renders "I have 3 cats."
- let data = ["cats": ["Kitty", "Pussy", "Melba"]]
- try! template.render(Box(data))
-
-The argument is converted to `T` using the built-in `as?` operator before being
-given to the filter.
-
-If the template provides more than one argument, the filter returns a
-MustacheError of type RenderError.
-
-See the documentation of the `RenderFunction` type for a detailed discussion of
-the `RenderingInfo` and `Rendering` types.
-
-- parameter filter: A function `(T?, RenderingInfo) throws -> Rendering`.
-- returns: A FilterFunction.
-*/
+/// Creates a filter that takes a single argument of type `T?` and performs
+/// custom rendering.
+///
+/// For example:
+///
+/// // {{# pluralize(count) }}...{{/ }} renders the plural form of the section
+/// // content if the `count` argument is greater than 1.
+/// let pluralize = Filter { (count: Int?, info: RenderingInfo) in
+///
+/// // Pluralize the inner content of the section tag:
+/// var string = info.tag.innerTemplateString
+/// if let count = count, count > 1 {
+/// string += "s" // naive pluralization
+/// }
+///
+/// return Rendering(string)
+/// }
+///
+/// let template = try! Template(string: "I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.")
+/// template.register(pluralize, forKey: "pluralize")
+///
+/// // Renders "I have 3 cats."
+/// let data = ["cats": ["Kitty", "Pussy", "Melba"]]
+/// try! template.render(data)
+///
+/// The argument is converted to `T` using the built-in `as?` operator before
+/// being given to the filter.
+///
+/// If the template provides more than one argument, the filter returns a
+/// MustacheError.renderError.
+///
+/// See the documentation of the `RenderFunction` type for a detailed discussion
+/// of the `RenderingInfo` and `Rendering` types.
+///
+/// - parameter filter: A function `(T?, RenderingInfo) throws -> Rendering`.
+/// - returns: A FilterFunction.
public func Filter(_ filter: @escaping (T?, RenderingInfo) throws -> Rendering) -> FilterFunction {
return Filter { (t: T?) in
- // Box a RenderFunction
- return Box { (info: RenderingInfo) in
+ return { (info: RenderingInfo) in
return try filter(t, info)
}
}
@@ -355,150 +326,151 @@ public func Filter(_ filter: @escaping (T?, RenderingInfo) throws -> Renderin
// =============================================================================
// MARK: - RenderFunction
-/**
-`RenderFunction` is used by the Mustache rendering engine when it renders a
-variable tag (`{{name}}` or `{{{body}}}`) or a section tag
-(`{{#name}}...{{/name}}`).
-
-
-### Output: `Rendering`
-
-The return type of `RenderFunction` is `Rendering`, a type that wraps a rendered
-String, and its ContentType (HTML or Text).
-
-Text renderings are HTML-escaped, except for `{{{triple}}}` mustache tags, and
-Text templates (see `Configuration.contentType` for a full discussion of the
-content type of templates).
-
-For example:
-
- let HTML: RenderFunction = { (_) in
- return Rendering("", .HTML)
- }
- let text: RenderFunction = { (_) in
- return Rendering("") // default content type is text
- }
-
- // Renders ", <text>"
- let template = try! Template(string: "{{HTML}}, {{text}}")
- let data = ["HTML": Box(HTML), "text": Box(text)]
- let rendering = try! template.render(Box(data))
-
-
-### Input: `RenderingInfo`
-
-The `info` argument contains a `RenderingInfo` which provides information
-about the requested rendering:
-
-- `info.context: Context` is the context stack which contains the rendered data.
-
-- `info.tag: Tag` is the tag to render.
-
-- `info.tag.type: TagType` is the type of the tag (variable or section). You can
- use this type to have a different rendering for variable and section tags.
-
-- `info.tag.innerTemplateString: String` is the inner content of the tag,
- unrendered. For the section tag `{{# person }}Hello {{ name }}!{{/ person }}`,
- it is `Hello {{ name }}!`.
-
-- `info.tag.render: (Context) throws -> Rendering` is a
- function that renders the inner content of the tag, given a context stack.
- For example, the section tag `{{# person }}Hello {{ name }}!{{/ person }}`
- would render `Hello Arthur!`, assuming the provided context stack provides
- "Arthur" for the key "name".
-
-
-### Example
-
-As an example, let's write a `RenderFunction` which performs the default
-rendering of a value:
-
-- `{{ value }}` and `{{{ value }}}` renders the built-in Swift String
- Interpolation of the value: our render function will return a `Rendering` with
- content type Text when the rendered tag is a variable tag. The Text content
- type makes sure that the returned string will be HTML-escaped if needed.
-
-- `{{# value }}...{{/ value }}` pushes the value on the top of the context
- stack, and renders the inner content of section tags.
-
-The default rendering thus reads:
-
- let value = ... // Some value
- let renderValue: RenderFunction = { (info: RenderingInfo) throws in
-
- // Default rendering depends on the tag type:
- switch info.tag.type {
-
- case .Variable:
- // {{ value }} and {{{ value }}}
-
- // Return the built-in Swift String Interpolation of the value:
- return Rendering("\(value)", .Text)
-
- case .Section:
- // {{# value }}...{{/ value }}
-
- // Push the value on the top of the context stack:
- let context = info.context.extendedContext(Box(value))
-
- // Renders the inner content of the section tag:
- return try info.tag.render(context)
- }
- }
-
- let template = try! Template(string: "{{value}}")
- let rendering = try! template.render(Box(["value": Box(renderValue)]))
-
-- parameter info: A RenderingInfo.
-- parameter error: If there is a problem in the rendering, throws an error
- that describes the problem.
-- returns: A Rendering.
-*/
+/// `RenderFunction` is used by the Mustache rendering engine when it renders a
+/// variable tag (`{{name}}` or `{{{body}}}`) or a section tag
+/// (`{{#name}}...{{/name}}`).
+///
+///
+/// ### Output: `Rendering`
+///
+/// The return type of `RenderFunction` is `Rendering`, a type that wraps a
+/// rendered String, and its ContentType (HTML or Text).
+///
+/// Text renderings are HTML-escaped, except for `{{{triple}}}` mustache tags,
+/// and Text templates (see `Configuration.contentType` for a full discussion of
+/// the content type of templates).
+///
+/// For example:
+///
+/// let html: RenderFunction = { (_) in
+/// return Rendering("", .html)
+/// }
+/// let text: RenderFunction = { (_) in
+/// return Rendering("") // default content type is text
+/// }
+///
+/// // Renders ", <text>"
+/// let template = try! Template(string: "{{html}}, {{text}}")
+/// let data = ["html": html, "text": text]
+/// let rendering = try! template.render(data)
+///
+///
+/// ### Input: `RenderingInfo`
+///
+/// The `info` argument contains a `RenderingInfo` which provides information
+/// about the requested rendering:
+///
+/// - `info.context: Context` is the context stack which contains the
+/// rendered data.
+///
+/// - `info.tag: Tag` is the tag to render.
+///
+/// - `info.tag.type: TagType` is the type of the tag (variable or section). You
+/// can use this type to have a different rendering for variable and
+/// section tags.
+///
+/// - `info.tag.innerTemplateString: String` is the inner content of the tag,
+/// unrendered. For the section tag `{{# person }}Hello {{ name }}!{{/ person }}`,
+/// it is `Hello {{ name }}!`.
+///
+/// - `info.tag.render: (Context) throws -> Rendering` is a function that
+/// renders the inner content of the tag, given a context stack. For example,
+/// the section tag `{{# person }}Hello {{ name }}!{{/ person }}` would render
+/// `Hello Arthur!`, assuming the provided context stack provides "Arthur" for
+/// the key "name".
+///
+///
+/// ### Example
+///
+/// As an example, let's write a `RenderFunction` which performs the default
+/// rendering of a value:
+///
+/// - `{{ value }}` and `{{{ value }}}` renders the built-in Swift String
+/// Interpolation of the value: our render function will return a `Rendering`
+/// with content type Text when the rendered tag is a variable tag. The Text
+/// content type makes sure that the returned string will be HTML-escaped
+/// if needed.
+///
+/// - `{{# value }}...{{/ value }}` pushes the value on the top of the context
+/// stack, and renders the inner content of section tags.
+///
+/// The default rendering thus reads:
+///
+/// let value = ... // Some value
+/// let renderValue: RenderFunction = { (info: RenderingInfo) throws in
+///
+/// // Default rendering depends on the tag type:
+/// switch info.tag.type {
+///
+/// case .variable:
+/// // {{ value }} and {{{ value }}}
+///
+/// // Return the built-in Swift String Interpolation of the value:
+/// return Rendering("\(value)", .text)
+///
+/// case .section:
+/// // {{# value }}...{{/ value }}
+///
+/// // Push the value on the top of the context stack:
+/// let context = info.context.extendedContext(Box(value))
+///
+/// // Renders the inner content of the section tag:
+/// return try info.tag.render(context)
+/// }
+/// }
+///
+/// let template = try! Template(string: "{{value}}")
+/// let rendering = try! template.render(["value": renderValue])
+///
+/// - parameter info: A RenderingInfo.
+/// - parameter error: If there is a problem in the rendering, throws an error
+/// that describes the problem.
+/// - returns: A Rendering.
+/// */
public typealias RenderFunction = (_ info: RenderingInfo) throws -> Rendering
// -----------------------------------------------------------------------------
// MARK: - Mustache lambdas
-/**
-Builds a `RenderFunction` which conforms to the "lambda" defined by the
-Mustache specification (see https://github.com/mustache/spec/blob/v1.1.2/specs/%7Elambdas.yml).
-
-The `lambda` parameter is a function which takes the unrendered context of a
-section, and returns a String. The returned `RenderFunction` renders this string
-against the current delimiters.
-
-For example:
-
- let template = try! Template(string: "{{#bold}}{{name}} has a Mustache!{{/bold}}")
-
- let bold = Lambda { (string) in "\(string)" }
- let data = [
- "name": Box("Clark Gable"),
- "bold": Box(bold)]
-
- // Renders "Clark Gable has a Mustache."
- let rendering = try! template.render(Box(data))
-
-**Warning**: the returned String is *parsed* each time the lambda is executed.
-In the example above, this is inefficient because the inner content of the
-bolded section has already been parsed with its template. You may prefer the raw
-`RenderFunction` type, capable of an equivalent and faster implementation:
-
- let bold: RenderFunction = { (info: RenderingInfo) in
- let rendering = try info.tag.render(info.context)
- return Rendering("\(rendering.string)", rendering.contentType)
- }
- let data = [
- "name": Box("Clark Gable"),
- "bold": Box(bold)]
-
- // Renders "Lionel Richie has a Mustache."
- let rendering = try! template.render(Box(data))
-
-- parameter lambda: A `String -> String` function.
-- returns: A RenderFunction.
-*/
+/// Creates a `RenderFunction` which conforms to the "lambda" defined by the
+/// Mustache specification (see https://github.com/mustache/spec/blob/v1.1.2/specs/%7Elambdas.yml).
+///
+/// The `lambda` parameter is a function which takes the unrendered context of a
+/// section, and returns a String. The returned `RenderFunction` renders this
+/// string against the current delimiters.
+///
+/// For example:
+///
+/// let template = try! Template(string: "{{#bold}}{{name}} has a Mustache!{{/bold}}")
+///
+/// let bold = Lambda { (string) in "\(string)" }
+/// let data: [String: Any] = [
+/// "name": "Clark Gable",
+/// "bold": bold]
+///
+/// // Renders "Clark Gable has a Mustache."
+/// let rendering = try! template.render(data)
+///
+/// **Warning**: the returned String is *parsed* each time the lambda is
+/// executed. In the example above, this is inefficient because the inner
+/// content of the bolded section has already been parsed with its template. You
+/// may prefer the raw `RenderFunction` type, capable of an equivalent and
+/// faster implementation:
+///
+/// let bold: RenderFunction = { (info: RenderingInfo) in
+/// let rendering = try info.tag.render(info.context)
+/// return Rendering("\(rendering.string)", rendering.contentType)
+/// }
+/// let data: [String: Any] = [
+/// "name": "Clark Gable",
+/// "bold": bold]
+///
+/// // Renders "Lionel Richie has a Mustache."
+/// let rendering = try! template.render(data)
+///
+/// - parameter lambda: A `String -> String` function.
+/// - returns: A RenderFunction.
public func Lambda(_ lambda: @escaping (String) -> String) -> RenderFunction {
return { (info: RenderingInfo) in
switch info.tag.type {
@@ -577,45 +549,43 @@ public func Lambda(_ lambda: @escaping (String) -> String) -> RenderFunction {
}
-/**
-Builds a `RenderFunction` which conforms to the "lambda" defined by the
-Mustache specification (see https://github.com/mustache/spec/blob/v1.1.2/specs/%7Elambdas.yml).
-
-The `lambda` parameter is a function without any argument that returns a String.
-The returned `RenderFunction` renders this string against the default `{{` and
-`}}` delimiters.
-
-For example:
-
- let template = try! Template(string: "{{fullName}} has a Mustache.")
-
- let fullName = Lambda { "{{firstName}} {{lastName}}" }
- let data = [
- "firstName": Box("Lionel"),
- "lastName": Box("Richie"),
- "fullName": Box(fullName)]
-
- // Renders "Lionel Richie has a Mustache."
- let rendering = try! template.render(Box(data))
-
-**Warning**: the returned String is *parsed* each time the lambda is executed.
-In the example above, this is inefficient because the same
-`"{{firstName}} {{lastName}}"` would be parsed several times. You may prefer
-using a Template instead of a lambda (see the documentation of
-`Template.mustacheBox` for more information):
-
- let fullName = try! Template(string:"{{firstName}} {{lastName}}")
- let data = [
- "firstName": Box("Lionel"),
- "lastName": Box("Richie"),
- "fullName": Box(fullName)]
-
- // Renders "Lionel Richie has a Mustache."
- let rendering = try! template.render(Box(data))
-
-- parameter lambda: A `() -> String` function.
-- returns: A RenderFunction.
-*/
+/// Creates a `RenderFunction` which conforms to the "lambda" defined by the
+/// Mustache specification (see https://github.com/mustache/spec/blob/v1.1.2/specs/%7Elambdas.yml).
+///
+/// The `lambda` parameter is a function without any argument that returns a
+/// String. The returned `RenderFunction` renders this string against the
+/// default `{{` and `}}` delimiters.
+///
+/// For example:
+///
+/// let template = try! Template(string: "{{fullName}} has a Mustache.")
+///
+/// let fullName = Lambda { "{{firstName}} {{lastName}}" }
+/// let data: [String: Any] = [
+/// "firstName": "Lionel",
+/// "lastName": "Richie",
+/// "fullName": fullName]
+///
+/// // Renders "Lionel Richie has a Mustache."
+/// let rendering = try! template.render(data)
+///
+/// **Warning**: the returned String is *parsed* each time the lambda is
+/// executed. In the example above, this is inefficient because the same
+/// `"{{firstName}} {{lastName}}"` would be parsed several times. You may prefer
+/// using a Template instead of a lambda (see the documentation of
+/// `Template.mustacheBox` for more information):
+///
+/// let fullName = try! Template(string:"{{firstName}} {{lastName}}")
+/// let data: [String: Any] = [
+/// "firstName": "Lionel",
+/// "lastName": "Richie",
+/// "fullName": fullName]
+///
+/// // Renders "Lionel Richie has a Mustache."
+/// let rendering = try! template.render(data)
+///
+/// - parameter lambda: A `() -> String` function.
+/// - returns: A RenderFunction.
public func Lambda(_ lambda: @escaping () -> String) -> RenderFunction {
return { (info: RenderingInfo) in
switch info.tag.type {
@@ -695,12 +665,10 @@ public func Lambda(_ lambda: @escaping () -> String) -> RenderFunction {
}
-/**
-`Rendering` is a type that wraps a rendered String, and its content type (HTML
-or Text).
-
-See `RenderFunction` and `FilterFunction` for more information.
-*/
+/// `Rendering` is a type that wraps a rendered String, and its content type
+/// (HTML or Text).
+///
+/// See `RenderFunction` and `FilterFunction` for more information.
public struct Rendering {
/// The rendered string
@@ -709,17 +677,14 @@ public struct Rendering {
/// The content type of the rendering
public let contentType: ContentType
- /**
- Builds a Rendering with a String and a ContentType.
-
- Rendering("foo") // Defaults to Text
- Rendering("foo", .Text)
- Rendering("foo", .HTML)
-
- - parameter string: A string.
- - parameter contentType: A content type.
- - returns: A Rendering.
- */
+ /// Creates a Rendering with a String and a ContentType.
+ ///
+ /// Rendering("foo") // Defaults to text
+ /// Rendering("foo", .text)
+ /// Rendering("foo", .html)
+ ///
+ /// - parameter string: A string.
+ /// - parameter contentType: A content type.
public init(_ string: String, _ contentType: ContentType = .text) {
self.string = string
self.contentType = contentType
@@ -742,11 +707,9 @@ extension Rendering : CustomDebugStringConvertible {
}
-/**
-`RenderingInfo` provides information about a rendering.
-
-See `RenderFunction` for more information.
-*/
+/// `RenderingInfo` provides information about a rendering.
+///
+/// See `RenderFunction` for more information.
public struct RenderingInfo {
/// The currently rendered tag.
@@ -773,95 +736,91 @@ public struct RenderingInfo {
// =============================================================================
// MARK: - WillRenderFunction
-/**
-Once a `WillRenderFunction` has entered the context stack, it is called just
-before tags are about to render, and has the opportunity to replace the value
-they are about to render.
-
- let logTags: WillRenderFunction = { (tag: Tag, box: MustacheBox) in
- print("\(tag) will render \(box.value!)")
- return box
- }
-
- // By entering the base context of the template, the logTags function
- // will be notified of all tags.
- let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
- template.extendBaseContext(Box(logTags))
-
- // Prints:
- // {{# user }} at line 1 will render { firstName = Errol; lastName = Flynn; }
- // {{ firstName }} at line 1 will render Errol
- // {{ lastName }} at line 1 will render Flynn
- let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
- try! template.render(Box(data))
-
-`WillRenderFunction` don't have to enter the base context of a template to
-perform: they can enter the context stack just like any other value, by being
-attached to a section. In this case, they are only notified of tags inside that
-section.
-
- let template = try! Template(string: "{{# user }}{{ firstName }} {{# spy }}{{ lastName }}{{/ spy }}{{/ user }}")
-
- // Prints:
- // {{ lastName }} at line 1 will render Flynn
- let data = [
- "user": Box(["firstName": "Errol", "lastName": "Flynn"]),
- "spy": Box(logTags)
- ]
- try! template.render(Box(data))
-*/
+/// Once a `WillRenderFunction` has entered the context stack, it is called just
+/// before tags are about to render, and has the opportunity to replace the
+/// value they are about to render.
+///
+/// let logTags: WillRenderFunction = { (tag: Tag, box: MustacheBox) in
+/// print("\(tag) will render \(box.value!)")
+/// return box // don't replace
+/// }
+///
+/// // By entering the base context of the template, the logTags function
+/// // will be notified of all tags.
+/// let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
+/// template.extendBaseContext(logTags)
+///
+/// // Prints:
+/// // {{# user }} at line 1 will render { firstName = Errol; lastName = Flynn; }
+/// // {{ firstName }} at line 1 will render Errol
+/// // {{ lastName }} at line 1 will render Flynn
+/// let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
+/// try! template.render(data)
+///
+/// `WillRenderFunction` don't have to enter the base context of a template to
+/// perform: they can enter the context stack just like any other value, by
+/// being attached to a section. In this case, they are only notified of tags
+/// inside that section.
+///
+/// let template = try! Template(string: "{{# user }}{{ firstName }} {{# spy }}{{ lastName }}{{/ spy }}{{/ user }}")
+///
+/// // Prints:
+/// // {{ lastName }} at line 1 will render Flynn
+/// let data: [String: Any] = [
+/// "user": ["firstName": "Errol", "lastName": "Flynn"],
+/// "spy": logTags
+/// ]
+/// try! template.render(data)
+///
+/// - seealso: DidRenderFunction
public typealias WillRenderFunction = (_ tag: Tag, _ box: MustacheBox) -> Any?
// =============================================================================
// MARK: - DidRenderFunction
-/**
-Once a DidRenderFunction has entered the context stack, it is called just
-after tags have been rendered.
-
- let logRenderings: DidRenderFunction = { (tag: Tag, box: MustacheBox, string: String?) in
- println("\(tag) did render \(box.value!) as `\(string!)`")
- }
-
- // By entering the base context of the template, the logRenderings function
- // will be notified of all tags.
- let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
- template.extendBaseContext(Box(logRenderings))
-
- // Renders "Errol Flynn"
- //
- // Prints:
- // {{ firstName }} at line 1 did render Errol as `Errol`
- // {{ lastName }} at line 1 did render Flynn as `Flynn`
- // {{# user }} at line 1 did render { firstName = Errol; lastName = Flynn; } as `Errol Flynn`
- let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
- try! template.render(Box(data))
-
-DidRender functions don't have to enter the base context of a template to
-perform: they can enter the context stack just like any other value, by being
-attached to a section. In this case, they are only notified of tags inside that
-section.
-
- let template = try! Template(string: "{{# user }}{{ firstName }} {{# spy }}{{ lastName }}{{/ spy }}{{/ user }}")
-
- // Renders "Errol Flynn"
- //
- // Prints:
- // {{ lastName }} at line 1 did render Flynn as `Flynn`
- let data = [
- "user": Box(["firstName": "Errol", "lastName": "Flynn"]),
- "spy": Box(logRenderings)
- ]
- try! template.render(Box(data))
-
-The string argument of DidRenderFunction is optional: it is nil if and only if
-the tag could not render because of a rendering error.
-
-See also:
-
-- WillRenderFunction
-*/
+/// Once a DidRenderFunction has entered the context stack, it is called just
+/// after tags have been rendered.
+///
+/// let logRenderings: DidRenderFunction = { (tag: Tag, box: MustacheBox, string: String?) in
+/// print("\(tag) did render \(box.value!) as `\(string!)`")
+/// }
+///
+/// // By entering the base context of the template, the logRenderings function
+/// // will be notified of all tags.
+/// let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}")
+/// template.extendBaseContext(logRenderings)
+///
+/// // Renders "Errol Flynn"
+/// //
+/// // Prints:
+/// // {{ firstName }} at line 1 did render Errol as `Errol`
+/// // {{ lastName }} at line 1 did render Flynn as `Flynn`
+/// // {{# user }} at line 1 did render { firstName = Errol; lastName = Flynn; } as `Errol Flynn`
+/// let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]]
+/// try! template.render(data)
+///
+/// DidRender functions don't have to enter the base context of a template to
+/// perform: they can enter the context stack just like any other value, by being
+/// attached to a section. In this case, they are only notified of tags inside that
+/// section.
+///
+/// let template = try! Template(string: "{{# user }}{{ firstName }} {{# spy }}{{ lastName }}{{/ spy }}{{/ user }}")
+///
+/// // Renders "Errol Flynn"
+/// //
+/// // Prints:
+/// // {{ lastName }} at line 1 did render Flynn as `Flynn`
+/// let data: [String: Any] = [
+/// "user": ["firstName": "Errol", "lastName": "Flynn"],
+/// "spy": logRenderings
+/// ]
+/// try! template.render(data)
+///
+/// The string argument of DidRenderFunction is optional: it is nil if and only
+/// if the tag could not render because of a rendering error.
+///
+/// - seealso: WillRenderFunction
public typealias DidRenderFunction = (_ tag: Tag, _ box: MustacheBox, _ string: String?) -> Void
From 5a79ad78331e9eec2781a2104795d16a8106e125 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 11:03:25 +0200
Subject: [PATCH 35/46] Documentation
---
Sources/MustacheBox.swift | 558 ++++++++++++++++++--------------------
1 file changed, 264 insertions(+), 294 deletions(-)
diff --git a/Sources/MustacheBox.swift b/Sources/MustacheBox.swift
index 964def6b..38566c23 100644
--- a/Sources/MustacheBox.swift
+++ b/Sources/MustacheBox.swift
@@ -24,36 +24,17 @@
import Foundation
-/**
-Mustache templates don't eat raw values: they eat values boxed in `MustacheBox`.
-
-To box something in a `MustacheBox`, you use one variant of the `Box()`
-function. It comes in several variants so that nearly anything can be boxed and
-feed templates:
-
-- Basic Swift values:
-
- template.render(Box("foo"))
-
-- Dictionaries & collections:
-
- template.render(Box(["numbers": [1,2,3]]))
-
-- Custom types via the `MustacheBoxable` protocol:
-
- extension User: MustacheBoxable { ... }
- template.render(Box(user))
-
-- Functions such as `FilterFunction`, `RenderFunction`, `WillRenderFunction` and
- `DidRenderFunction`:
-
- let square = Filter { (x: Int?) in Box(x! * x!) }
- template.registerInBaseContext("square", Box(square))
-
-**Warning**: the fact that `MustacheBox` is a subclass of NSObject is an
-implementation detail that is enforced by the Swift 2 language itself. This may
-change in the future: do not rely on it.
-*/
+/// Mustache templates don't eat raw values: they eat values boxed
+/// in `MustacheBox`.
+///
+/// Boxing is generally automatic:
+///
+/// // The render method automatically boxes the dictionary:
+/// template.render(["name": "Arthur"])
+///
+/// **Warning**: the fact that `MustacheBox` is a subclass of NSObject is an
+/// implementation detail that is enforced by the Swift language itself. This
+/// may change in the future: do not rely on it.
final public class MustacheBox : NSObject {
// IMPLEMENTATION NOTE
@@ -116,39 +97,30 @@ final public class MustacheBox : NSObject {
/// The only empty box is `Box()`.
public let isEmpty: Bool
- /**
- The boolean value of the box.
-
- It tells whether the Box should trigger or prevent the rendering of regular
- `{{#section}}...{{/}}` and inverted `{{^section}}...{{/}}`.
- */
+ /// The boolean value of the box.
+ ///
+ /// It tells whether the Box should trigger or prevent the rendering of
+ /// regular `{{#section}}...{{/}}` and inverted `{{^section}}...{{/}}`.
public let boolValue: Bool
- /**
- If the boxed value can be iterated (Swift collection, NSArray, NSSet, etc.),
- returns an array of `MustacheBox`.
- */
+ /// If the boxed value can be iterated (array or set), returns an array
+ /// of `MustacheBox`.
public var arrayValue: [MustacheBox]? {
return converter?.arrayValue()
}
- /**
- If the boxed value is a dictionary (Swift dictionary, NSDictionary, etc.),
- returns a dictionary `[String: MustacheBox]`.
- */
+ /// If the boxed value is a dictionary, returns a `[String: MustacheBox]`.
public var dictionaryValue: [String: MustacheBox]? {
return converter?.dictionaryValue()
}
- /**
- Extracts a key out of a box.
-
- let box = Box(["firstName": "Arthur"])
- box.mustacheBox(forKey: "firstName").value // "Arthur"
-
- - parameter key: A key.
- - returns: The MustacheBox for *key*.
- */
+ /// Extracts a key out of a box.
+ ///
+ /// let box = Box(["firstName": "Arthur"])
+ /// box.mustacheBox(forKey: "firstName").value // "Arthur"
+ ///
+ /// - parameter key: A key.
+ /// - returns: The MustacheBox for *key*.
public func mustacheBox(forKey key: String) -> MustacheBox {
guard let keyedSubscript = keyedSubscript else {
return EmptyBox
@@ -176,247 +148,245 @@ final public class MustacheBox : NSObject {
// -------------------------------------------------------------------------
// MARK: - Multi-facetted Box Initialization
- /**
- This is the most low-level initializer of MustacheBox.
-
- It is suited for building "advanced" boxes. There are simpler versions of
- the `Box` function that may well better suit your need: you should check
- them.
-
- This initializer can take up to seven parameters, all optional, that define
- how the box interacts with the Mustache engine:
-
- - `value`: an optional boxed value
- - `boolValue`: an optional boolean value for the Box.
- - `keyedSubscript`: an optional KeyedSubscriptFunction
- - `filter`: an optional FilterFunction
- - `render`: an optional RenderFunction
- - `willRender`: an optional WillRenderFunction
- - `didRender`: an optional DidRenderFunction
-
-
- To illustrate the usage of all those parameters, let's look at how the
- `{{f(a)}}` tag is rendered.
-
- First the `a` and `f` expressions are evaluated. The Mustache engine looks
- in the context stack for boxes whose *keyedSubscript* return non-empty boxes
- for the keys "a" and "f". Let's call them aBox and fBox.
-
- Then the *filter* of the fBox is evaluated with aBox as an argument. It is
- likely that the result depends on the *value* of the aBox: it is the
- resultBox.
-
- Then the Mustache engine is ready to render resultBox. It looks in the
- context stack for boxes whose *willRender* function is defined. Those
- willRender functions have the opportunity to process the resultBox, and
- eventually provide the box that will be actually rendered: the renderedBox.
-
- The renderedBox has a *render* function: it is evaluated by the Mustache
- engine which appends its result to the final rendering.
-
- Finally the Mustache engine looks in the context stack for boxes whose
- *didRender* function is defined, and call them.
-
-
- ### value
-
- The optional `value` parameter gives the boxed value. The value is used when
- the box is rendered (unless you provide a custom RenderFunction). It is also
- returned by the `value` property of MustacheBox.
-
- let aBox = MustacheBox(value: 1)
-
- // Renders "1"
- let template = try! Template(string: "{{a}}")
- try! template.render(Box(["a": aBox]))
-
-
- ### boolValue
-
- The optional `boolValue` parameter tells whether the Box should trigger or
- prevent the rendering of regular `{{#section}}...{{/}}` and inverted
- `{{^section}}...{{/}}` tags. The default boolValue is true, unless the
- Box is initialized without argument to build the empty box.
-
- // Render "true", "false"
- let template = try! Template(string:"{{#.}}true{{/.}}{{^.}}false{{/.}}")
- try! template.render(MustacheBox(boolValue: true))
- try! template.render(MustacheBox(boolValue: false))
-
-
- ### keyedSubscript
-
- The optional `keyedSubscript` parameter is a `KeyedSubscriptFunction` that
- lets the Mustache engine extract keys out of the box. For example, the
- `{{a}}` tag would call the subscript function with `"a"` as an argument, and
- render the returned box.
-
- The default value is nil, which means that no key can be extracted.
-
- See `KeyedSubscriptFunction` for a full discussion of this type.
-
- let box = MustacheBox(keyedSubscript: { (key: String) in
- return Box("key:\(key)")
- })
-
- // Renders "key:a"
- let template = try! Template(string:"{{a}}")
- try! template.render(box)
-
-
- ### filter
-
- The optional `filter` parameter is a `FilterFunction` that lets the Mustache
- engine evaluate filtered expression that involve the box. The default value
- is nil, which means that the box can not be used as a filter.
-
- See `FilterFunction` for a full discussion of this type.
-
- let box = MustacheBox(filter: Filter { (x: Int?) in
- return Box(x! * x!)
- })
-
- // Renders "100"
- let template = try! Template(string:"{{square(x)}}")
- try! template.render(Box(["square": box, "x": Box(10)]))
-
-
- ### render
-
- The optional `render` parameter is a `RenderFunction` that is evaluated when
- the Box is rendered.
-
- The default value is nil, which makes the box perform default Mustache
- rendering:
-
- - `{{box}}` renders the built-in Swift String Interpolation of the value,
- HTML-escaped.
-
- - `{{{box}}}` renders the built-in Swift String Interpolation of the value,
- not HTML-escaped.
-
- - `{{#box}}...{{/box}}` does not render if `boolValue` is false. Otherwise,
- it pushes the box on the top of the context stack, and renders the section
- once.
-
- - `{{^box}}...{{/box}}` renders once if `boolValue` is false. Otherwise, it
- does not render.
-
- See `RenderFunction` for a full discussion of this type.
-
- let box = MustacheBox(render: { (info: RenderingInfo) in
- return Rendering("foo")
- })
-
- // Renders "foo"
- let template = try! Template(string:"{{.}}")
- try! template.render(box)
-
-
- ### willRender, didRender
-
- The optional `willRender` and `didRender` parameters are a
- `WillRenderFunction` and `DidRenderFunction` that are evaluated for all tags
- as long as the box is in the context stack.
-
- See `WillRenderFunction` and `DidRenderFunction` for a full discussion of
- those types.
-
- let box = MustacheBox(willRender: { (tag: Tag, box: MustacheBox) in
- return Box("baz")
- })
-
- // Renders "baz baz"
- let template = try! Template(string:"{{#.}}{{foo}} {{bar}}{{/.}}")
- try! template.render(box)
-
-
- ### Multi-facetted boxes
-
- By mixing all those parameters, you can finely tune the behavior of a box.
-
- GRMustache source code ships a few multi-facetted boxes, which may inspire
- you. See for example:
-
- - NSFormatter.mustacheBox
- - HTMLEscape.mustacheBox
- - StandardLibrary.Localizer.mustacheBox
-
- Let's give an example:
-
- // A regular type:
-
- struct Person {
- let firstName: String
- let lastName: String
- }
-
- We want:
-
- 1. `{{person.firstName}}` and `{{person.lastName}}` should render the
- matching properties.
- 2. `{{person}}` should render the concatenation of the first and last names.
-
- We'll provide a `KeyedSubscriptFunction` to implement 1, and a
- `RenderFunction` to implement 2:
-
- // Have Person conform to MustacheBoxable so that we can box people, and
- // render them:
-
- extension Person : MustacheBoxable {
-
- // MustacheBoxable protocol requires objects to implement this property
- // and return a MustacheBox:
-
- var mustacheBox: MustacheBox {
-
- // A person is a multi-facetted object:
- return MustacheBox(
- // It has a value:
- value: self,
-
- // It lets Mustache extracts properties by name:
- keyedSubscript: { (key: String) -> Any? in
- switch key {
- case "firstName": return Box(self.firstName)
- case "lastName": return Box(self.lastName)
- default: return Box()
- }
- },
-
- // It performs custom rendering:
- render: { (info: RenderingInfo) -> Rendering in
- switch info.tag.type {
- case .Variable:
- // {{ person }}
- return Rendering("\(self.firstName) \(self.lastName)")
- case .Section:
- // {{# person }}...{{/}}
- //
- // Perform the default rendering: push self on the top
- // of the context stack, and render the section:
- let context = info.context.extendedContext(Box(self))
- return try info.tag.render(context)
- }
- }
- )
- }
- }
-
- // Renders "The person is Errol Flynn"
- let person = Person(firstName: "Errol", lastName: "Flynn")
- let template = try! Template(string: "{{# person }}The person is {{.}}{{/ person }}")
- try! template.render(Box(["person": person]))
-
- - parameter value: An optional boxed value.
- - parameter boolValue: An optional boolean value for the Box.
- - parameter keyedSubscript: An optional `KeyedSubscriptFunction`.
- - parameter filter: An optional `FilterFunction`.
- - parameter render: An optional `RenderFunction`.
- - parameter willRender: An optional `WillRenderFunction`.
- - parameter didRender: An optional `DidRenderFunction`.
- - returns: A MustacheBox.
- */
+ /// This is the low-level initializer of MustacheBox, suited for building
+ /// "advanced" boxes.
+ ///
+ /// This initializer can take up to seven parameters, all optional, that
+ /// define how the box interacts with the Mustache engine:
+ ///
+ /// - `value`: an optional boxed value
+ /// - `boolValue`: an optional boolean value for the Box.
+ /// - `keyedSubscript`: an optional KeyedSubscriptFunction
+ /// - `filter`: an optional FilterFunction
+ /// - `render`: an optional RenderFunction
+ /// - `willRender`: an optional WillRenderFunction
+ /// - `didRender`: an optional DidRenderFunction
+ ///
+ ///
+ /// To illustrate the usage of all those parameters, let's look at how the
+ /// `{{f(a)}}` tag is rendered.
+ ///
+ /// First the `a` and `f` expressions are evaluated. The Mustache engine
+ /// looks in the context stack for boxes whose *keyedSubscript* return
+ /// non-empty boxes for the keys "a" and "f". Let's call them aBox and fBox.
+ ///
+ /// Then the *filter* of the fBox is evaluated with aBox as an argument. It
+ /// is likely that the result depends on the *value* of the aBox: it is the
+ /// resultBox.
+ ///
+ /// Then the Mustache engine is ready to render resultBox. It looks in the
+ /// context stack for boxes whose *willRender* function is defined. Those
+ /// willRender functions have the opportunity to process the resultBox, and
+ /// eventually provide the box that will be actually rendered:
+ /// the renderedBox.
+ ///
+ /// The renderedBox has a *render* function: it is evaluated by the Mustache
+ /// engine which appends its result to the final rendering.
+ ///
+ /// Finally the Mustache engine looks in the context stack for boxes whose
+ /// *didRender* function is defined, and call them.
+ ///
+ ///
+ /// ### value
+ ///
+ /// The optional `value` parameter gives the boxed value. The value is used
+ /// when the box is rendered (unless you provide a custom RenderFunction).
+ /// It is also returned by the `value` property of MustacheBox.
+ ///
+ /// let aBox = MustacheBox(value: 1)
+ ///
+ /// // Renders "1"
+ /// let template = try! Template(string: "{{a}}")
+ /// try! template.render(["a": aBox])
+ ///
+ ///
+ /// ### boolValue
+ ///
+ /// The optional `boolValue` parameter tells whether the Box should trigger
+ /// or prevent the rendering of regular `{{#section}}...{{/}}` and inverted
+ /// `{{^section}}...{{/}}` tags. The default boolValue is true, unless the
+ /// Box is initialized without argument to build the empty box.
+ ///
+ /// // Render "true", then "false"
+ /// let template = try! Template(string:"{{#.}}true{{/.}}{{^.}}false{{/.}}")
+ /// try! template.render(MustacheBox(boolValue: true))
+ /// try! template.render(MustacheBox(boolValue: false))
+ ///
+ ///
+ /// ### keyedSubscript
+ ///
+ /// The optional `keyedSubscript` parameter is a `KeyedSubscriptFunction`
+ /// that lets the Mustache engine extract keys out of the box. For example,
+ /// the `{{a}}` tag would call the subscript function with `"a"` as an
+ /// argument, and render the returned box.
+ ///
+ /// The default value is nil, which means that no key can be extracted.
+ ///
+ /// See `KeyedSubscriptFunction` for a full discussion of this type.
+ ///
+ /// let box = MustacheBox(keyedSubscript: { (key: String) in
+ /// return Box("key:\(key)")
+ /// })
+ ///
+ /// // Renders "key:a"
+ /// let template = try! Template(string:"{{a}}")
+ /// try! template.render(box)
+ ///
+ ///
+ /// ### filter
+ ///
+ /// The optional `filter` parameter is a `FilterFunction` that lets the
+ /// Mustache engine evaluate filtered expression that involve the box. The
+ /// default value is nil, which means that the box can not be used as
+ /// a filter.
+ ///
+ /// See `FilterFunction` for a full discussion of this type.
+ ///
+ /// let box = MustacheBox(filter: Filter { (x: Int?) in
+ /// return Box(x! * x!)
+ /// })
+ ///
+ /// // Renders "100"
+ /// let template = try! Template(string:"{{square(x)}}")
+ /// try! template.render(["square": box, "x": 10])
+ ///
+ ///
+ /// ### render
+ ///
+ /// The optional `render` parameter is a `RenderFunction` that is evaluated
+ /// when the Box is rendered.
+ ///
+ /// The default value is nil, which makes the box perform default Mustache
+ /// rendering:
+ ///
+ /// - `{{box}}` renders the built-in Swift String Interpolation of the value,
+ /// HTML-escaped.
+ ///
+ /// - `{{{box}}}` renders the built-in Swift String Interpolation of the
+ /// value, not HTML-escaped.
+ ///
+ /// - `{{#box}}...{{/box}}` does not render if `boolValue` is false.
+ /// Otherwise, it pushes the box on the top of the context stack, and
+ /// renders the section once.
+ ///
+ /// - `{{^box}}...{{/box}}` renders once if `boolValue` is false. Otherwise,
+ /// it does not render.
+ ///
+ /// See `RenderFunction` for a full discussion of this type.
+ ///
+ /// let box = MustacheBox(render: { (info: RenderingInfo) in
+ /// return Rendering("foo")
+ /// })
+ ///
+ /// // Renders "foo"
+ /// let template = try! Template(string:"{{.}}")
+ /// try! template.render(box)
+ ///
+ ///
+ /// ### willRender, didRender
+ ///
+ /// The optional `willRender` and `didRender` parameters are a
+ /// `WillRenderFunction` and `DidRenderFunction` that are evaluated for all
+ /// tags as long as the box is in the context stack.
+ ///
+ /// See `WillRenderFunction` and `DidRenderFunction` for a full discussion of
+ /// those types.
+ ///
+ /// let box = MustacheBox(willRender: { (tag: Tag, box: MustacheBox) in
+ /// return Box("baz")
+ /// })
+ ///
+ /// // Renders "baz baz"
+ /// let template = try! Template(string:"{{#.}}{{foo}} {{bar}}{{/.}}")
+ /// try! template.render(box)
+ ///
+ ///
+ /// ### Multi-facetted boxes
+ ///
+ /// By mixing all those parameters, you can finely tune the behavior of
+ /// a box.
+ ///
+ /// GRMustache source code ships a few multi-facetted boxes, which may
+ /// inspire you. See for example:
+ ///
+ /// - Formatter.mustacheBox
+ /// - HTMLEscape.mustacheBox
+ /// - StandardLibrary.Localizer.mustacheBox
+ ///
+ /// Let's give an example:
+ ///
+ /// // A regular type:
+ ///
+ /// struct Person {
+ /// let firstName: String
+ /// let lastName: String
+ /// }
+ ///
+ /// We want:
+ ///
+ /// 1. `{{person.firstName}}` and `{{person.lastName}}` should render the
+ /// matching properties.
+ /// 2. `{{person}}` should render the concatenation of the first and last names.
+ ///
+ /// We'll provide a `KeyedSubscriptFunction` to implement 1, and a
+ /// `RenderFunction` to implement 2:
+ ///
+ /// // Have Person conform to MustacheBoxable so that we can box people, and
+ /// // render them:
+ ///
+ /// extension Person : MustacheBoxable {
+ ///
+ /// // MustacheBoxable protocol requires objects to implement this property
+ /// // and return a MustacheBox:
+ ///
+ /// var mustacheBox: MustacheBox {
+ ///
+ /// // A person is a multi-facetted object:
+ /// return MustacheBox(
+ /// // It has a value:
+ /// value: self,
+ ///
+ /// // It lets Mustache extracts properties by name:
+ /// keyedSubscript: { (key: String) -> Any? in
+ /// switch key {
+ /// case "firstName": return self.firstName
+ /// case "lastName": return self.lastName
+ /// default: return nil
+ /// }
+ /// },
+ ///
+ /// // It performs custom rendering:
+ /// render: { (info: RenderingInfo) -> Rendering in
+ /// switch info.tag.type {
+ /// case .variable:
+ /// // {{ person }}
+ /// return Rendering("\(self.firstName) \(self.lastName)")
+ /// case .section:
+ /// // {{# person }}...{{/}}
+ /// //
+ /// // Perform the default rendering: push self on the top
+ /// // of the context stack, and render the section:
+ /// let context = info.context.extendedContext(Box(self))
+ /// return try info.tag.render(context)
+ /// }
+ /// }
+ /// )
+ /// }
+ /// }
+ ///
+ /// // Renders "The person is Errol Flynn"
+ /// let person = Person(firstName: "Errol", lastName: "Flynn")
+ /// let template = try! Template(string: "{{# person }}The person is {{.}}{{/ person }}")
+ /// try! template.render(["person": person])
+ ///
+ /// - parameter value: An optional boxed value.
+ /// - parameter boolValue: An optional boolean value for the Box.
+ /// - parameter keyedSubscript: An optional `KeyedSubscriptFunction`.
+ /// - parameter filter: An optional `FilterFunction`.
+ /// - parameter render: An optional `RenderFunction`.
+ /// - parameter willRender: An optional `WillRenderFunction`.
+ /// - parameter didRender: An optional `DidRenderFunction`.
+ /// - returns: A MustacheBox.
public convenience init(
value: Any? = nil,
boolValue: Bool? = nil,
From 136c4688578f69d65da8d8e0ec7826e076de5457 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 11:11:19 +0200
Subject: [PATCH 36/46] Documentation
---
Sources/CoreGraphics.swift | 6 +-
Sources/Foundation.swift | 69 ++----
Sources/SwiftStandardLibrary.swift | 354 +++++++++++------------------
3 files changed, 152 insertions(+), 277 deletions(-)
diff --git a/Sources/CoreGraphics.swift b/Sources/CoreGraphics.swift
index e4b38d2e..9753f975 100644
--- a/Sources/CoreGraphics.swift
+++ b/Sources/CoreGraphics.swift
@@ -29,11 +29,7 @@
/// CGFloat adopts the MustacheBoxable protocol so that it can feed
/// Mustache templates.
///
- /// You should not directly call the `mustacheBox` property. Always use
- /// the `Box()` function instead:
- ///
- /// CGFloat(3.14).mustacheBox // Valid, but discouraged
- /// Box(CGFloat(3.14)) // Preferred
+ /// You should not directly call the `mustacheBox` property.
///
/// ### Rendering
///
diff --git a/Sources/Foundation.swift b/Sources/Foundation.swift
index c9e30ef3..6d270085 100644
--- a/Sources/Foundation.swift
+++ b/Sources/Foundation.swift
@@ -29,17 +29,13 @@ extension NSObject : MustacheBoxable {
/// `NSObject` adopts the `MustacheBoxable` protocol so that it can feed
/// Mustache templates.
///
- /// You should not directly call the `mustacheBox` property. Always use the
- /// `Box()` function instead:
- ///
- /// object.mustacheBox // Valid, but discouraged
- /// Box(object) // Preferred
+ /// You should not directly call the `mustacheBox` property.
///
///
/// NSObject's default implementation handles two general cases:
///
- /// - Enumerable objects that conform to the `NSFastEnumeration` protocol, such
- /// as `NSArray` and `NSOrderedSet`.
+ /// - Enumerable objects that conform to the `NSFastEnumeration` protocol,
+ /// such as `NSArray` and `NSOrderedSet`.
/// - All other objects
///
/// GRMustache ships with a few specific classes that escape the general
@@ -94,7 +90,8 @@ extension NSObject : MustacheBoxable {
///
/// ### Rendering
///
- /// - `{{object}}` renders the result of the `description` method, HTML-escaped.
+ /// - `{{object}}` renders the result of the `description` method,
+ /// HTML-escaped.
///
/// - `{{{object}}}` renders the result of the `description` method, *not*
/// HTML-escaped.
@@ -103,7 +100,6 @@ extension NSObject : MustacheBoxable {
/// of the context stack.
///
/// - `{{^object}}...{{/object}}` does not render.
- ///
open var mustacheBox: MustacheBox {
if let enumerable = self as? NSFastEnumeration {
// Enumerable
@@ -169,11 +165,7 @@ extension NSNumber {
/// `NSNumber` adopts the `MustacheBoxable` protocol so that it can feed
/// Mustache templates.
///
- /// You should not directly call the `mustacheBox` property. Always use the
- /// `Box()` function instead:
- ///
- /// NSNumber(integer: 1).mustacheBox // Valid, but discouraged
- /// Box(NSNumber(integer: 1)) // Preferred
+ /// You should not directly call the `mustacheBox` property.
///
///
/// ### Rendering
@@ -183,8 +175,8 @@ extension NSNumber {
/// UInt64, or Double.
///
/// - `{{number}}` is rendered with built-in Swift String Interpolation.
- /// Custom formatting can be explicitly required with NSNumberFormatter,
- /// as in `{{format(a)}}` (see `NSFormatter`).
+ /// Custom formatting can be explicitly required with NumberFormatter,
+ /// as in `{{format(a)}}` (see `Formatter`).
///
/// - `{{#number}}...{{/number}}` renders if and only if `number` is
/// not 0 (zero).
@@ -234,11 +226,7 @@ extension NSString {
/// `NSString` adopts the `MustacheBoxable` protocol so that it can feed
/// Mustache templates.
///
- /// You should not directly call the `mustacheBox` property. Always use the
- /// `Box()` function instead:
- ///
- /// "foo".mustacheBox // Valid, but discouraged
- /// Box("foo") // Preferred
+ /// You should not directly call the `mustacheBox` property.
///
///
/// ### Rendering
@@ -281,12 +269,7 @@ extension NSSet {
/// try! template.render(Box(["set": Box(set)]))
///
///
- /// You should not directly call the `mustacheBox` property. Always use the
- /// `Box()` function instead:
- ///
- /// set.mustacheBox // Valid, but discouraged
- /// Box(set) // Preferred
- ///
+ /// You should not directly call the `mustacheBox` property.
///
/// ### Rendering
///
@@ -328,11 +311,7 @@ extension NSDictionary {
/// let rendering = try! template.render(Box(dictionary))
///
///
- /// You should not directly call the `mustacheBox` property. Always use the
- /// `Box()` function instead:
- ///
- /// dictionary.mustacheBox // Valid, but discouraged
- /// Box(dictionary) // Preferred
+ /// You should not directly call the `mustacheBox` property.
///
///
/// ### Rendering
@@ -349,8 +328,8 @@ extension NSDictionary {
/// - `{{^dictionary}}...{{/dictionary}}` does not render.
///
///
- /// In order to iterate over the key/value pairs of a dictionary, use the `each`
- /// filter from the Standard Library:
+ /// In order to iterate over the key/value pairs of a dictionary, use the
+ /// `each` filter from the Standard Library:
///
/// // Attach StandardLibrary.each to the key "each":
/// let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>")
@@ -358,29 +337,9 @@ extension NSDictionary {
///
/// // Renders ""
/// let dictionary = ["name": "Arthur", "age": 36] as NSDictionary
- /// let rendering = try! template.render(Box(["dictionary": dictionary]))
+ /// let rendering = try! template.render(["dictionary": dictionary])
open override var mustacheBox: MustacheBox {
return Box(self as? [AnyHashable: Any])
-// return MustacheBox(
-// converter: MustacheBox.Converter(dictionaryValue: {
-// var boxDictionary: [String: MustacheBox] = [:]
-// for (key, value) in self {
-// if let key = key as? String {
-// boxDictionary[key] = Box(value)
-// } else {
-// NSLog("Mustache: non-string key in dictionary (\(key)) is discarded.")
-// }
-// }
-// return boxDictionary
-// }),
-// value: self,
-// keyedSubscript: { (key: String) in
-// if let value = self[key] {
-// return Box(value)
-// } else {
-// return EmptyBox
-// }
-// })
}
}
diff --git a/Sources/SwiftStandardLibrary.swift b/Sources/SwiftStandardLibrary.swift
index 99f87e5e..89d12c16 100644
--- a/Sources/SwiftStandardLibrary.swift
+++ b/Sources/SwiftStandardLibrary.swift
@@ -21,34 +21,24 @@
// THE SOFTWARE.
-/**
- GRMustache provides built-in support for rendering `Double`.
- */
-
+/// GRMustache provides built-in support for rendering `Double`.
extension Double : MustacheBoxable {
- /**
- `Double` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 3.14.mustacheBox // Valid, but discouraged
- Box(3.14) // Preferred
-
-
- ### Rendering
-
- - `{{double}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#double}}...{{/double}}` renders if and only if `double` is not 0 (zero).
-
- - `{{^double}}...{{/double}}` renders if and only if `double` is 0 (zero).
-
- */
+ /// `Double` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{double}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NumberFormatter, as in
+ /// `{{format(a)}}` (see `Formatter`).
+ ///
+ /// - `{{#double}}...{{/double}}` renders if and only if `double` is not 0 (zero).
+ ///
+ /// - `{{^double}}...{{/double}}` renders if and only if `double` is 0 (zero).
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -78,34 +68,24 @@ extension Double : MustacheBoxable {
}
-/**
- GRMustache provides built-in support for rendering `Float`.
- */
-
+/// GRMustache provides built-in support for rendering `Float`.
extension Float : MustacheBoxable {
- /**
- `Float` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 3.14.mustacheBox // Valid, but discouraged
- Box(3.14) // Preferred
-
-
- ### Rendering
-
- - `{{float}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#float}}...{{/float}}` renders if and only if `float` is not 0 (zero).
-
- - `{{^float}}...{{/float}}` renders if and only if `float` is 0 (zero).
-
- */
+ /// `Float` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ /// templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{float}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NumberFormatter, as in
+ /// `{{format(a)}}` (see `Formatter`).
+ ///
+ /// - `{{#float}}...{{/float}}` renders if and only if `float` is not 0 (zero).
+ ///
+ /// - `{{^float}}...{{/float}}` renders if and only if `float` is 0 (zero).
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -135,45 +115,35 @@ extension Float : MustacheBoxable {
}
-/**
- GRMustache provides built-in support for rendering `String`.
- */
-
+/// GRMustache provides built-in support for rendering `String`.
extension String : MustacheBoxable {
- /**
- `String` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- "foo".mustacheBox // Valid, but discouraged
- Box("foo") // Preferred
-
-
- ### Rendering
-
- - `{{string}}` renders the string, HTML-escaped.
-
- - `{{{string}}}` renders the string, *not* HTML-escaped.
-
- - `{{#string}}...{{/string}}` renders if and only if `string` is not empty.
-
- - `{{^string}}...{{/string}}` renders if and only if `string` is empty.
-
- HTML-escaping of `{{string}}` tags is disabled for Text templates: see
- `Configuration.contentType` for a full discussion of the content type of
- templates.
-
-
- ### Keys exposed to templates
-
- A string can be queried for the following keys:
-
- - `length`: the number of characters in the string.
-
- */
+ /// `String` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{string}}` renders the string, HTML-escaped.
+ ///
+ /// - `{{{string}}}` renders the string, *not* HTML-escaped.
+ ///
+ /// - `{{#string}}...{{/string}}` renders if and only if `string` is not empty.
+ ///
+ /// - `{{^string}}...{{/string}}` renders if and only if `string` is empty.
+ ///
+ /// HTML-escaping of `{{string}}` tags is disabled for Text templates: see
+ /// `Configuration.contentType` for a full discussion of the content type of
+ /// templates.
+ ///
+ ///
+ /// ### Keys exposed to templates
+ ///
+ /// A string can be queried for the following keys:
+ ///
+ /// - `length`: the number of characters in the string.
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -190,32 +160,22 @@ extension String : MustacheBoxable {
}
-/**
-GRMustache provides built-in support for rendering `Bool`.
-*/
-
+/// GRMustache provides built-in support for rendering `Bool`.
extension Bool : MustacheBoxable {
- /**
- `Bool` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- true.mustacheBox // Valid, but discouraged
- Box(true) // Preferred
-
-
- ### Rendering
-
- - `{{bool}}` renders as `0` or `1`.
-
- - `{{#bool}}...{{/bool}}` renders if and only if `bool` is true.
-
- - `{{^bool}}...{{/bool}}` renders if and only if `bool` is false.
-
- */
+ /// `Bool` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{bool}}` renders as `0` or `1`.
+ ///
+ /// - `{{#bool}}...{{/bool}}` renders if and only if `bool` is true.
+ ///
+ /// - `{{^bool}}...{{/bool}}` renders if and only if `bool` is false.
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -245,34 +205,24 @@ extension Bool : MustacheBoxable {
}
-/**
-GRMustache provides built-in support for rendering `Int64`.
-*/
-
+/// GRMustache provides built-in support for rendering `Int64`.
extension Int64 : MustacheBoxable {
- /**
- `Int64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{int}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
-
- - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
-
- */
+ /// `Int64` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{int}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NumberFormatter,
+ /// as in `{{format(a)}}` (see `Formatter`).
+ ///
+ /// - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
+ ///
+ /// - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -302,34 +252,24 @@ extension Int64 : MustacheBoxable {
}
-/**
-GRMustache provides built-in support for rendering `Int`.
-*/
-
+/// GRMustache provides built-in support for rendering `Int`.
extension Int : MustacheBoxable {
- /**
- `Int` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{int}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
-
- - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
-
- */
+ /// `Int` adopts the `MustacheBoxable` protocol so that it can feed Mustache
+ /// templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{int}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NumberFormatter, as
+ /// in `{{format(a)}}` (see `Formatter`).
+ ///
+ /// - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero).
+ ///
+ /// - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero).
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -359,34 +299,24 @@ extension Int : MustacheBoxable {
}
-/**
-GRMustache provides built-in support for rendering `UInt64`.
-*/
-
+/// GRMustache provides built-in support for rendering `UInt64`.
extension UInt64 : MustacheBoxable {
- /**
- `UInt64` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{uint}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
-
- - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
-
- */
+ /// `UInt64` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{uint}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NumberFormatter, as
+ /// in `{{format(a)}}` (see `Formatter`).
+ ///
+ /// - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
+ ///
+ /// - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
@@ -416,34 +346,24 @@ extension UInt64 : MustacheBoxable {
}
-/**
-GRMustache provides built-in support for rendering `UInt`.
-*/
-
+/// GRMustache provides built-in support for rendering `UInt`.
extension UInt : MustacheBoxable {
- /**
- `UInt` adopts the `MustacheBoxable` protocol so that it can feed Mustache
- templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- 1.mustacheBox // Valid, but discouraged
- Box(1) // Preferred
-
-
- ### Rendering
-
- - `{{uint}}` is rendered with built-in Swift String Interpolation.
- Custom formatting can be explicitly required with NSNumberFormatter, as in
- `{{format(a)}}` (see `NSFormatter`).
-
- - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
-
- - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
-
- */
+ /// `UInt` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ ///
+ /// ### Rendering
+ ///
+ /// - `{{uint}}` is rendered with built-in Swift String Interpolation.
+ /// Custom formatting can be explicitly required with NumberFormatter, as
+ /// in `{{format(a)}}` (see `Formatter`).
+ ///
+ /// - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero).
+ ///
+ /// - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero).
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
From 708d368efc064abe7e6fbef06ecfa258d177ee3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 11:45:27 +0200
Subject: [PATCH 37/46] Documentation
---
.../MustacheDemoOSX/ViewController.swift | 2 +-
Docs/DesignNotes/Box.md | 149 -------
Sources/Template.swift | 388 ++++++++----------
3 files changed, 168 insertions(+), 371 deletions(-)
delete mode 100644 Docs/DesignNotes/Box.md
diff --git a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
index 2a1ee969..0e4e2f49 100644
--- a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
+++ b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
@@ -37,7 +37,7 @@ class ViewController: NSViewController {
template.register(percentFormatter, forKey: "percent")
template.register(StandardLibrary.each, forKey: "each")
template.register(StandardLibrary.zip, forKey: "zip")
- template.registerInBaseContext("localize", Box(StandardLibrary.Localizer(bundle: nil, table: nil)))
+ template.register(StandardLibrary.Localizer(), forKey: "localize")
template.register(StandardLibrary.HTMLEscape, forKey: "HTMLEscape")
template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
diff --git a/Docs/DesignNotes/Box.md b/Docs/DesignNotes/Box.md
deleted file mode 100644
index 311be64f..00000000
--- a/Docs/DesignNotes/Box.md
+++ /dev/null
@@ -1,149 +0,0 @@
-The Box function
-================
-
-Why do we force the user to box the rendered data with the `Box()` function?
-
-```swift
-template.registerInBaseContext("square", Box(squareFilter))
-template.render(Box(["foo": "bar"]))
-template.render(Box(profile))
-```
-
-It is not because we want it.
-
-It is because it is the only way to feed templates with a *unique* API. `Box()` is the only function the library user has to remember in order to feed templates with ints, strings, arrays, dictionaries, filters, lambdas, Obj-C objects, custom types, all of them being able to inject their behaviors in the rendering engine. Especially custom types: only them let the user escape the dull world of a Mustache engine that would only support a fixed set of blessed types.
-
-The tricky part is of course to wrap all those "boxable" types into `MustacheBox`, a single type that wraps the various behaviors of all boxed values. Mustache templates are not a printf-like flat format, but a way to render values burried inside a tree. When the boxed value is a such a tree, we must make sure that all intermediate values, down to the leaves, are rendered correctly, according to their actual type.
-
-Well, the `Box()` function allows us to do that, with some reasonable limitations that we will see. Other options could have been:
-
-- No API at all:
-
- - `template.render(["foo": "bar"])`.
- - `template.render(profile)`.
-
- This is what the Objective-C GRMustache do. Because it can.
-
- With Swift, this is impossible, because is it impossible to define a single type (the argument to `Template.render()`) that does the job. Let's review some candidates for this unicorn type:
-
- - **Any** is not an option: there is no dynamic dispatch behind it, and Swift makes it impossible to perform a runtime check for some boxable types, like collections of boxable types.
-
- - **AnyObject** is not an option: structs and enums are not classes, and we want GRMustache.swift to eat structs and enums. If it would not, the library would need to refactor his code, and this a key requirement of GRMustache.swift that it eats user types without refactoring.
-
- - **NSObject** is not an option: this would again require the user to refactor his existing structs and enum models. And the Objective-C GRMustache already exists. What's the purpose of a Swift Mustache engine that can only render Objective-C objects?
-
- - A **protocol** with a required function is a serious candidate, but Swift won't let specializations of generic types adopt a protocol. In other words, the type system can not make boxable a collection of boxable values. And we need to support collections and dictionaries.
-
- - A **protocol extension** is not an option, because protocol extensions are limited to static dispatch, unable to fulfill our needs.
-
- We have exhausted all general types of Swift, and none of them help us.
-
-- A common method:
-
- - `template.render(["foo": "bar"].asMustacheFood)`.
- - `template.render(profile.asMustacheFood)`.
-
- This will not do because one can not add methods to function types, and templates have to eat some functions, such as `FilterFunction`, `RenderFunction`, etc.
-
- Even if we'd remove those functions from the GRMustache.swift bestiary, and hide them behind some struct that can have such method, we would still face an issue: the method name should never conflict with user-land concepts. To make this happen, it would need to contain the "mustache" word, and would thus be more verbose that `Box()`.
-
-That said, let's have `Box()` box as many types as we can.
-
-We can acheive a reasonably good result through a long series of Swift features and work arounds.
-
-By "reasonably good", we mean that library user does not have to think about it too much: boxing usually work. And when boxing fails, it is because of a few, understandable reasons, that are not too difficult to fix.
-
-1. We start with the MustacheBoxable protocol, and `func Box(boxable: MustacheBoxable?) -> MustacheBox`
-
- Through adoption of MustacheBoxable by Swift standard types Int, UInt,
- etc. and user types, we support:
-
- - `Int`
- - `UInt`
- - `Double`
- - `String`
- - `MustacheBox`
- - User types that adopt MustacheBoxable, with a limitation: Swift won't let non-@objc subclasses override mustacheBox if protocol is adopted by an extension of a non-@objc super class. We get a compiler error "declarations in extensions cannot override yet").
-
-2. `MustacheBox` is an @objc class.
-
- Of course it should have been a struct. Being an immutable class, we don't loose value semantics. Being an @objc class, we avoid the "declarations in extensions cannot override yet" error: subclasses of NSObject can override NSObject.mustacheBox, and this gives us support for:
-
- - `NSObject`
- - `NSNumber`
- - `NSNull`
- - `NSString`
- - `NSSet`
- - `NSDictionary`
- - `NSFormatter`
- - All user subclasses of NSObject
-
- If `MustacheBox` were not an @objc class, NSObject subclasses could not override NSObject.mustacheBox. We'd have to support NSNumber, NSString, etc. through runtime check, and user types would not provide their own custom behavior.
-
-3. `NSObject.mustacheBox` checks for NSFastEnumeration conformance.
-
- This gives us support for:
-
- - `NSArray`
- - `NSOrderedSet`
- - All classes that conform to NSFastEnumeration
-
- This runtime check is necessary because Swift won't let us provide any other way to support NSFastEnumeration.
-
-4. We box Set-like collections with `func Box(set: C?) -> MustacheBox`
-
- We get support for:
-
- - `Set`
- - `Set`
- - `Set` as long as UserType conforms to MustacheBoxable.
-
-5. We box Array-like collections with `func Box(array: C?) -> MustacheBox`
-
- We get support for:
-
- - `Range`
- - `Array`
- - `Array`
- - `Array` as long as UserType conforms to MustacheBoxable.
-
- Swift2 currently won't support collections of optional MustacheBoxable because of a compiler crash. Swift1.2 supports it, though: we can expect support for those collections to come back sooner or later.
-
-6. We box dictionaries with `func Box(dictionary: [String: T]?) -> MustacheBox` and `func Box(dictionary: [String: T?]?) -> MustacheBox`
-
- We get support for:
-
- - `[String:Int]`
- - `[String:String?]`
- - `[String:MustacheBox]`
- - `[String:UserType]` as long as UserType conforms to MustacheBoxable.
-
-7. NSObject adopts MustacheBoxable, so an explicit `func Box(object: NSObject?) -> MustacheBox` is rendundant.
-
- Yet we define it.
-
- This (documented?) trick makes Swift perform automatic conversion to Foundation types when possible. This is very handy because we get support for Foundation-compatible nested collections:
-
- - `[String: [Int]]`
- - `[String: [[String: String]]`
-
-8. Core functions FilterFunction, RenderFunction, WillRenderFunction and DidRenderFunction have their own `Box()` variants.
-
- This gives us support for:
-
- - `Filter { ... }`
- - `Lambda { ... }`
- - `RenderFunction`
- - `WillRenderFunction`
- - `DidRenderFunction`
-
-Phew, now we can box quite a bunch of types. But that's a lot of work, and there is often a long chain of causes that makes a type boxable. Don't look for elegance here: there is none. Just a best effort, considering the language limitations.
-
-Some types are still not boxable. Especially all types involving Any or AnyObject, and nested collections that are not Foundation-compatible:
-
-- `[Any]`
-- `[String:AnyObject]`
-- `[Range]`
-- `[String: FilterFunction]`
-
-For those, there is a single rule: the user has to convert them to a known boxable type. He doesn't feed his templates with random data, does he? So he just has to use the `as` operator or to create a brand new `MustacheBox`, `[MustacheBox]`, or `[String: MustacheBox]`.
diff --git a/Sources/Template.swift b/Sources/Template.swift
index b24dd4d2..6e1c4362 100644
--- a/Sources/Template.swift
+++ b/Sources/Template.swift
@@ -23,43 +23,33 @@
import Foundation
-/**
-Template instances render Mustache templates.
-*/
+/// Template instances render Mustache templates.
final public class Template {
// =========================================================================
// MARK: - Loading templates
- /**
- Parses a template string, and returns a template.
-
- - parameter string: The template string.
- - parameter error: If there is an error loading or parsing template and
- partials, throws an error that describes the problem.
- - returns: A new Template.
- */
+ /// Creates a template from a template string.
+ ///
+ /// - parameter string: The template string.
+ /// - throws: MustacheError
public convenience init(string: String) throws {
let repository = TemplateRepository()
let templateAST = try repository.templateAST(string: string)
self.init(repository: repository, templateAST: templateAST, baseContext: repository.configuration.baseContext)
}
- /**
- Parses a template file, and returns a template.
-
- Eventual partial tags in the template refer to sibling template files using
- the same extension.
-
- // `{{>partial}}` in `/path/to/template.txt` loads `/path/to/partial.txt`:
- let template = try! Template(path: "/path/to/template.txt")
-
- - parameter path: The path to the template file.
- - parameter encoding: The encoding of the template file.
- - parameter error: If there is an error loading or parsing template and
- partials, throws an error that describes the problem.
- - returns: A new Template.
- */
+ /// Creates a template from the contents of a file.
+ ///
+ /// Eventual partial tags in the template refer to sibling template files
+ /// using the same extension.
+ ///
+ /// // `{{>partial}}` in `/path/to/template.txt` loads `/path/to/partial.txt`:
+ /// let template = try! Template(path: "/path/to/template.txt")
+ ///
+ /// - parameter path: The path to the template file.
+ /// - parameter encoding: The encoding of the template file.
+ /// - throws: MustacheError
public convenience init(path: String, encoding: String.Encoding = String.Encoding.utf8) throws {
let nsPath = path as NSString
let directoryPath = nsPath.deletingLastPathComponent
@@ -70,21 +60,17 @@ final public class Template {
self.init(repository: repository, templateAST: templateAST, baseContext: repository.configuration.baseContext)
}
- /**
- Parses a template file, and returns a template.
-
- Eventual partial tags in the template refer to sibling templates using
- the same extension.
-
- // `{{>partial}}` in `file://path/to/template.txt` loads `file://path/to/partial.txt`:
- let template = try! Template(URL: "file://path/to/template.txt")
-
- - parameter URL: The URL of the template.
- - parameter encoding: The encoding of the template resource.
- - parameter error: If there is an error loading or parsing template and
- partials, throws an error that describes the problem.
- - returns: A new Template.
- */
+ /// Creates a template from the contents of a URL.
+ ///
+ /// Eventual partial tags in the template refer to sibling templates using
+ /// the same extension.
+ ///
+ /// // `{{>partial}}` in `file://path/to/template.txt` loads `file://path/to/partial.txt`:
+ /// let template = try! Template(URL: "file://path/to/template.txt")
+ ///
+ /// - parameter URL: The URL of the template.
+ /// - parameter encoding: The encoding of the template resource.
+ /// - throws: MustacheError
public convenience init(URL: Foundation.URL, encoding: String.Encoding = String.Encoding.utf8) throws {
let baseURL = URL.deletingLastPathComponent()
let templateExtension = URL.pathExtension
@@ -94,28 +80,22 @@ final public class Template {
self.init(repository: repository, templateAST: templateAST, baseContext: repository.configuration.baseContext)
}
- /**
- Parses a template resource identified by the specified name and file
- extension, and returns a template.
-
- Eventual partial tags in the template refer to template resources using
- the same extension.
-
- // `{{>partial}}` in `template.mustache` loads resource `partial.mustache`:
- let template = try! Template(named: "template")
-
- - parameter name: The name of a bundle resource.
- - parameter bundle: The bundle where to look for the template
- resource. If nil, the main bundle is used.
- - parameter templateExtension: If extension is an empty string or nil, the
- extension is assumed not to exist and the
- template file should exactly matches name.
- - parameter encoding: The encoding of template resource.
- - parameter error: If there is an error loading or parsing
- template and partials, throws an error that
- describes the problem.
- - returns: A new Template.
- */
+ /// Creates a template from a bundle resource.
+ ///
+ /// Eventual partial tags in the template refer to template resources using
+ /// the same extension.
+ ///
+ /// // `{{>partial}}` in `template.mustache` loads resource `partial.mustache`:
+ /// let template = try! Template(named: "template")
+ ///
+ /// - parameter name: The name of a bundle resource.
+ /// - parameter bundle: The bundle where to look for the template resource.
+ // If nil, the main bundle is used.
+ /// - parameter templateExtension: If extension is an empty string or nil,
+ /// the extension is assumed not to exist and the template file should
+ /// exactly match name.
+ /// - parameter encoding: The encoding of template resource.
+ /// - throws: MustacheError
public convenience init(named name: String, bundle: Bundle? = nil, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) throws {
let repository = TemplateRepository(bundle: bundle, templateExtension: templateExtension, encoding: encoding)
let templateAST = try repository.templateAST(named: name)
@@ -126,49 +106,40 @@ final public class Template {
// =========================================================================
// MARK: - Rendering Templates
- /**
- Renders a template with a context stack initialized with the provided box
- on top of the templates's base context.
-
- - parameter box: A boxed value used for evaluating Mustache tags.
- - parameter error: If there is an error rendering the tag, throws an error
- that describes the problem.
- - returns: The rendered string.
- */
+ /// Renders a template with a context stack initialized with the provided
+ /// value on top of the templates's base context.
+ ///
+ /// - parameter value: A value.
+ /// - throws: MustacheError
+ /// - returns: The rendered string.
public func render(_ value: Any? = nil) throws -> String {
let rendering = try render(baseContext.extendedContext(value))
return rendering.string
}
- /**
- Returns the rendering of the receiver, evaluating mustache tags from values
- stored in the given context stack.
-
- This method does not return a String, but a Rendering value that wraps both
- the rendered string and its content type (HTML or Text). It is intended to
- be used when you perform custom rendering in a `RenderFunction`.
-
- - parameter context: A context stack
- - parameter error: If there is an error rendering the tag, throws an
- error that describes the problem.
- - returns: The template rendering.
-
- See also:
-
- - RenderFunction
- - Template.contentType
- */
+ /// Returns the rendering of the receiver, evaluating mustache tags from
+ /// values stored in the given context stack.
+ ///
+ /// This method does not return a String, but a Rendering value that wraps
+ /// both the rendered string and its content type (HTML or Text). It is
+ /// intended to be used when you perform custom rendering in
+ /// a `RenderFunction`.
+ ///
+ /// - parameter context: A context stack
+ /// - throws: MustacheError
+ /// - returns: The template rendering.
+ ///
+ /// - seealso: RenderFunction
+ /// - seealso: Template.contentType
public func render(_ context: Context) throws -> Rendering {
let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
return try renderingEngine.render()
}
- /**
- The content type of the template and of its renderings.
-
- See `Configuration.contentType` for a full discussion of the content type of
- templates.
- */
+ /// The content type of the template and of its renderings.
+ ///
+ /// See `Configuration.contentType` for a full discussion of the content
+ /// type of templates.
public var contentType: ContentType {
return templateAST.contentType
}
@@ -177,66 +148,52 @@ final public class Template {
// =========================================================================
// MARK: - Configuring Templates
- /**
- The template's base context: all renderings start from this context.
-
- Its default value comes from the configuration of the template
- repository this template comes from.
-
- You can set the base context to some custom context, or extend it with the
- `extendBaseContext` and `registerInBaseContext` methods.
-
- // Renders "bar"
- let template = try! Template(string: "{{foo}}")
- template.baseContext = Context(Box(["foo": "bar"]))
- try! template.render()
-
- See also:
-
- - extendBaseContext
- - registerInBaseContext
- */
+ /// The template's base context: all renderings start from this context.
+ ///
+ /// Its default value comes from the configuration of the template
+ /// repository this template comes from.
+ ///
+ /// You can set the base context to some custom context, or extend it with
+ /// the `extendBaseContext(_)` and `register(_:forKey:)` methods.
+ ///
+ /// // Renders "Arthur"
+ /// let template = try! Template(string: "{{name}}")
+ /// template.baseContext = Context(["name": "Arthur"])
+ /// try! template.render()
+ ///
+ /// - seealso: extendBaseContext(_)
+ /// - seealso: register(_:forKey:)
public var baseContext: Context
- /**
- Extends the base context with the provided boxed value. All renderings will
- start from this extended context.
-
- // Renders "bar"
- let template = try! Template(string: "{{foo}}")
- template.extendBaseContext(Box(["foo": "bar"]))
- try! template.render()
-
- See also:
-
- - baseContext
- - registerInBaseContext
- - Context.extendedContext
- */
+ /// Extends the base context with the provided boxed value. All renderings
+ /// will start from this extended context.
+ ///
+ /// // Renders "Arthur"
+ /// let template = try! Template(string: "{{name}}")
+ /// template.extendBaseContext(["name": "Arthur"])
+ /// try! template.render()
+ ///
+ /// - seealso: baseContext
+ /// - seealso: register(_:forKey:)
+ /// - seealso: Context.extendedContext
public func extendBaseContext(_ value: Any?) {
baseContext = baseContext.extendedContext(value)
}
- /**
- Registers a key in the base context. All renderings will be able to access
- the provided box through this key.
-
- Registered keys are looked up first when evaluating Mustache tags.
-
- // Renders "bar"
- let template = try! Template(string: "{{foo}}")
- template.registerInBaseContext("foo", Box("bar"))
- try! template.render()
-
- // Renders "bar" again, because the registered key "foo" has priority.
- try! template.render(Box(["foo": "qux"]))
-
- See also:
-
- - baseContext
- - extendBaseContext
- - Context.extendedContext(withRegisteredValue:forKey:)
- */
+ /// Registers the value for the given *key* in the base context. Registered
+ /// keys are looked up first when evaluating Mustache tags.
+ ///
+ /// // Renders "Arthur"
+ /// let template = try! Template(string: "{{name}}")
+ /// template.register("Arthur", forKey: "name")
+ /// try! template.render()
+ ///
+ /// // Renders "Arthur" again, because the registered key "name" has priority.
+ /// try! template.render(["name": "Barbara"])
+ ///
+ /// - seealso: baseContext
+ /// - seealso: extendBaseContext(_)
+ /// - seealso: Context.extendedContext(withRegisteredValue:forKey:)
public func register(_ value: Any?, forKey key: String) {
baseContext = baseContext.extendedContext(withRegisteredValue: value, forKey: key)
}
@@ -245,38 +202,34 @@ final public class Template {
// =========================================================================
// MARK: - Accessing Sibling Templates
- /**
- The template repository that issued the receiver.
-
- All templates belong a template repository:
-
- - Templates returned by `init(string:)` have a template
- repository that can not load any template or partial by name.
-
- - Templates returned by `init(path:encoding:)` have a template
- repository that loads templates and partials stored in the directory of
- the receiver, with the same file extension.
-
- - Templates returned by `init(URL:encoding:)` have a template
- repository that loads templates and partials stored in the directory of
- the receiver, with the same file extension.
-
- - Templates returned by `init(named:bundle:templateExtension:encoding:)`
- have a template repository that loads templates and partials stored as
- resources in the specified bundle.
-
- - Templates returned by `TemplateRepository.template(named:)` and
- `TemplateRepository.template(string:)` belong to the invoked
- repository.
-
- See also:
-
- - TemplateRepository
- - init(string:)
- - init(path:)
- - init(URL:)
- - init(named:bundle:templateExtension:encoding:)
- */
+ /// The template repository that issued the template.
+ ///
+ /// All templates belong a template repository:
+ ///
+ /// - Templates returned by `init(string:)` have a template
+ /// repository that can not load any template or partial by name.
+ ///
+ /// - Templates returned by `init(path:encoding:)` have a template
+ /// repository that loads templates and partials stored in the directory
+ /// of the receiver, with the same file extension.
+ ///
+ /// - Templates returned by `init(URL:encoding:)` have a template
+ /// repository that loads templates and partials stored in the directory
+ /// of the receiver, with the same file extension.
+ ///
+ /// - Templates returned by `init(named:bundle:templateExtension:encoding:)`
+ /// have a template repository that loads templates and partials stored as
+ /// resources in the specified bundle.
+ ///
+ /// - Templates returned by `TemplateRepository.template(named:)` and
+ /// `TemplateRepository.template(string:)` belong to the invoked
+ /// repository.
+ ///
+ /// - seealso: TemplateRepository
+ /// - seealso: init(string:)
+ /// - seealso: init(path:)
+ /// - seealso: init(URL:)
+ /// - seealso: init(named:bundle:templateExtension:encoding:)
public let repository: TemplateRepository
@@ -299,45 +252,38 @@ final public class Template {
extension Template : MustacheBoxable {
- /**
- `Template` adopts the `MustacheBoxable` protocol so that it can feed
- Mustache templates.
-
- You should not directly call the `mustacheBox` property. Always use the
- `Box()` function instead:
-
- template.mustacheBox // Valid, but discouraged
- Box(template) // Preferred
-
-
- A template renders just like a partial tag:
-
- - `{{template}}` renders like an embedded partial tag `{{>partial}}` that
- would refer to the same template.
-
- - `{{#template}}...{{/template}}` renders like a partial override tag
- `{{partial}}` is a hard-coded template name, when
- `{{template}}` is a template that you can choose at runtime.
-
-
- For example:
-
- let template = try! Template(string: "{{firstName}} {{lastName}}")
- let data = [
- "firstName": Box("Salvador"),
- "lastName": Box("Dali"),
- "url": Box("/people/123"),
- "template": Box(template)
- ]
-
- // Salvador Dali
- try! Template(string: "{{template}}").render(Box(data))
-
- Note that templates whose contentType is Text are HTML-escaped when they are
- included in an HTML template.
- */
+ /// `Template` adopts the `MustacheBoxable` protocol so that it can feed
+ /// Mustache templates.
+ ///
+ /// You should not directly call the `mustacheBox` property.
+ ///
+ /// A template renders just like a partial tag:
+ ///
+ /// - `{{template}}` renders like an embedded partial tag `{{>partial}}`
+ /// that would refer to the same template.
+ ///
+ /// - `{{#template}}...{{/template}}` renders like a partial override tag
+ /// `{{partial}}` is a hard-coded template name, when
+ /// `{{template}}` is a template that you can choose at runtime.
+ ///
+ ///
+ /// For example:
+ ///
+ /// let template = try! Template(string: "{{firstName}} {{lastName}}")
+ /// let data: [String: Any] = [
+ /// "firstName": "Salvador",
+ /// "lastName": "Dali",
+ /// "url": "/people/123",
+ /// "template": template
+ /// ]
+ ///
+ /// // Salvador Dali
+ /// try! Template(string: "{{template}}").render(data)
+ ///
+ /// Note that templates whose contentType is Text are HTML-escaped when they
+ /// are included in an HTML template.
public var mustacheBox: MustacheBox {
return MustacheBox(
value: self,
From 571258facf6c17968d29a9b78e80498e4198c9b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 12:48:41 +0200
Subject: [PATCH 38/46] Documentation
---
Sources/TemplateRepository.swift | 459 ++++++++++++++-----------------
1 file changed, 213 insertions(+), 246 deletions(-)
diff --git a/Sources/TemplateRepository.swift b/Sources/TemplateRepository.swift
index d464d006..91bc62be 100644
--- a/Sources/TemplateRepository.swift
+++ b/Sources/TemplateRepository.swift
@@ -26,214 +26,195 @@ import Foundation
/// See the documentation of the `TemplateRepositoryDataSource` protocol.
public typealias TemplateID = String
-/**
-The protocol for a TemplateRepository's dataSource.
-
-The dataSource's responsability is to provide Mustache template strings for
-template and partial names.
-*/
+/// The protocol for a TemplateRepository's dataSource.
+///
+/// The dataSource's responsability is to provide Mustache template strings for
+/// template and partial names.
public protocol TemplateRepositoryDataSource {
- /**
- Returns a template ID, that is to say a string that uniquely identifies a
- template or a template partial.
-
- The meaning of this String is opaque: your implementation of a
- `TemplateRepositoryDataSource` would define, for itself, how strings would
- identity a template or a partial. For example, a file-based data source may
- use paths to the templates.
-
- The return value of this method can be nil: the library user will then
- eventually catch a missing template error.
-
- ### Template hierarchies
-
- Whenever relevant, template and partial hierarchies are supported via the
- baseTemplateID parameter: it contains the template ID of the enclosing
- template, or nil when the data source is asked for a template ID from a raw
- template string (see TemplateRepository.template(string:error:)).
-
- Not all data sources have to implement hierarchies: they can simply ignore
- this parameter.
-
- *Well-behaved* data sources that support hierarchies should support
- "absolute paths to partials". For example, the built-in directory-based data
- source lets users write both `{{> relative/path/to/partial}}` and
- `{{> /absolute/path/tp/partial}}`tags.
-
- ### Unique IDs
-
- Each template must be identified by a single template ID. For example, a
- file-based data source which uses path IDs must normalize paths. Not
- following this rule yields undefined behavior.
-
- - parameter name: The name of the template or template partial.
- - parameter baseTemplateID: The template ID of the enclosing template.
- - returns: A template ID.
- */
+ /// Returns a template ID, that is to say a string that uniquely identifies
+ /// a template or a template partial.
+ ///
+ /// The meaning of this String is opaque: your implementation of a
+ /// `TemplateRepositoryDataSource` would define, for itself, how strings
+ /// would identity a template or a partial. For example, a file-based data
+ /// source may use paths to the templates.
+ ///
+ /// The return value of this method can be nil: the library user will then
+ /// eventually catch a missing template error.
+ ///
+ /// ### Template hierarchies
+ ///
+ /// Whenever relevant, template and partial hierarchies are supported via
+ /// the baseTemplateID parameter: it contains the template ID of the
+ /// enclosing template, or nil when the data source is asked for a
+ /// template ID from a raw template string (see
+ /// TemplateRepository.template(string:error:)).
+ ///
+ /// Not all data sources have to implement hierarchies: they can simply
+ /// ignore this parameter.
+ ///
+ /// *Well-behaved* data sources that support hierarchies should support
+ /// "absolute paths to partials". For example, the built-in directory-based
+ /// data source lets users write both `{{> relative/path/to/partial}}` and
+ /// `{{> /absolute/path/tp/partial}}`tags.
+ ///
+ /// ### Unique IDs
+ ///
+ /// Each template must be identified by a single template ID. For example, a
+ /// file-based data source which uses path IDs must normalize paths. Not
+ /// following this rule yields undefined behavior.
+ ///
+ /// - parameter name: The name of the template or template partial.
+ /// - parameter baseTemplateID: The template ID of the enclosing template.
+ /// - returns: A template ID.
func templateIDForName(_ name: String, relativeToTemplateID baseTemplateID: TemplateID?) -> TemplateID?
- /**
- Returns the Mustache template string that matches the template ID.
-
- - parameter templateID: The template ID of the template.
- - parameter error: If there is an error returning a template string,
- throws an error that describes the problem.
- - returns: A Mustache template string.
- */
+ /// Returns the Mustache template string that matches the template ID.
+ ///
+ /// - parameter templateID: The template ID of the template.
+ /// - throws: MustacheError
+ /// - returns: A Mustache template string.
func templateStringForTemplateID(_ templateID: TemplateID) throws -> String
}
-/**
-A template repository represents a set of sibling templates and partials.
-
-You don't have to instanciate template repositories, because GRMustache provides
-implicit ones whenever you load templates with methods like
-`Template(named:error:)`, for example.
-
-However, you may like to use one for your profit. Template repositories provide:
-
-- custom template data source
-- custom `Configuration`
-- a cache of template parsings
-- absolute paths to ease loading of partial templates in a hierarchy of
- directories and template files
-*/
+/// A template repository represents a set of sibling templates and partials.
+///
+/// You don't have to instanciate template repositories, because GRMustache
+/// provides implicit ones whenever you load templates with methods like
+/// `Template(named:error:)`, for example.
+///
+/// However, you may like to use one for your profit. Template repositories
+/// provide:
+///
+/// - custom template data source
+/// - custom `Configuration`
+/// - a cache of template parsings
+/// - absolute paths to ease loading of partial templates in a hierarchy of
+/// directories and template files
final public class TemplateRepository {
// =========================================================================
// MARK: - Creating Template Repositories
- /**
- Returns a TemplateRepository which loads template through the provided
- dataSource.
-
- The dataSource is optional, but repositories without dataSource can not load
- templates by name, and can only parse template strings that do not contain
- any `{{> partial }}` tag.
-
- let repository = TemplateRepository()
- let template = try! repository.template(string: "Hello {{name}}")
- */
+ /// Creates a TemplateRepository which loads template through the provided
+ /// dataSource.
+ ///
+ /// The dataSource is optional, but repositories without dataSource can not
+ /// load templates by name, and can only parse template strings that do not
+ /// contain any `{{> partial }}` tag.
+ ///
+ /// let repository = TemplateRepository()
+ /// let template = try! repository.template(string: "Hello {{name}}")
public init(dataSource: TemplateRepositoryDataSource? = nil) {
configuration = DefaultConfiguration
templateASTCache = [:]
self.dataSource = dataSource
}
- /**
- Returns a TemplateRepository that loads templates from a dictionary.
-
- let templates = ["template": "Hulk Hogan has a Mustache."]
- let repository = TemplateRepository(templates: templates)
-
- // Renders "Hulk Hogan has a Mustache." twice
- try! repository.template(named: "template").render()
- try! repository.template(string: "{{>template}}").render()
-
- - parameter templates: A dictionary whose keys are template names and values
- template strings.
- - returns: A new TemplateRepository.
- */
+ /// Creates a TemplateRepository that loads templates from a dictionary.
+ ///
+ /// let templates = ["template": "Hulk Hogan has a Mustache."]
+ /// let repository = TemplateRepository(templates: templates)
+ ///
+ /// // Renders "Hulk Hogan has a Mustache." twice
+ /// try! repository.template(named: "template").render()
+ /// try! repository.template(string: "{{>template}}").render()
+ ///
+ /// - parameter templates: A dictionary whose keys are template names and
+ /// values template strings.
convenience public init(templates: [String: String]) {
self.init(dataSource: DictionaryDataSource(templates: templates))
}
- /**
- Returns a TemplateRepository that loads templates from a directory.
-
- let repository = TemplateRepository(directoryPath: "/path/to/templates")
-
- // Loads /path/to/templates/template.mustache
- let template = try! repository.template(named: "template")
-
-
- Eventual partial tags in template files refer to sibling template files.
-
- If the target directory contains a hierarchy of template files and
- sub-directories, you can navigate through this hierarchy with both relative
- and absolute paths to partials. For example, given the following hierarchy:
-
- - /path/to/templates
- - a.mustache
- - partials
- - b.mustache
-
- The a.mustache template can embed b.mustache with both `{{> partials/b }}`
- and `{{> /partials/b }}` partial tags.
-
- The b.mustache template can embed a.mustache with both `{{> ../a }}` and
- `{{> /a }}` partial tags.
-
-
- - parameter directoryPath: The path to the directory containing template
- files.
- - parameter templateExtension: The extension of template files. Default
- extension is "mustache".
- - parameter encoding: The encoding of template files. Default
- encoding is NSUTF8StringEncoding.
- - returns: A new TemplateRepository.
- */
+ /// Creates a TemplateRepository that loads templates from a directory.
+ ///
+ /// let repository = TemplateRepository(directoryPath: "/path/to/templates")
+ ///
+ /// // Loads /path/to/templates/template.mustache
+ /// let template = try! repository.template(named: "template")
+ ///
+ ///
+ /// Eventual partial tags in template files refer to sibling template files.
+ ///
+ /// If the target directory contains a hierarchy of template files and
+ /// sub-directories, you can navigate through this hierarchy with both
+ /// relative and absolute paths to partials. For example, given the
+ /// following hierarchy:
+ ///
+ /// - /path/to/templates
+ /// - a.mustache
+ /// - partials
+ /// - b.mustache
+ ///
+ /// The a.mustache template can embed b.mustache with both
+ /// `{{> partials/b }}` and `{{> /partials/b }}` partial tags.
+ ///
+ /// The b.mustache template can embed a.mustache with both `{{> ../a }}` and
+ /// `{{> /a }}` partial tags.
+ ///
+ ///
+ /// - parameter directoryPath: The path to the directory containing
+ /// template files.
+ /// - parameter templateExtension: The extension of template files. Default
+ /// extension is "mustache".
+ /// - parameter encoding: The encoding of template files. Default encoding
+ /// is UTF-8.
convenience public init(directoryPath: String, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) {
self.init(dataSource: URLDataSource(baseURL: URL(fileURLWithPath: directoryPath, isDirectory: true), templateExtension: templateExtension, encoding: encoding))
}
- /**
- Returns a TemplateRepository that loads templates from a URL.
-
- let templatesURL = NSURL.fileURLWithPath("/path/to/templates")!
- let repository = TemplateRepository(baseURL: templatesURL)
-
- // Loads /path/to/templates/template.mustache
- let template = try! repository.template(named: "template")
-
-
- Eventual partial tags in template files refer to sibling template files.
-
- If the target directory contains a hierarchy of template files and
- sub-directories, you can navigate through this hierarchy with both relative
- and absolute paths to partials. For example, given the following hierarchy:
-
- - /path/to/templates
- - a.mustache
- - partials
- - b.mustache
-
- The a.mustache template can embed b.mustache with both `{{> partials/b }}`
- and `{{> /partials/b }}` partial tags.
-
- The b.mustache template can embed a.mustache with both `{{> ../a }}` and
- `{{> /a }}` partial tags.
-
-
- - parameter baseURL: The base URL where to look for templates.
- - parameter templateExtension: The extension of template resources. Default
- extension is "mustache".
- - parameter encoding: The encoding of template resources. Default
- encoding is NSUTF8StringEncoding.
- - returns: A new TemplateRepository.
- */
+ /// Creates a TemplateRepository that loads templates from a URL.
+ ///
+ /// let templatesURL = NSURL.fileURLWithPath("/path/to/templates")!
+ /// let repository = TemplateRepository(baseURL: templatesURL)
+ ///
+ /// // Loads /path/to/templates/template.mustache
+ /// let template = try! repository.template(named: "template")
+ ///
+ ///
+ /// Eventual partial tags in template files refer to sibling template files.
+ ///
+ /// If the target directory contains a hierarchy of template files and
+ /// sub-directories, you can navigate through this hierarchy with both
+ /// relative and absolute paths to partials. For example, given the
+ /// following hierarchy:
+ ///
+ /// - /path/to/templates
+ /// - a.mustache
+ /// - partials
+ /// - b.mustache
+ ///
+ /// The a.mustache template can embed b.mustache with both `{{> partials/b }}`
+ /// and `{{> /partials/b }}` partial tags.
+ ///
+ /// The b.mustache template can embed a.mustache with both `{{> ../a }}` and
+ /// `{{> /a }}` partial tags.
+ ///
+ ///
+ /// - parameter baseURL: The base URL where to look for templates.
+ /// - parameter templateExtension: The extension of template resources.
+ /// Default extension is "mustache".
+ /// - parameter encoding: The encoding of template resources. Default
+ /// encoding is UTF-8.
convenience public init(baseURL: URL, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) {
self.init(dataSource: URLDataSource(baseURL: baseURL, templateExtension: templateExtension, encoding: encoding))
}
- /**
- Returns a TemplateRepository that loads templates stored as resources in a
- bundle.
-
- let repository = TemplateRepository(bundle: nil)
-
- // Loads the template.mustache resource of the main bundle:
- let template = try! repository.template(named: "template")
-
- - parameter bundle: The bundle that stores templates resources.
- Nil stands for the main bundle.
- - parameter templateExtension: The extension of template resources. Default
- extension is "mustache".
- - parameter encoding: The encoding of template resources. Default
- encoding is NSUTF8StringEncoding.
- - returns: A new TemplateRepository.
- */
+ /// Creates a TemplateRepository that loads templates stored as resources in
+ /// a bundle.
+ ///
+ /// let repository = TemplateRepository(bundle: nil)
+ ///
+ /// // Loads the template.mustache resource of the main bundle:
+ /// let template = try! repository.template(named: "template")
+ ///
+ /// - parameter bundle: The bundle that stores templates resources. Nil
+ /// stands for the main bundle.
+ /// - parameter templateExtension: The extension of template resources.
+ /// Default extension is "mustache".
+ /// - parameter encoding: The encoding of template resources. Default
+ /// encoding is UTF-8.
convenience public init(bundle: Bundle?, templateExtension: String? = "mustache", encoding: String.Encoding = String.Encoding.utf8) {
self.init(dataSource: BundleDataSource(bundle: bundle ?? Bundle.main, templateExtension: templateExtension, encoding: encoding))
}
@@ -242,26 +223,25 @@ final public class TemplateRepository {
// =========================================================================
// MARK: - Configuring Template Repositories
- /**
- The configuration for all templates and partials built by the repository.
-
- It is initialized with `Mustache.DefaultConfiguration`.
-
- You can alter the repository's configuration, or set it to another value,
- before you load templates:
-
- // Reset the configuration to a factory configuration and change tag delimiters:
- let repository = TemplateRepository()
- repository.configuration = Configuration()
- repository.configuration.tagDelimiterPair = ("<%", "%>")
-
- // Renders "Hello Luigi"
- let template = try! repository.template(string: "Hello <%name%>")
- try! template.render(Box(["name": "Luigi"]))
-
- **Warning**: changing the configuration has no effect after the repository
- has loaded one template.
- */
+ /// The configuration for all templates and partials built by
+ /// the repository.
+ ///
+ /// It is initialized with `Mustache.DefaultConfiguration`.
+ ///
+ /// You can alter the repository's configuration, or set it to another
+ /// value, before you load templates:
+ ///
+ /// // Reset the configuration to a factory configuration and change tag delimiters:
+ /// let repository = TemplateRepository()
+ /// repository.configuration = Configuration()
+ /// repository.configuration.tagDelimiterPair = ("<%", "%>")
+ ///
+ /// // Renders "Hello Luigi"
+ /// let template = try! repository.template(string: "Hello <%name%>")
+ /// try! template.render(["name": "Luigi"])
+ ///
+ /// Changing the configuration has no effect after the repository has loaded
+ /// one template.
public var configuration: Configuration
@@ -269,66 +249,53 @@ final public class TemplateRepository {
// MARK: - Loading Templates from a Repository
- /**
- The template repository data source, responsible for loading template
- strings.
- */
+ /// The template repository data source, responsible for loading template
+ /// strings.
public let dataSource: TemplateRepositoryDataSource?
- /**
- Returns a template.
-
- Depending on the way the repository has been created, partial tags such as
- `{{>partial}}` load partial templates from URLs, file paths, keys in a
- dictionary, or whatever is relevant to the repository's data source.
-
- - parameter templateString: A Mustache template string.
- - parameter error: If there is an error loading or parsing template
- and partials, throws an error that describes the
- problem.
- - returns: A Mustache Template.
- */
+ /// Creates a template from a template string.
+ ///
+ /// Depending on the way the repository has been created, partial tags such
+ /// as `{{>partial}}` load partial templates from URLs, file paths, keys in
+ /// a dictionary, or whatever is relevant to the repository's data source.
+ ///
+ /// - parameter templateString: A Mustache template string.
+ /// - throws: MustacheError
+ /// - returns: A Mustache Template.
public func template(string: String) throws -> Template {
let templateAST = try self.templateAST(string: string)
return Template(repository: self, templateAST: templateAST, baseContext: lockedConfiguration.baseContext)
}
- /**
- Returns a template identified by its name.
-
- Depending on the repository's data source, the name identifies a bundle
- resource, a URL, a file path, a key in a dictionary, etc.
-
- Template repositories cache the parsing of their templates. However this
- method always return new Template instances, which you can further configure
- independently.
-
- - parameter name: The template name.
- - parameter error: If there is an error loading or parsing template and
- partials, throws an error that describes the problem.
- - returns: A Mustache Template.
-
- See also:
-
- - reloadTemplates
- */
+ /// Creates a template, identified by its name.
+ ///
+ /// Depending on the repository's data source, the name identifies a bundle
+ /// resource, a URL, a file path, a key in a dictionary, etc.
+ ///
+ /// Template repositories cache the parsing of their templates. However this
+ /// method always return new Template instances, which you can further
+ /// configure independently.
+ ///
+ /// - parameter name: The template name.
+ /// - throws: MustacheError
+ /// - returns: A Mustache Template.
+ ///
+ /// - seealso: reloadTemplates()
public func template(named name: String) throws -> Template {
let templateAST = try self.templateAST(named: name, relativeToTemplateID: nil)
return Template(repository: self, templateAST: templateAST, baseContext: lockedConfiguration.baseContext)
}
- /**
- Clears the cache of parsed template strings.
-
- // May reuse a cached parsing:
- let template = try! repository.template(named:"profile")
-
- // Forces the reloading of the template:
- repository.reloadTemplates();
- let template = try! repository.template(named:"profile")
-
- **Warning**: previously created Template instances are not reloaded.
- */
+ /// Clears the cache of parsed template strings.
+ ///
+ /// // May reuse a cached parsing:
+ /// let template = try! repository.template(named:"profile")
+ ///
+ /// // Forces the reloading of the template:
+ /// repository.reloadTemplates();
+ /// let template = try! repository.template(named:"profile")
+ ///
+ /// Note that previously created Template instances are not reloaded.
public func reloadTemplates() {
templateASTCache.removeAll()
}
From 882fb63df6ff6af311db516f0e952d63f0b4c785 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 13:19:23 +0200
Subject: [PATCH 39/46] Documentation
---
CHANGELOG.md | 46 ++++++++++++++++++++++++++++++++++++---
Sources/Fixit-1.1.0.swift | 3 +++
2 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4b313b2..d64412ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,9 +5,49 @@ Release Notes
**New**
-- Swift 3
-- Templates learned to render Int64 and UInt64
-- TODO: describe modified relationships with Foundation
+- **Swift 3**
+- Templates learned to render Int64, UInt64, Float, and CGFloat.
+- The Box() function is no longer necessary when feeding templates:
+
+ ```swift
+ // Still supported
+ let rendering try template.render(Box(["name": "Arthur"]))
+
+ // New:
+ let rendering try template.render(["name": "Arthur"])
+ ```
+
+**Breaking Changes**
+
+- The only collections that can feed Mustache templates are arrays, sets, dictionaries, and Foundation collections that adopt NSFastEnumeration such as NSArray, SSet, NSOrderedSet, NSDictionary, etc.
+
+- The Swift 3 *Grand Renaming* has impacted a few GRMustache APIs:
+
+ ```diff
+ struct Configuration {
+ - func registerInBaseContext(_ key: String, _ box: MustacheBox)
+ + func register(_ value: Any?, forKey key: String)
+ }
+
+ class Template {
+ - func registerInBaseContext(_ key: String, _ box: MustacheBox)
+ + func register(_ value: Any?, forKey key: String)
+ }
+
+ class Context {
+ - func contextWithRegisteredKey(_ key: String, box: MustacheBox) -> Context
+ - func mustacheBoxForKey(_ key: String) -> MustacheBox
+ - func mustacheBoxForExpression(_ string: String) throws -> MustacheBox
+ + func extendedContext(withRegisteredValue value: Any?, forKey key: String) -> Context
+ + func mustacheBox(forKey key: String) -> MustacheBox
+ + func mustacheBox(forExpression string: String) throws -> MustacheBox
+ }
+
+ class MustacheBox {
+ - func mustacheBoxForKey(_ key: String) -> MustacheBox
+ + func mustacheBox(forKey key: String) -> MustacheBox
+ }
+ ```
## v1.1.0
diff --git a/Sources/Fixit-1.1.0.swift b/Sources/Fixit-1.1.0.swift
index 3a9e8433..f538ed68 100644
--- a/Sources/Fixit-1.1.0.swift
+++ b/Sources/Fixit-1.1.0.swift
@@ -32,6 +32,9 @@ extension Context {
@available(*, unavailable, renamed:"mustacheBox(forExpression:)")
public func mustacheBoxForExpression(_ string: String) throws -> MustacheBox { return EmptyBox }
+
+ @available(*, unavailable, renamed:"extendedContext(withRegisteredValue:forKey:)")
+ func contextWithRegisteredKey(_ key: String, box: MustacheBox) -> Context { return self }
}
extension MustacheBox {
From 147083a638c1feee13e829ab1e390c1cd5cee52b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 13:46:02 +0200
Subject: [PATCH 40/46] Add fix-it for Box()
---
Sources/Fixit-1.1.0.swift | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Sources/Fixit-1.1.0.swift b/Sources/Fixit-1.1.0.swift
index f538ed68..4e33a9c5 100644
--- a/Sources/Fixit-1.1.0.swift
+++ b/Sources/Fixit-1.1.0.swift
@@ -21,6 +21,9 @@
// THE SOFTWARE.
+@available(*, unavailable, message:"Use nil instead.")
+public func Box() -> MustacheBox { return EmptyBox }
+
extension Template {
@available(*, unavailable, renamed:"register(_:forKey:)")
public func registerInBaseContext(_ key: String, _ value: Any?) { }
From c62ffa114f2b6d83152ecabf8543c9d633b98732 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 13:46:36 +0200
Subject: [PATCH 41/46] MustacheDemoOSX uses Swift 3
---
.../MustacheDemoOSX.xcodeproj/project.pbxproj | 4 ++--
.../MustacheDemoOSX/ViewController.swift | 21 ++++++++++---------
2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX.xcodeproj/project.pbxproj b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX.xcodeproj/project.pbxproj
index 1fa318ee..157772c1 100644
--- a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX.xcodeproj/project.pbxproj
+++ b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX.xcodeproj/project.pbxproj
@@ -393,7 +393,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 2.3;
+ SWIFT_VERSION = 3.0;
};
name = Debug;
};
@@ -433,7 +433,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
- SWIFT_VERSION = 2.3;
+ SWIFT_VERSION = 3.0;
};
name = Release;
};
diff --git a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
index 0e4e2f49..4898f9bc 100644
--- a/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
+++ b/Docs/DemoApps/MustacheDemoOSX/MustacheDemoOSX/ViewController.swift
@@ -19,13 +19,14 @@ class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
- for textView in [templateTextView, JSONTextView] {
- textView.automaticQuoteSubstitutionEnabled = false;
- textView.textStorage?.font = font
- }
+ templateTextView.isAutomaticQuoteSubstitutionEnabled = false
+ templateTextView.textStorage?.font = font
+
+ JSONTextView.isAutomaticQuoteSubstitutionEnabled = false
+ JSONTextView.textStorage?.font = font
}
- @IBAction func render(sender: AnyObject) {
+ @IBAction func render(_ sender: Any?) {
do {
let template = try Template(string: model.templateString)
@@ -42,17 +43,17 @@ class ViewController: NSViewController {
template.register(StandardLibrary.URLEscape, forKey: "URLEscape")
template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape")
- let data = model.JSONString.dataUsingEncoding(NSUTF8StringEncoding)!
- let JSONObject: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
+ let data = model.JSONString.data(using: .utf8)!
+ let JSONObject = try JSONSerialization.jsonObject(with: data, options: [])
let string = try template.render(Box(JSONObject as? NSObject))
- presentRenderingString(string)
+ present(renderingString: string)
}
catch let error as NSError {
- presentRenderingString("\(error.domain): \(error.localizedDescription)")
+ present(renderingString: "\(error.domain): \(error.localizedDescription)")
}
}
- func presentRenderingString(string: String) {
+ func present(renderingString string: String) {
self.renderingTextView.string = string
self.renderingTextView.textStorage?.font = font
}
From 1f828fefdd98f57892d9cf78f2fc23890f1074d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 13:46:47 +0200
Subject: [PATCH 42/46] Documentation
---
CHANGELOG.md | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d64412ec..2a11b8b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,11 +19,23 @@ Release Notes
**Breaking Changes**
-- The only collections that can feed Mustache templates are arrays, sets, dictionaries, and Foundation collections that adopt NSFastEnumeration such as NSArray, SSet, NSOrderedSet, NSDictionary, etc.
+- The only collections that can feed Mustache templates are arrays, sets, dictionaries, and Foundation collections that adopt NSFastEnumeration such as NSArray, SSet, NSOrderedSet, NSDictionary, etc. Other Swift collections such as ranges can no longer feed templates.
-- The Swift 3 *Grand Renaming* has impacted a few GRMustache APIs:
+- The following APIs were modified:
```diff
+ // Use nil instead
+ -func Box() -> MustacheBox
+
+ -typealias KeyedSubscriptFunction = (key: String) -> MustacheBox
+ +typealias KeyedSubscriptFunction = (_ key: String) -> Any?
+
+ -typealias FilterFunction = (box: MustacheBox, partialApplication: Bool) throws -> MustacheBox
+ +typealias FilterFunction = (_ box: MustacheBox, _ partialApplication: Bool) throws -> Any?
+
+ -typealias WillRenderFunction = (tag: Tag, box: MustacheBox) -> MustacheBox
+ +typealias WillRenderFunction = (_ tag: Tag, _ box: MustacheBox) -> Any?
+
struct Configuration {
- func registerInBaseContext(_ key: String, _ box: MustacheBox)
+ func register(_ value: Any?, forKey key: String)
From f4f5b9905864a804a5eec774a81d45410d31a539 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 13:48:31 +0200
Subject: [PATCH 43/46] MustacheDemoiOS uses Swift 3
---
.../MustacheDemoiOS/MustacheDemoiOS.xcodeproj/project.pbxproj | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Docs/DemoApps/MustacheDemoiOS/MustacheDemoiOS.xcodeproj/project.pbxproj b/Docs/DemoApps/MustacheDemoiOS/MustacheDemoiOS.xcodeproj/project.pbxproj
index a3d10c65..10eb88e3 100644
--- a/Docs/DemoApps/MustacheDemoiOS/MustacheDemoiOS.xcodeproj/project.pbxproj
+++ b/Docs/DemoApps/MustacheDemoiOS/MustacheDemoiOS.xcodeproj/project.pbxproj
@@ -396,7 +396,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 2.3;
+ SWIFT_VERSION = 3.0;
};
name = Debug;
};
@@ -435,7 +435,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
- SWIFT_VERSION = 2.3;
+ SWIFT_VERSION = 3.0;
VALIDATE_PRODUCT = YES;
};
name = Release;
From 202379deac3f31406cfbe693aee3d1b6773bf9dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 13:53:30 +0200
Subject: [PATCH 44/46] Documentation
---
Docs/IMPLEMENTATION_NOTES.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Docs/IMPLEMENTATION_NOTES.md b/Docs/IMPLEMENTATION_NOTES.md
index 40d56bf7..f2acc499 100644
--- a/Docs/IMPLEMENTATION_NOTES.md
+++ b/Docs/IMPLEMENTATION_NOTES.md
@@ -9,8 +9,7 @@ There are a lot of types in GRMustache.swift. Each one has its own tiny role. Le
// Render "Hello Arthur"
let template = Template(string: "Hello {{name}}")!
let data = ["name": "Arthur"]
-let box = Box(data)
-let rendering = template.render(box)!
+let rendering = template.render(data)!
```
@@ -54,7 +53,7 @@ Templates do not eat raw values. They eat boxed values.
## MustacheBox
-The [Box() functions](Mustache/Rendering/Box.swift) turn a value whose type is known at compile time into a [MustacheBox](Mustache/Rendering/MustacheBox.swift) that encapsulates a set of dynamic behaviors against the Mustache engine. Depending on how a box is used, one facet or another will be activated by the engine:
+The [Box() function](Mustache/Rendering/Box.swift) turn a value whose type is known at compile time into a [MustacheBox](Mustache/Rendering/MustacheBox.swift) that encapsulates a set of dynamic behaviors against the Mustache engine. Depending on how a box is used, one facet or another will be activated by the engine:
- `{{ name }}`: the *key extraction* facet of the current box is used, so that the key "name" gets turned into the value that gets eventually rendered.
From 945855b7ccdb9456ded89afb253802d81ff375da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 14:13:10 +0200
Subject: [PATCH 45/46] Fix boxing of NSObject on platforms without Objc
runtime
---
Sources/Foundation.swift | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/Sources/Foundation.swift b/Sources/Foundation.swift
index 6d270085..37e294fc 100644
--- a/Sources/Foundation.swift
+++ b/Sources/Foundation.swift
@@ -111,19 +111,19 @@ extension NSObject : MustacheBoxable {
// Generic NSObject
#if OBJC
- return MustacheBox(
- value: self,
- keyedSubscript: { (key: String) in
- if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
- // Use valueForKey: for safe keys
- return self.value(forKey: key)
- } else {
- // Missing key
- return nil
- }
+ return MustacheBox(
+ value: self,
+ keyedSubscript: { (key: String) in
+ if GRMustacheKeyAccess.isSafeMustacheKey(key, for: self) {
+ // Use valueForKey: for safe keys
+ return self.value(forKey: key)
+ } else {
+ // Missing key
+ return nil
+ }
})
#else
- return self
+ return MustacheBox(value: self)
#endif
}
}
From 9f141416723a59af15836e93c28f2e74cc5fa0a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gwendal=20Roue=CC=81?=
Date: Sun, 23 Oct 2016 14:13:53 +0200
Subject: [PATCH 46/46] 2.0.0
---
CHANGELOG.md | 2 +-
GRMustache.swift.podspec | 2 +-
README.md | 29 +++++++++++++++--------------
Xcode/Info.plist | 2 +-
4 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a11b8b5..d5998deb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
Release Notes
=============
-## Next Version
+## v2.0.0
**New**
diff --git a/GRMustache.swift.podspec b/GRMustache.swift.podspec
index 715a6ad6..80f6e53c 100644
--- a/GRMustache.swift.podspec
+++ b/GRMustache.swift.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'GRMustache.swift'
- s.version = '1.1.0'
+ s.version = '2.0.0'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'Flexible Mustache templates for Swift.'
s.homepage = 'https://github.com/groue/GRMustache.swift'
diff --git a/README.md b/README.md
index df401fe1..41db2b28 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,12 @@ GRMustache.swift [![Swift](https://img.shields.io/badge/swift-3-orange.svg?style
### Mustache templates for Swift
-**Latest release**: September 19, 2016 • version 1.1.0 • [CHANGELOG](CHANGELOG.md)
+**Latest release**: October 23, 2016 • version 2.0.0 • [CHANGELOG](CHANGELOG.md)
-**Requirements**: iOS 8.0+ / OSX 10.9+ / tvOS 9.0+ • Xcode 8+ • Swift 2.3
+**Requirements**: iOS 8.0+ / OSX 10.9+ / tvOS 9.0+ • Xcode 8+ • Swift 3
- Swift 2.2: use the [version 1.0.1](https://github.com/groue/GRMustache.swift/tree/1.0.1)
+- Swift 2.3: use the [version 1.1.0](https://github.com/groue/GRMustache.swift/tree/1.1.0)
Follow [@groue](http://twitter.com/groue) on Twitter for release announcements and usage tips.
@@ -91,7 +92,7 @@ To use GRMustache.swift with CocoaPods, specify in your Podfile:
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
-pod 'GRMustache.swift', '~> 1.1.0'
+pod 'GRMustache.swift'
```
@@ -102,7 +103,7 @@ pod 'GRMustache.swift', '~> 1.1.0'
To use GRMustache.swift with Carthage, specify in your Cartfile:
```
-github "groue/GRMustache.swift" ~> 1.1.0
+github "groue/GRMustache.swift"
```
@@ -135,7 +136,7 @@ Check [groue/GRMustacheSPM](https://github.com/groue/GRMustacheSPM) for a sample
```sh
cd [GRMustache.swift directory]
- git checkout 1.1.0
+ git checkout 2.0.0
````
3. Embed the `Mustache.xcodeproj` project in your own project.
@@ -155,7 +156,7 @@ To fiddle with the library, open the `Xcode/Mustache.xcworkspace` workspace: it
External links:
- [The Mustache Language](http://mustache.github.io/mustache.5.html): the Mustache language itself. You should start here.
-- [GRMustache.swift Reference](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html) on cocoadocs.org
+- [GRMustache.swift Reference](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Classes/Template.html) on cocoadocs.org
Rendering templates:
@@ -222,8 +223,8 @@ Templates may come from various sources:
For more information, check:
-- [Template.swift](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html)
-- [TemplateRepository.swift](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/TemplateRepository.html)
+- [Template.swift](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Classes/Template.html)
+- [TemplateRepository.swift](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Classes/TemplateRepository.html)
Errors
@@ -539,7 +540,7 @@ Generally speaking, partial names are always interpreted by a **Template Reposit
- `Template(string:...)` uses a template repository that can’t load any partial.
- `templateRepository.template(named:...)` uses the partial loading mechanism of the template repository.
-Check [TemplateRepository.swift](Sources/TemplateRepository.swift) for more information ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/TemplateRepository.html)).
+Check [TemplateRepository.swift](Sources/TemplateRepository.swift) for more information ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Classes/TemplateRepository.html)).
#### Dynamic partials
@@ -672,7 +673,7 @@ ERB-styled tags: <% name %>
Default tags again: {{ name }}
```
-There are also APIs for setting those delimiters. Check `Configuration.tagDelimiterPair` in [Configuration.swift](Sources/Configuration.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Structs/Configuration.html)).
+There are also APIs for setting those delimiters. Check `Configuration.tagDelimiterPair` in [Configuration.swift](Sources/Configuration.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Structs/Configuration.html)).
### Comment Tags
@@ -693,7 +694,7 @@ GRMustache.swift interprets two pragma tags that set the content type of the tem
In a **text template**, there is no HTML-escaping. Both `{{name}}` and `{{{name}}}` have the same rendering. Text templates are globally HTML-escaped when included in HTML templates.
-For a more complete discussion, see the documentation of `Configuration.contentType` in [Configuration.swift](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Structs/Configuration.html).
+For a more complete discussion, see the documentation of `Configuration.contentType` in [Configuration.swift](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Structs/Configuration.html).
The Context Stack and Expressions
@@ -763,7 +764,7 @@ But you will generally register filters with the `register(:forKey:)` method, be
template.register(StandardLibrary.each, forKey: "each")
```
-See [Template](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Classes/Template.html) for more information on the base context.
+See [Template](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Classes/Template.html) for more information on the base context.
### Expressions
@@ -1037,7 +1038,7 @@ let data: [String: Any] = [
let rendering = try template.render(data)
```
-Lambdas are a special case of custom rendering functions. The raw `RenderFunction` type gives you extra flexibility when you need to perform custom rendering. See [CoreFunctions.swift](Sources/CoreFunctions.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Typealiases.html)).
+Lambdas are a special case of custom rendering functions. The raw `RenderFunction` type gives you extra flexibility when you need to perform custom rendering. See [CoreFunctions.swift](Sources/CoreFunctions.swift) ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Typealiases.html)).
Filters
@@ -1265,7 +1266,7 @@ let data = ["cats": ["Kitty", "Pussy", "Melba"]]
let rendering = try template.render(data)
```
-As those filters perform custom rendering, they are based on `RenderFunction`, just like [lambdas](#lambdas). Check the `RenderFunction` type in [CoreFunctions.swift](Sources/CoreFunctions.swift) for more information about the `RenderingInfo` and `Rendering` types ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/1.1.0/Typealiases.html)).
+As those filters perform custom rendering, they are based on `RenderFunction`, just like [lambdas](#lambdas). Check the `RenderFunction` type in [CoreFunctions.swift](Sources/CoreFunctions.swift) for more information about the `RenderingInfo` and `Rendering` types ([read on cocoadocs.org](http://cocoadocs.org/docsets/GRMustache.swift/2.0.0/Typealiases.html)).
### Advanced Filters
diff --git a/Xcode/Info.plist b/Xcode/Info.plist
index 09bc9720..7e7479f0 100644
--- a/Xcode/Info.plist
+++ b/Xcode/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 1.1.0
+ 2.0.0
CFBundleSignature
????
CFBundleVersion