0001 // The MIT License 0002 // 0003 // Copyright (c) 2015 Gwendal Roué 0004 // 0005 // Permission is hereby granted, free of charge, to any person obtaining a copy 0006 // of this software and associated documentation files (the "Software"), to deal 0007 // in the Software without restriction, including without limitation the rights 0008 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 0009 // copies of the Software, and to permit persons to whom the Software is 0010 // furnished to do so, subject to the following conditions: 0011 // 0012 // The above copyright notice and this permission notice shall be included in 0013 // all copies or substantial portions of the Software. 0014 // 0015 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0016 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0017 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0018 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0019 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 0020 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 0021 // THE SOFTWARE. 0022 0023 /** 0024 Mustache templates don't eat raw values: they eat values boxed in `MustacheBox`. 0025 0026 To box something in a `MustacheBox`, you use one variant of the `Box()` 0027 function. It comes in several variants so that nearly anything can be boxed and 0028 feed templates: 0029 0030 - Basic Swift values: 0031 0032 template.render(Box("foo")) 0033 0034 - Dictionaries & collections: 0035 0036 template.render(Box(["numbers": [1,2,3]])) 0037 0038 - Custom types via the `MustacheBoxable` protocol: 0039 0040 extension User: MustacheBoxable { ... } 0041 template.render(Box(user)) 0042 0043 - Functions such as `FilterFunction`, `RenderFunction`, `WillRenderFunction` and 0044 `DidRenderFunction`: 0045 0046 let square = Filter { (x: Int?) in Box(x! * x!) } 0047 template.registerInBaseContext("square", Box(square)) 0048 0049 **Warning**: the fact that `MustacheBox` is a subclass of NSObject is an 0050 implementation detail that is enforced by the Swift 2 language itself. This may 0051 change in the future: do not rely on it. 0052 */ 0053 final public class MustacheBox{ 0054 0055 // IMPLEMENTATION NOTE 0056 // 0057 // Why is MustacheBox a subclass of NSObject, and not, say, a Swift struct? 0058 // 0059 // Swift does not allow a class extension to override a method that is 0060 // inherited from an extension to its superclass and incompatible with 0061 // Objective-C. 0062 // 0063 // If MustacheBox were a pure Swift type, this Swift limit would prevent 0064 // NSObject subclasses such as NSNull, NSNumber, etc. to override 0065 // MustacheBoxable.mustacheBox, and provide custom rendering behavior. 0066 // 0067 // For an example of this limitation, see example below: 0068 // 0069 // import Foundation 0070 // 0071 // // A type that is not compatible with Objective-C 0072 // struct MustacheBox { } 0073 // 0074 // // So far so good 0075 // extension NSObject { 0076 // var mustacheBox: MustacheBox { return MustacheBox() } 0077 // } 0078 // 0079 // // Error: declarations in extensions cannot override yet 0080 // extension NSNull { 0081 // override var mustacheBox: MustacheBox { return MustacheBox() } 0082 // } 0083 // 0084 // This problem does not apply to Objc-C compatible protocols: 0085 // 0086 // import Foundation 0087 // 0088 // // So far so good 0089 // extension NSObject { 0090 // var prop: String { return "NSObject" } 0091 // } 0092 // 0093 // // No error 0094 // extension NSNull { 0095 // override var prop: String { return "NSNull" } 0096 // } 0097 // 0098 // NSObject().prop // "NSObject" 0099 // NSNull().prop // "NSNull" 0100 // 0101 // In order to let the user easily override NSObject.mustacheBox, we had to 0102 // keep its return type compatible with Objective-C, that is to say make 0103 // MustacheBox a subclass of NSObject. 0104 0105 0106 // ------------------------------------------------------------------------- 0107 // MARK: - The boxed value 0108 0109 /// The boxed value. 0110 public let value
Configuration.swift:240 public mutating func extendBaseContext(box: MustacheBox) {Configuration.swift:287 public mutating func registerInBaseContext(key: String, _ box: MustacheBox) {EachFilter.swift:23 let EachFilter = Filter { (box: MustacheBox) -> MustacheBox inEachFilter.swift:23 let EachFilter = Filter { (box: MustacheBox) -> MustacheBox inEachFilter.swift:43 let transformedBoxes = dictionary.enumerate().map { (index: Int, element: (key: String, box: MustacheBox)) -> MustacheBox inEachFilter.swift:43 let transformedBoxes = dictionary.enumerate().map { (index: Int, element: (key: String, box: MustacheBox)) -> MustacheBox inEachFilter.swift:48 var position: [String: MustacheBox] = [:]EachFilter.swift:76 let transformedBoxes = boxes.enumerate().map { (index: Int, box: MustacheBox) -> MustacheBox inEachFilter.swift:76 let transformedBoxes = boxes.enumerate().map { (index: Int, box: MustacheBox) -> MustacheBox inEachFilter.swift:81 var position: [String: MustacheBox] = [:]HTMLEscapeHelper.swift:28 return MustacheBox(HTMLEscapeHelper.swift:25 var mustacheBox: MustacheBox {HTMLEscapeHelper.swift:49 private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {HTMLEscapeHelper.swift:49 private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {JavascriptEscapeHelper.swift:28 return MustacheBox(JavascriptEscapeHelper.swift:25 var mustacheBox: MustacheBox {JavascriptEscapeHelper.swift:49 private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {JavascriptEscapeHelper.swift:49 private func willRender(tag: Tag, box: MustacheBox) -> MustacheBox {Logger.swift:75 return MustacheBox(Logger.swift:74 public var mustacheBox: MustacheBox {ZipFilter.swift:30 var zippedGenerators: [AnyGenerator<MustacheBox>] = []ZipFilter.swift:57 var zippedBoxes: [MustacheBox] = []Box.swift:126 var mustacheBox: MustacheBox { get }Box.swift:136 extension MustacheBox {Box.swift:142 public var mustacheBox: MustacheBox {Box.swift:175 return MustacheBox(Box.swift:174 public var mustacheBox: MustacheBox {Box.swift:232 return MustacheBox(Box.swift:231 public var mustacheBox: MustacheBox {Box.swift:289 return MustacheBox(Box.swift:288 public var mustacheBox: MustacheBox {Box.swift:346 return MustacheBox(Box.swift:345 public var mustacheBox: MustacheBox {Box.swift:414 return MustacheBox(Box.swift:413 public var mustacheBox: MustacheBox {Box.swift:480 public var mustacheBox: MustacheBox {Box.swift:500 public var mustacheBox: MustacheBox {Box.swift:573 var dictionaryValue: [String: MustacheBox] = [:]Box.swift:576 if let v = value as? MustacheBox {Box.swift:593 return MustacheBox(Box.swift:594 converter: MustacheBox.Converter(dictionaryValue: dict),Box.swift:572 public var mustacheBox: MustacheBox {Box.swift:610 public func Box(boxable boxable: MustacheBoxable?) -> MustacheBox {Box.swift:738 private func BoxAnyObject(object: AnyObject?) -> MustacheBox {Box.swift:824 private func renderItems(info: RenderingInfo, @noescape box: (Generator.Element) -> MustacheBox) throws -> Rendering {Box.swift:916 private func mustacheBoxWithSetValue(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox {Box.swift:916 private func mustacheBoxWithSetValue(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox {Box.swift:917 return MustacheBox(Box.swift:918 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),Box.swift:966 private func mustacheBoxWithArrayValue(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox {Box.swift:966 private func mustacheBoxWithArrayValue(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox {Box.swift:967 return MustacheBox(Box.swift:968 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),Box.swift:1047 public func Box<C: CollectionType where C.Generator.Element: MustacheBoxable, C.Index.Distance == Int>(set set: C?) -> MustacheBox {Box.swift:1098 public func Box<C: CollectionType where C.Generator.Element: MustacheBoxable, C.Index: BidirectionalIndexType, C.Index.Distance == Int>(array array: C?) -> MustacheBox {Box.swift:1149 public func Box<C: CollectionType, T where C.Generator.Element == Optional<T>, T: MustacheBoxable, C.Index: BidirectionalIndexType, C.Index.Distance == Int>(array array: C?) -> MustacheBox {Box.swift:1210 public func Box<T: MustacheBoxable>(dictionary dictionary: [String: T]?) -> MustacheBox {Box.swift:1212 return MustacheBox(Box.swift:1213 converter: MustacheBox.Converter(Box.swift:1214 dictionaryValue: dictionary.reduce([String: MustacheBox](), combine: { (b, item: (key: String, value: T)) inBox.swift:1276 public func Box<T: MustacheBoxable>(optionalDictionary dictionary: [String: T?]?) -> MustacheBox {Box.swift:1278 return MustacheBox(Box.swift:1279 converter: MustacheBox.Converter(Box.swift:1280 dictionaryValue: dictionary.reduce([String: MustacheBox](), combine: { (b, item: (key: String, value: T?)) inBox.swift:1322 public func Box(filter filter: FilterFunction) -> MustacheBox {Box.swift:1323 return MustacheBox(filter: filter)Box.swift:1343 public func Box(render render: RenderFunction) -> MustacheBox {Box.swift:1344 return MustacheBox(render: render)Box.swift:1375 public func Box(willRender willRender: WillRenderFunction) -> MustacheBox {Box.swift:1376 return MustacheBox(willRender: willRender)Box.swift:1408 public func Box(didRender didRender: DidRenderFunction) -> MustacheBox {Box.swift:1409 return MustacheBox(didRender: didRender)Box.swift:1670 didRender: DidRenderFunction? = nil) -> MustacheBoxBox.swift:1673 return MustacheBox(Context.swift:74 public convenience init(_ box: MustacheBox) {Context.swift:86 public convenience init(registeredKey key: String, box: MustacheBox) {Context.swift:102 public func extendedContext(box: MustacheBox) -> Context {Context.swift:115 public func contextWithRegisteredKey(key: String, box: MustacheBox) -> Context {Context.swift:128 public var topBox: MustacheBox {Context.swift:163 public func mustacheBoxForKey(key: String) -> MustacheBox {Context.swift:201 public func mustacheBoxForExpression(string: String) throws -> MustacheBox {Context.swift:215 case Box(box: MustacheBox, parent: Context)CoreFunctions.swift:71 public typealias KeyedSubscriptFunction = (key: String) -> MustacheBoxCoreFunctions.swift:117 public typealias FilterFunction = (box: MustacheBox, partialApplication: Bool) throws -> MustacheBoxCoreFunctions.swift:117 public typealias FilterFunction = (box: MustacheBox, partialApplication: Bool) throws -> MustacheBoxCoreFunctions.swift:144 public func Filter(filter: (MustacheBox) throws -> MustacheBox) -> FilterFunction {CoreFunctions.swift:144 public func Filter(filter: (MustacheBox) throws -> MustacheBox) -> FilterFunction {CoreFunctions.swift:145 return { (box: MustacheBox, partialApplication: Bool) inCoreFunctions.swift:178 public func Filter<T>(filter: (T?) throws -> MustacheBox) -> FilterFunction {CoreFunctions.swift:179 return { (box: MustacheBox, partialApplication: Bool) inCoreFunctions.swift:210 public func VariadicFilter(filter: ([MustacheBox]) throws -> MustacheBox) -> FilterFunction {CoreFunctions.swift:210 public func VariadicFilter(filter: ([MustacheBox]) throws -> MustacheBox) -> FilterFunction {CoreFunctions.swift:219 func partialFilter(filter: ([MustacheBox]) throws -> MustacheBox, arguments: [MustacheBox]) -> FilterFunction {CoreFunctions.swift:219 func partialFilter(filter: ([MustacheBox]) throws -> MustacheBox, arguments: [MustacheBox]) -> FilterFunction {CoreFunctions.swift:219 func partialFilter(filter: ([MustacheBox]) throws -> MustacheBox, arguments: [MustacheBox]) -> FilterFunction {CoreFunctions.swift:220 return { (nextArgument: MustacheBox, partialApplication: Bool) inCoreFunctions.swift:265 return { (box: MustacheBox, partialApplication: Bool) inCoreFunctions.swift:295 public func Filter(filter: (MustacheBox, RenderingInfo) throws -> Rendering) -> FilterFunction {CoreFunctions.swift:296 return Filter { (box: MustacheBox) inCoreFunctions.swift:810 public typealias WillRenderFunction = (tag: Tag, box: MustacheBox) -> MustacheBoxCoreFunctions.swift:810 public typealias WillRenderFunction = (tag: Tag, box: MustacheBox) -> MustacheBoxCoreFunctions.swift:862 public typealias DidRenderFunction = (tag: Tag, box: MustacheBox, string: String?) -> VoidExpressionInvocation.swift:26 func invokeWithContext(context: Context) throws -> MustacheBox {ExpressionInvocation.swift:30 private func evaluate(context context: Context, expression: Expression) throws -> MustacheBox {MustacheBox.swift:127 public var arrayValue: [MustacheBox]? {MustacheBox.swift:135 public var dictionaryValue: [String: MustacheBox]? {MustacheBox.swift:148 public func mustacheBoxForKey(key: String) -> MustacheBox {MustacheBox.swift:505 let arrayValue: (() -> [MustacheBox]?)MustacheBox.swift:506 let dictionaryValue: (() -> [String: MustacheBox]?)MustacheBox.swift:509 @autoclosure(escaping) arrayValue: () -> [MustacheBox]? = nil,MustacheBox.swift:510 @autoclosure(escaping) dictionaryValue: () -> [String: MustacheBox]? = nil)MustacheBox.swift:518 extension MustacheBox {MustacheBox.swift:532 extension MustacheBox {RenderingEngine.swift:131 var box: MustacheBoxTemplate.swift:57 public func render(box: MustacheBox = Box()) throws -> String {Template.swift:135 public func extendBaseContext(box: MustacheBox) {Template.swift:159 public func registerInBaseContext(key: String, _ box: MustacheBox) {Template.swift:261 return MustacheBox(Template.swift:260 public var mustacheBox: MustacheBox {: Any? 0111 0112 /// The only empty box is `Box()`. 0113 public let isEmpty
EachFilter.swift:96 throw MustacheError(kind: .RenderError, message: "Non-enumerable argument in each filter: \(box.value)")ZipFilter.swift:40 throw MustacheError(kind: .RenderError, message: "Non-enumerable argument in zip filter: `\(box.value)`")CoreFunctions.swift:184 return try filter(box.value as? T)MustacheBox.swift:452 self.value = valueMustacheBox.swift:481 if let value = self.value {MustacheBox.swift:560 } else if let value = value {MustacheBox.swift:573 if value == nil && hasCustomRenderFunction {: Bool 0114 0115 /** 0116 The boolean value of the box. 0117 0118 It tells whether the Box should trigger or prevent the rendering of regular 0119 `{{#section}}...{{/}}` and inverted `{{^section}}...{{/}}`. 0120 */ 0121 public let boolValue
EachFilter.swift:26 if box.isEmpty {ZipFilter.swift:33 if box.isEmpty {Context.swift:166 if !box.isEmpty {Context.swift:176 if innerBox.isEmpty {ExpressionInvocation.swift:53 if filterBox.isEmpty {MustacheBox.swift:451 self.isEmpty = empty: Bool 0122 0123 /** 0124 If the boxed value can be iterated (Swift collection, NSArray, NSSet, etc.), 0125 returns an array of `MustacheBox`. 0126 */ 0127 public var arrayValue
MustacheBox.swift:454 self.boolValue = boolValue ?? !emptyRenderingEngine.swift:164 switch (inverted, box.boolValue) {: [MustacheBox]? { 0128 return converter?.arrayValue() 0129 } 0130 0131 /** 0132 If the boxed value is a dictionary (Swift dictionary, NSDictionary, etc.), 0133 returns a dictionary `[String: MustacheBox]`. 0134 */ 0135 public var dictionaryValue
EachFilter.swift:74 if let boxes = box.arrayValue {ZipFilter.swift:35 } else if let array = box.arrayValue {MustacheBox.swift:548 if let array = arrayValue {: [String: MustacheBox]? { 0136 return converter?.dictionaryValue() 0137 } 0138 0139 /** 0140 Extracts a key out of a box. 0141 0142 let box = Box(["firstName": "Arthur"]) 0143 box.mustacheBoxForKey("firstName").value // "Arthur" 0144 0145 - parameter key: A key. 0146 - returns: The MustacheBox for *key*. 0147 */ 0148 public func mustacheBoxForKey
EachFilter.swift:41 if let dictionary = box.dictionaryValue {MustacheBox.swift:551 } else if let dictionary = dictionaryValue {(key: String) -> MustacheBox { 0149 return keyedSubscript?(key: key) ?? Box() 0150 } 0151 0152 0153 // ------------------------------------------------------------------------- 0154 // MARK: - Other facets 0155 0156 /// See the documentation of `RenderFunction`. 0157 public private(set) var render
Context.swift:175 let innerBox = box.mustacheBoxForKey(key)ExpressionInvocation.swift:45 return try evaluate(context: context, expression: baseExpression).mustacheBoxForKey(identifier): RenderFunction 0158 0159 /// See the documentation of `FilterFunction`. 0160 public let filter
EachFilter.swift:56 return try element.box.render(info: info)EachFilter.swift:88 return try box.render(info: info)HTMLEscapeHelper.swift:56 let rendering = try box.render(info: info)JavascriptEscapeHelper.swift:55 let rendering = try box.render(info: info)Box.swift:847 let boxRendering = try box(item).render(info: info)CoreFunctions.swift:272 let rendering = try box.render(info: info)MustacheBox.swift:461 self.render = renderMustacheBox.swift:472 self.render = { (_) in return Rendering("") }MustacheBox.swift:474 self.render = { [unowned self] (info: RenderingInfo) inRenderingEngine.swift:162 rendering = try box.render(info: info)RenderingEngine.swift:169 rendering = try box.render(info: info): FilterFunction? 0161 0162 /// See the documentation of `WillRenderFunction`. 0163 public let willRender
ExpressionInvocation.swift:52 guard let filter = filterBox.filter else {MustacheBox.swift:456 self.filter = filterMustacheBox.swift:564 if let _ = filter {: WillRenderFunction? 0164 0165 /// See the documentation of `DidRenderFunction`. 0166 public let didRender
Context.swift:227 if let willRender = box.willRender {MustacheBox.swift:457 self.willRender = willRenderMustacheBox.swift:567 if let _ = willRender {: DidRenderFunction? 0167 0168 0169 // ------------------------------------------------------------------------- 0170 // MARK: - Multi-facetted Box Initialization 0171 0172 /** 0173 This is the most low-level initializer of MustacheBox. 0174 0175 It is suited for building "advanced" boxes. There are simpler versions of 0176 the `Box` function that may well better suit your need: you should check 0177 them. 0178 0179 This initializer can take up to seven parameters, all optional, that define 0180 how the box interacts with the Mustache engine: 0181 0182 - `value`: an optional boxed value 0183 - `boolValue`: an optional boolean value for the Box. 0184 - `keyedSubscript`: an optional KeyedSubscriptFunction 0185 - `filter`: an optional FilterFunction 0186 - `render`: an optional RenderFunction 0187 - `willRender`: an optional WillRenderFunction 0188 - `didRender`: an optional DidRenderFunction 0189 0190 0191 To illustrate the usage of all those parameters, let's look at how the 0192 `{{f(a)}}` tag is rendered. 0193 0194 First the `a` and `f` expressions are evaluated. The Mustache engine looks 0195 in the context stack for boxes whose *keyedSubscript* return non-empty boxes 0196 for the keys "a" and "f". Let's call them aBox and fBox. 0197 0198 Then the *filter* of the fBox is evaluated with aBox as an argument. It is 0199 likely that the result depends on the *value* of the aBox: it is the 0200 resultBox. 0201 0202 Then the Mustache engine is ready to render resultBox. It looks in the 0203 context stack for boxes whose *willRender* function is defined. Those 0204 willRender functions have the opportunity to process the resultBox, and 0205 eventually provide the box that will be actually rendered: the renderedBox. 0206 0207 The renderedBox has a *render* function: it is evaluated by the Mustache 0208 engine which appends its result to the final rendering. 0209 0210 Finally the Mustache engine looks in the context stack for boxes whose 0211 *didRender* function is defined, and call them. 0212 0213 0214 ### value 0215 0216 The optional `value` parameter gives the boxed value. The value is used when 0217 the box is rendered (unless you provide a custom RenderFunction). It is also 0218 returned by the `value` property of MustacheBox. 0219 0220 let aBox = MustacheBox(value: 1) 0221 0222 // Renders "1" 0223 let template = try! Template(string: "{{a}}") 0224 try! template.render(Box(["a": aBox])) 0225 0226 0227 ### boolValue 0228 0229 The optional `boolValue` parameter tells whether the Box should trigger or 0230 prevent the rendering of regular `{{#section}}...{{/}}` and inverted 0231 `{{^section}}...{{/}}` tags. The default boolValue is true, unless the 0232 Box is initialized without argument to build the empty box. 0233 0234 // Render "true", "false" 0235 let template = try! Template(string:"{{#.}}true{{/.}}{{^.}}false{{/.}}") 0236 try! template.render(MustacheBox(boolValue: true)) 0237 try! template.render(MustacheBox(boolValue: false)) 0238 0239 0240 ### keyedSubscript 0241 0242 The optional `keyedSubscript` parameter is a `KeyedSubscriptFunction` that 0243 lets the Mustache engine extract keys out of the box. For example, the 0244 `{{a}}` tag would call the subscript function with `"a"` as an argument, and 0245 render the returned box. 0246 0247 The default value is nil, which means that no key can be extracted. 0248 0249 See `KeyedSubscriptFunction` for a full discussion of this type. 0250 0251 let box = MustacheBox(keyedSubscript: { (key: String) in 0252 return Box("key:\(key)") 0253 }) 0254 0255 // Renders "key:a" 0256 let template = try! Template(string:"{{a}}") 0257 try! template.render(box) 0258 0259 0260 ### filter 0261 0262 The optional `filter` parameter is a `FilterFunction` that lets the Mustache 0263 engine evaluate filtered expression that involve the box. The default value 0264 is nil, which means that the box can not be used as a filter. 0265 0266 See `FilterFunction` for a full discussion of this type. 0267 0268 let box = MustacheBox(filter: Filter { (x: Int?) in 0269 return Box(x! * x!) 0270 }) 0271 0272 // Renders "100" 0273 let template = try! Template(string:"{{square(x)}}") 0274 try! template.render(Box(["square": box, "x": Box(10)])) 0275 0276 0277 ### render 0278 0279 The optional `render` parameter is a `RenderFunction` that is evaluated when 0280 the Box is rendered. 0281 0282 The default value is nil, which makes the box perform default Mustache 0283 rendering: 0284 0285 - `{{box}}` renders the built-in Swift String Interpolation of the value, 0286 HTML-escaped. 0287 0288 - `{{{box}}}` renders the built-in Swift String Interpolation of the value, 0289 not HTML-escaped. 0290 0291 - `{{#box}}...{{/box}}` does not render if `boolValue` is false. Otherwise, 0292 it pushes the box on the top of the context stack, and renders the section 0293 once. 0294 0295 - `{{^box}}...{{/box}}` renders once if `boolValue` is false. Otherwise, it 0296 does not render. 0297 0298 See `RenderFunction` for a full discussion of this type. 0299 0300 let box = MustacheBox(render: { (info: RenderingInfo) in 0301 return Rendering("foo") 0302 }) 0303 0304 // Renders "foo" 0305 let template = try! Template(string:"{{.}}") 0306 try! template.render(box) 0307 0308 0309 ### willRender, didRender 0310 0311 The optional `willRender` and `didRender` parameters are a 0312 `WillRenderFunction` and `DidRenderFunction` that are evaluated for all tags 0313 as long as the box is in the context stack. 0314 0315 See `WillRenderFunction` and `DidRenderFunction` for a full discussion of 0316 those types. 0317 0318 let box = MustacheBox(willRender: { (tag: Tag, box: MustacheBox) in 0319 return Box("baz") 0320 }) 0321 0322 // Renders "baz baz" 0323 let template = try! Template(string:"{{#.}}{{foo}} {{bar}}{{/.}}") 0324 try! template.render(box) 0325 0326 0327 ### Multi-facetted boxes 0328 0329 By mixing all those parameters, you can finely tune the behavior of a box. 0330 0331 GRMustache source code ships a few multi-facetted boxes, which may inspire 0332 you. See for example: 0333 0334 - NSFormatter.mustacheBox 0335 - HTMLEscape.mustacheBox 0336 - StandardLibrary.Localizer.mustacheBox 0337 0338 Let's give an example: 0339 0340 // A regular type: 0341 0342 struct Person { 0343 let firstName: String 0344 let lastName: String 0345 } 0346 0347 We want: 0348 0349 1. `{{person.firstName}}` and `{{person.lastName}}` should render the 0350 matching properties. 0351 2. `{{person}}` should render the concatenation of the first and last names. 0352 0353 We'll provide a `KeyedSubscriptFunction` to implement 1, and a 0354 `RenderFunction` to implement 2: 0355 0356 // Have Person conform to MustacheBoxable so that we can box people, and 0357 // render them: 0358 0359 extension Person : MustacheBoxable { 0360 0361 // MustacheBoxable protocol requires objects to implement this property 0362 // and return a MustacheBox: 0363 0364 var mustacheBox: MustacheBox { 0365 0366 // A person is a multi-facetted object: 0367 return MustacheBox( 0368 // It has a value: 0369 value: self, 0370 0371 // It lets Mustache extracts properties by name: 0372 keyedSubscript: { (key: String) -> MustacheBox in 0373 switch key { 0374 case "firstName": return Box(self.firstName) 0375 case "lastName": return Box(self.lastName) 0376 default: return Box() 0377 } 0378 }, 0379 0380 // It performs custom rendering: 0381 render: { (info: RenderingInfo) -> Rendering in 0382 switch info.tag.type { 0383 case .Variable: 0384 // {{ person }} 0385 return Rendering("\(self.firstName) \(self.lastName)") 0386 case .Section: 0387 // {{# person }}...{{/}} 0388 // 0389 // Perform the default rendering: push self on the top 0390 // of the context stack, and render the section: 0391 let context = info.context.extendedContext(Box(self)) 0392 return try info.tag.render(context) 0393 } 0394 } 0395 ) 0396 } 0397 } 0398 0399 // Renders "The person is Errol Flynn" 0400 let person = Person(firstName: "Errol", lastName: "Flynn") 0401 let template = try! Template(string: "{{# person }}The person is {{.}}{{/ person }}") 0402 try! template.render(Box(["person": person])) 0403 0404 - parameter value: An optional boxed value. 0405 - parameter boolValue: An optional boolean value for the Box. 0406 - parameter keyedSubscript: An optional `KeyedSubscriptFunction`. 0407 - parameter filter: An optional `FilterFunction`. 0408 - parameter render: An optional `RenderFunction`. 0409 - parameter willRender: An optional `WillRenderFunction`. 0410 - parameter didRender: An optional `DidRenderFunction`. 0411 - returns: A MustacheBox. 0412 */ 0413 public convenience init
Context.swift:242 if let didRender = box.didRender {MustacheBox.swift:458 self.didRender = didRenderMustacheBox.swift:570 if let _ = didRender {( 0414 value: Any? = nil, 0415 boolValue: Bool? = nil, 0416 keyedSubscript: KeyedSubscriptFunction? = nil, 0417 filter: FilterFunction? = nil, 0418 render: RenderFunction? = nil, 0419 willRender: WillRenderFunction? = nil, 0420 didRender: DidRenderFunction? = nil) 0421 { 0422 self.init( 0423 converter: nil, 0424 value: value, 0425 boolValue: boolValue, 0426 keyedSubscript: keyedSubscript, 0427 filter: filter, 0428 render: render, 0429 willRender: willRender, 0430 didRender: didRender) 0431 } 0432 0433 0434 // ------------------------------------------------------------------------- 0435 // MARK: - Internal 0436 0437 let keyedSubscript
HTMLEscapeHelper.swift:28 return MustacheBox(JavascriptEscapeHelper.swift:28 return MustacheBox(Logger.swift:75 return MustacheBox(Box.swift:175 return MustacheBox(Box.swift:232 return MustacheBox(Box.swift:289 return MustacheBox(Box.swift:346 return MustacheBox(Box.swift:414 return MustacheBox(Box.swift:1323 return MustacheBox(filter: filter)Box.swift:1344 return MustacheBox(render: render)Box.swift:1376 return MustacheBox(willRender: willRender)Box.swift:1409 return MustacheBox(didRender: didRender)Box.swift:1673 return MustacheBox(Template.swift:261 return MustacheBox(: KeyedSubscriptFunction? 0438 let converter
MustacheBox.swift:149 return keyedSubscript?(key: key) ?? Box()MustacheBox.swift:455 self.keyedSubscript = keyedSubscript: Converter? 0439 0440 init
MustacheBox.swift:128 return converter?.arrayValue()MustacheBox.swift:136 return converter?.dictionaryValue()MustacheBox.swift:453 self.converter = converter( 0441 converter: Converter?, 0442 value: Any? = nil, 0443 boolValue: Bool? = nil, 0444 keyedSubscript: KeyedSubscriptFunction? = nil, 0445 filter: FilterFunction? = nil, 0446 render: RenderFunction? = nil, 0447 willRender: WillRenderFunction? = nil, 0448 didRender: DidRenderFunction? = nil) 0449 { 0450 let empty = (value == nil) && (keyedSubscript == nil) && (render == nil) && (filter == nil) && (willRender == nil) && (didRender == nil) 0451 self.isEmpty = empty 0452 self.value = value 0453 self.converter = converter 0454 self.boolValue = boolValue ?? !empty 0455 self.keyedSubscript = keyedSubscript 0456 self.filter = filter 0457 self.willRender = willRender 0458 self.didRender = didRender 0459 if let render = render { 0460 self.hasCustomRenderFunction = true 0461 self.render = render 0462 } else { 0463 // The default render function: it renders {{variable}} tags as the 0464 // boxed value, and {{#section}}...{{/}} tags by adding the box to 0465 // the context stack. 0466 // 0467 // IMPLEMENTATIN NOTE 0468 // 0469 // We have to set self.render twice in order to avoid the compiler 0470 // error: "variable 'self.render' captured by a closure before being 0471 // initialized" 0472 self.render = { (_) in return Rendering("") } 0473 self.hasCustomRenderFunction = false 0474 self.render = { [unowned self] (info: RenderingInfo) in 0475 0476 // Default rendering depends on the tag type: 0477 switch info.tag.type { 0478 case .Variable: 0479 // {{ box }} and {{{ box }}} 0480 0481 if let value = self.value { 0482 // Use the built-in Swift String Interpolation: 0483 return Rendering("\(value)", .Text) 0484 } else { 0485 return Rendering("", .Text) 0486 } 0487 case .Section: 0488 // {{# box }}...{{/ box }} 0489 0490 // Push the value on the top of the context stack: 0491 let context = info.context.extendedContext(self) 0492 0493 // Renders the inner content of the section tag: 0494 return try info.tag.render(context) 0495 } 0496 } 0497 } 0498 } 0499 0500 private let hasCustomRenderFunction
Box.swift:593 return MustacheBox(Box.swift:917 return MustacheBox(Box.swift:967 return MustacheBox(Box.swift:1212 return MustacheBox(Box.swift:1278 return MustacheBox(MustacheBox.swift:422 self.init(: Bool 0501 0502 // Converter wraps all the conversion closures that help MustacheBox expose 0503 // its raw value (typed Any) as useful types. 0504 struct Converter
MustacheBox.swift:460 self.hasCustomRenderFunction = trueMustacheBox.swift:473 self.hasCustomRenderFunction = falseMustacheBox.swift:573 if value == nil && hasCustomRenderFunction {{ 0505 let arrayValue
Box.swift:594 converter: MustacheBox.Converter(dictionaryValue: dict),Box.swift:918 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),Box.swift:968 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),Box.swift:1213 converter: MustacheBox.Converter(Box.swift:1279 converter: MustacheBox.Converter(MustacheBox.swift:438 let converter: Converter?MustacheBox.swift:441 converter: Converter?,: (() -> [MustacheBox]?) 0506 let dictionaryValue
MustacheBox.swift:128 return converter?.arrayValue()MustacheBox.swift:512 self.arrayValue = arrayValue: (() -> [String: MustacheBox]?) 0507 0508 init
MustacheBox.swift:136 return converter?.dictionaryValue()MustacheBox.swift:513 self.dictionaryValue = dictionaryValue( 0509 @autoclosure(escaping) arrayValue: () -> [MustacheBox]? = nil, 0510 @autoclosure(escaping) dictionaryValue: () -> [String: MustacheBox]? = nil) 0511 { 0512 self.arrayValue = arrayValue 0513 self.dictionaryValue = dictionaryValue 0514 } 0515 } 0516 } 0517 0518 extension MustacheBox { 0519 /// A textual representation of `self`. 0520 public var description: String { 0521 let facets = self.facetsDescriptions 0522 switch facets.count { 0523 case 0: 0524 return "MustacheBox(Empty)" 0525 default: 0526 let content = facets.joinWithSeparator(",") 0527 return "MustacheBox(\(content))" 0528 } 0529 } 0530 } 0531 0532 extension MustacheBox { 0533 /// A textual representation of the boxed value. Useful for debugging. 0534 public var valueDescription
Box.swift:594 converter: MustacheBox.Converter(dictionaryValue: dict),Box.swift:918 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),Box.swift:968 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })),Box.swift:1213 converter: MustacheBox.Converter(Box.swift:1279 converter: MustacheBox.Converter(: String { 0535 let facets = self.facetsDescriptions 0536 switch facets.count { 0537 case 0: 0538 return "Empty" 0539 case 1: 0540 return facets.first! 0541 default: 0542 return "(" + facets.joinWithSeparator(",") + ")" 0543 } 0544 } 0545 0546 var facetsDescriptions
Logger.swift:78 self.log("\(self.indentationPrefix)\(tag) will render \(box.valueDescription)")Logger.swift:88 self.log("\(self.indentationPrefix)\(tag) did render \(box.valueDescription) as \(string.debugDescription)")MustacheBox.swift:549 let items = array.map { $0.valueDescription }.joinWithSeparator(",")MustacheBox.swift:556 return "\(key.debugDescription):\(box.valueDescription)": [String] { 0547 var facets = [String]() 0548 if let array = arrayValue { 0549 let items = array.map { $0.valueDescription }.joinWithSeparator(",") 0550 facets.append("[\(items)]") 0551 } else if let dictionary = dictionaryValue { 0552 if dictionary.isEmpty { 0553 facets.append("[:]") 0554 } else { 0555 let items = dictionary.map { (key, box) in 0556 return "\(key.debugDescription):\(box.valueDescription)" 0557 }.joinWithSeparator(",") 0558 facets.append("[\(items)]") 0559 } 0560 } else if let value = value { 0561 facets.append(String(reflecting: value)) 0562 } 0563 0564 if let _ = filter { 0565 facets.append("FilterFunction") 0566 } 0567 if let _ = willRender { 0568 facets.append("WillRenderFunction") 0569 } 0570 if let _ = didRender { 0571 facets.append("DidRenderFunction") 0572 } 0573 if value == nil && hasCustomRenderFunction { 0574 facets.append("RenderFunction") 0575 } 0576 0577 return facets 0578 } 0579 } 0580
MustacheBox.swift:521 let facets = self.facetsDescriptionsMustacheBox.swift:535 let facets = self.facetsDescriptions