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 // "It's all boxes all the way down." 0024 // 0025 // Mustache templates don't eat raw values: they eat boxed values. 0026 // 0027 // To box something, you use the `Box()` function. It comes in several variants 0028 // so that nearly anything can be boxed and feed templates: 0029 // 0030 // let value = ... 0031 // template.render(Box(value)) 0032 // 0033 // This file is organized in five sections with many examples. You can use the 0034 // Playground included in `Mustache.xcworkspace` to run those examples. 0035 // 0036 // 0037 // - MustacheBoxable and the Boxing of Value Types 0038 // 0039 // The `MustacheBoxable` protocol lets any type describe how it interacts with 0040 // the Mustache rendering engine. 0041 // 0042 // It is adopted by the standard types Bool, Int, UInt, Double, String, and 0043 // NSObject. 0044 // 0045 // 0046 // - Boxing of Collections 0047 // 0048 // Learn how Array and Set are rendered. 0049 // 0050 // 0051 // - Boxing of Dictionaries 0052 // 0053 // Learn how Dictionary and NSDictionary are rendered. 0054 // 0055 // 0056 // - Boxing of Core Mustache functions 0057 // 0058 // The "core Mustache functions" are raw filters, Mustache lambdas, etc. Those 0059 // can be boxed as well so that you can feed templates with them. 0060 // 0061 // 0062 // - Boxing of multi-facetted values 0063 // 0064 // Describes the most advanced `Box()` function. 0065 0066 0067 // ============================================================================= 0068 // MARK: - MustacheBoxable and the Boxing of Value Types 0069 0070 /** 0071 The MustacheBoxable protocol gives any type the ability to feed Mustache 0072 templates. 0073 0074 It is adopted by the standard types Bool, Int, UInt, Double, String, and 0075 NSObject. 0076 0077 Your own types can conform to it as well, so that they can feed templates: 0078 0079 extension Profile: MustacheBoxable { ... } 0080 0081 let profile = ... 0082 let template = try! Template(named: "Profile") 0083 let rendering = try! template.render(Box(profile)) 0084 */ 0085 public protocol MustacheBoxable{ 0086 0087 /** 0088 You should not directly call the `mustacheBox` property. Always use the 0089 `Box()` function instead: 0090 0091 value.mustacheBox // Valid, but discouraged 0092 Box(value) // Preferred 0093 0094 Return a `MustacheBox` that describes how your type interacts with the 0095 rendering engine. 0096 0097 You can for example box another value that is already boxable, such as 0098 dictionaries: 0099 0100 struct Person { 0101 let firstName: String 0102 let lastName: String 0103 } 0104 0105 extension Person : MustacheBoxable { 0106 // Expose the `firstName`, `lastName` and `fullName` keys to 0107 // Mustache templates: 0108 var mustacheBox: MustacheBox { 0109 return Box([ 0110 "firstName": firstName, 0111 "lastName": lastName, 0112 "fullName": "\(self.firstName) \(self.lastName)", 0113 ]) 0114 } 0115 } 0116 0117 let person = Person(firstName: "Tom", lastName: "Selleck") 0118 0119 // Renders "Tom Selleck" 0120 let template = try! Template(string: "{{person.fullName}}") 0121 try! template.render(Box(["person": Box(person)])) 0122 0123 However, there are multiple ways to build a box, several `Box()` functions. 0124 See their documentations. 0125 */ 0126 var mustacheBox
HTMLEscapeHelper.swift:23 final class HTMLEscapeHelper : MustacheBoxable {JavascriptEscapeHelper.swift:23 final class JavascriptEscapeHelper : MustacheBoxable {Logger.swift:47 public final class Logger : MustacheBoxable {StandardLibrary.swift:57 public static let HTMLEscape: MustacheBoxable = HTMLEscapeHelper()StandardLibrary.swift:117 public static let javascriptEscape: MustacheBoxable = JavascriptEscapeHelper()Box.swift:152 extension Bool: MustacheBoxable {Box.swift:207 extension Int: MustacheBoxable {Box.swift:264 extension UInt: MustacheBoxable {Box.swift:321 extension Double: MustacheBoxable {Box.swift:378 extension String: MustacheBoxable {Box.swift:432 extension Set: MustacheBoxable {Box.swift:481 var array: [MustacheBoxable] = []Box.swift:483 if let e = element as? MustacheBoxable {Box.swift:499 extension Array: MustacheBoxable {Box.swift:501 var array: [MustacheBoxable] = []Box.swift:503 if let e = element as? MustacheBoxable {Box.swift:519 extension Dictionary: MustacheBoxable {Box.swift:578 } else if let v = value as? MustacheBoxable {Box.swift:610 public func Box(boxable boxable: MustacheBoxable?) -> MustacheBox {Box.swift:739 if let boxable = object as? MustacheBoxable {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:1276 public func Box<T: MustacheBoxable>(optionalDictionary dictionary: [String: T?]?) -> MustacheBox {Template.swift:219 extension Template : MustacheBoxable {: MustacheBox { get } 0127 } 0128 0129 // IMPLEMENTATION NOTE 0130 // 0131 // This protocol conformance is not only a matter of consistency. It is also 0132 // a convenience for the library implementation: it makes arrays 0133 // [MustacheBox] boxable via Box<C: CollectionType where C.Generator.Element: MustacheBoxable>(collection: C?) 0134 // and dictionaries [String:MustacheBox] boxable via Box<T: MustacheBoxable>(dictionary: [String: T]?) 0135 0136 extension MustacheBox { 0137 0138 /** 0139 `MustacheBox` adopts the `MustacheBoxable` protocol so that it can feed 0140 Mustache templates. Its mustacheBox property returns itself. 0141 */ 0142 public var mustacheBox: MustacheBox { 0143 return self 0144 } 0145 } 0146 0147 0148 /** 0149 GRMustache provides built-in support for rendering `Bool`. 0150 */ 0151 0152 extension Bool: MustacheBoxable { 0153 0154 /** 0155 `Bool` adopts the `MustacheBoxable` protocol so that it can feed Mustache 0156 templates. 0157 0158 You should not directly call the `mustacheBox` property. Always use the 0159 `Box()` function instead: 0160 0161 true.mustacheBox // Valid, but discouraged 0162 Box(true) // Preferred 0163 0164 0165 ### Rendering 0166 0167 - `{{bool}}` renders as `0` or `1`. 0168 0169 - `{{#bool}}...{{/bool}}` renders if and only if `bool` is true. 0170 0171 - `{{^bool}}...{{/bool}}` renders if and only if `bool` is false. 0172 0173 */ 0174 public var mustacheBox: MustacheBox { 0175 return MustacheBox( 0176 value: self, 0177 boolValue: self, 0178 render: { (info: RenderingInfo) in 0179 switch info.tag.type { 0180 case .Variable: 0181 // {{ bool }} 0182 return Rendering("\(self ? 1 : 0)") // Behave like [NSNumber numberWithBool:] 0183 case .Section: 0184 if info.enumerationItem { 0185 // {{# bools }}...{{/ bools }} 0186 return try info.tag.render(info.context.extendedContext(Box(boolValue: self))) 0187 } else { 0188 // {{# bool }}...{{/ bool }} 0189 // 0190 // Bools do not enter the context stack when used in a 0191 // boolean section. 0192 // 0193 // This behavior must not change: 0194 // https://github.com/groue/GRMustache/issues/83 0195 return try info.tag.render(info.context) 0196 } 0197 } 0198 }) 0199 } 0200 } 0201 0202 0203 /** 0204 GRMustache provides built-in support for rendering `Int`. 0205 */ 0206 0207 extension Int: MustacheBoxable { 0208 0209 /** 0210 `Int` adopts the `MustacheBoxable` protocol so that it can feed Mustache 0211 templates. 0212 0213 You should not directly call the `mustacheBox` property. Always use the 0214 `Box()` function instead: 0215 0216 1.mustacheBox // Valid, but discouraged 0217 Box(1) // Preferred 0218 0219 0220 ### Rendering 0221 0222 - `{{int}}` is rendered with built-in Swift String Interpolation. 0223 Custom formatting can be explicitly required with NSNumberFormatter, as in 0224 `{{format(a)}}` (see `NSFormatter`). 0225 0226 - `{{#int}}...{{/int}}` renders if and only if `int` is not 0 (zero). 0227 0228 - `{{^int}}...{{/int}}` renders if and only if `int` is 0 (zero). 0229 0230 */ 0231 public var mustacheBox: MustacheBox { 0232 return MustacheBox( 0233 value: self, 0234 boolValue: (self != 0), 0235 render: { (info: RenderingInfo) in 0236 switch info.tag.type { 0237 case .Variable: 0238 // {{ int }} 0239 return Rendering("\(self)") 0240 case .Section: 0241 if info.enumerationItem { 0242 // {{# ints }}...{{/ ints }} 0243 return try info.tag.render(info.context.extendedContext(Box(value: self))) 0244 } else { 0245 // {{# int }}...{{/ int }} 0246 // 0247 // Ints do not enter the context stack when used in a 0248 // boolean section. 0249 // 0250 // This behavior must not change: 0251 // https://github.com/groue/GRMustache/issues/83 0252 return try info.tag.render(info.context) 0253 } 0254 } 0255 }) 0256 } 0257 } 0258 0259 0260 /** 0261 GRMustache provides built-in support for rendering `UInt`. 0262 */ 0263 0264 extension UInt: MustacheBoxable { 0265 0266 /** 0267 `UInt` adopts the `MustacheBoxable` protocol so that it can feed Mustache 0268 templates. 0269 0270 You should not directly call the `mustacheBox` property. Always use the 0271 `Box()` function instead: 0272 0273 1.mustacheBox // Valid, but discouraged 0274 Box(1) // Preferred 0275 0276 0277 ### Rendering 0278 0279 - `{{uint}}` is rendered with built-in Swift String Interpolation. 0280 Custom formatting can be explicitly required with NSNumberFormatter, as in 0281 `{{format(a)}}` (see `NSFormatter`). 0282 0283 - `{{#uint}}...{{/uint}}` renders if and only if `uint` is not 0 (zero). 0284 0285 - `{{^uint}}...{{/uint}}` renders if and only if `uint` is 0 (zero). 0286 0287 */ 0288 public var mustacheBox: MustacheBox { 0289 return MustacheBox( 0290 value: self, 0291 boolValue: (self != 0), 0292 render: { (info: RenderingInfo) in 0293 switch info.tag.type { 0294 case .Variable: 0295 // {{ uint }} 0296 return Rendering("\(self)") 0297 case .Section: 0298 if info.enumerationItem { 0299 // {{# uints }}...{{/ uints }} 0300 return try info.tag.render(info.context.extendedContext(Box(value: self))) 0301 } else { 0302 // {{# uint }}...{{/ uint }} 0303 // 0304 // Uints do not enter the context stack when used in a 0305 // boolean section. 0306 // 0307 // This behavior must not change: 0308 // https://github.com/groue/GRMustache/issues/83 0309 return try info.tag.render(info.context) 0310 } 0311 } 0312 }) 0313 } 0314 } 0315 0316 0317 /** 0318 GRMustache provides built-in support for rendering `Double`. 0319 */ 0320 0321 extension Double: MustacheBoxable { 0322 0323 /** 0324 `Double` adopts the `MustacheBoxable` protocol so that it can feed Mustache 0325 templates. 0326 0327 You should not directly call the `mustacheBox` property. Always use the 0328 `Box()` function instead: 0329 0330 3.14.mustacheBox // Valid, but discouraged 0331 Box(3.14) // Preferred 0332 0333 0334 ### Rendering 0335 0336 - `{{double}}` is rendered with built-in Swift String Interpolation. 0337 Custom formatting can be explicitly required with NSNumberFormatter, as in 0338 `{{format(a)}}` (see `NSFormatter`). 0339 0340 - `{{#double}}...{{/double}}` renders if and only if `double` is not 0 (zero). 0341 0342 - `{{^double}}...{{/double}}` renders if and only if `double` is 0 (zero). 0343 0344 */ 0345 public var mustacheBox: MustacheBox { 0346 return MustacheBox( 0347 value: self, 0348 boolValue: (self != 0.0), 0349 render: { (info: RenderingInfo) in 0350 switch info.tag.type { 0351 case .Variable: 0352 // {{ double }} 0353 return Rendering("\(self)") 0354 case .Section: 0355 if info.enumerationItem { 0356 // {{# doubles }}...{{/ doubles }} 0357 return try info.tag.render(info.context.extendedContext(Box(value: self))) 0358 } else { 0359 // {{# double }}...{{/ double }} 0360 // 0361 // Doubles do not enter the context stack when used in a 0362 // boolean section. 0363 // 0364 // This behavior must not change: 0365 // https://github.com/groue/GRMustache/issues/83 0366 return try info.tag.render(info.context) 0367 } 0368 } 0369 }) 0370 } 0371 } 0372 0373 0374 /** 0375 GRMustache provides built-in support for rendering `String`. 0376 */ 0377 0378 extension String: MustacheBoxable { 0379 0380 /** 0381 `String` adopts the `MustacheBoxable` protocol so that it can feed Mustache 0382 templates. 0383 0384 You should not directly call the `mustacheBox` property. Always use the 0385 `Box()` function instead: 0386 0387 "foo".mustacheBox // Valid, but discouraged 0388 Box("foo") // Preferred 0389 0390 0391 ### Rendering 0392 0393 - `{{string}}` renders the string, HTML-escaped. 0394 0395 - `{{{string}}}` renders the string, *not* HTML-escaped. 0396 0397 - `{{#string}}...{{/string}}` renders if and only if `string` is not empty. 0398 0399 - `{{^string}}...{{/string}}` renders if and only if `string` is empty. 0400 0401 HTML-escaping of `{{string}}` tags is disabled for Text templates: see 0402 `Configuration.contentType` for a full discussion of the content type of 0403 templates. 0404 0405 0406 ### Keys exposed to templates 0407 0408 A string can be queried for the following keys: 0409 0410 - `length`: the number of characters in the string. 0411 0412 */ 0413 public var mustacheBox: MustacheBox { 0414 return MustacheBox( 0415 value: self, 0416 boolValue: (self.characters.count > 0), 0417 keyedSubscript: { (key: String) in 0418 switch key { 0419 case "length": 0420 return Box(value: self.characters.count) 0421 default: 0422 return Box() 0423 } 0424 }) 0425 } 0426 } 0427 0428 /** 0429 GRMustache provides built-in support for rendering `Set`. 0430 */ 0431 0432 extension Set: MustacheBoxable { 0433 0434 /** 0435 `NSSet` adopts the `MustacheBoxable` protocol so that it can feed Mustache 0436 templates. 0437 0438 let set: NSSet = [1,2,3] 0439 0440 // Renders "213" 0441 let template = try! Template(string: "{{#set}}{{.}}{{/set}}") 0442 try! template.render(Box(["set": Box(set)])) 0443 0444 0445 You should not directly call the `mustacheBox` property. Always use the 0446 `Box()` function instead: 0447 0448 set.mustacheBox // Valid, but discouraged 0449 Box(set) // Preferred 0450 0451 0452 ### Rendering 0453 0454 - `{{set}}` renders the concatenation of the renderings of the set items, in 0455 any order. 0456 0457 - `{{#set}}...{{/set}}` renders as many times as there are items in `set`, 0458 pushing each item on its turn on the top of the context stack. 0459 0460 - `{{^set}}...{{/set}}` renders if and only if `set` is empty. 0461 0462 0463 ### Keys exposed to templates 0464 0465 A set can be queried for the following keys: 0466 0467 - `count`: number of elements in the set 0468 - `first`: the first object in the set 0469 0470 Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once, 0471 if and only if `set` is not empty. 0472 0473 0474 ### Unwrapping from MustacheBox 0475 0476 Whenever you want to extract a collection of a MustacheBox, use the 0477 `arrayValue` property: it reliably returns an Array of MustacheBox, whatever 0478 the actual type of the raw boxed value (Set, Array, NSArray, NSSet, ...) 0479 */ 0480 public var mustacheBox: MustacheBox { 0481 var array: [MustacheBoxable] = [] 0482 for element in self { 0483 if let e = element as? MustacheBoxable { 0484 array.append(e) 0485 } else { 0486 print("Tried to use a set as MustacheBoxable, but the set is not boxable.") 0487 return Box() 0488 } 0489 } 0490 let a = array 0491 return a.mustacheBoxWithSetValue(a, box: { Box(boxable: $0) }) 0492 } 0493 } 0494 0495 /** 0496 GRMustache provides built-in support for rendering `Array`. 0497 */ 0498 0499 extension Array: MustacheBoxable { 0500 public var mustacheBox: MustacheBox { 0501 var array: [MustacheBoxable] = [] 0502 for element in self { 0503 if let e = element as? MustacheBoxable { 0504 array.append(e) 0505 } else { 0506 print("Tried to use an array as MustacheBoxable, but the array is not boxable.") 0507 return Box() 0508 } 0509 } 0510 let a = array 0511 return a.mustacheBoxWithArrayValue(a, box: { Box(boxable: $0) }) 0512 } 0513 } 0514 0515 /** 0516 GRMustache provides built-in support for rendering `Dictionary`. 0517 */ 0518 0519 extension Dictionary: MustacheBoxable { 0520 0521 /** 0522 `Dictionary` adopts the `MustacheBoxable` protocol so that it can feed 0523 Mustache templates. 0524 0525 // Renders "Freddy Mercury" 0526 let dictionary: NSDictionary = [ 0527 "firstName": "Freddy", 0528 "lastName": "Mercury" 0529 ] 0530 0531 let template = try! Template(string: "{{firstName}} {{lastName}}") 0532 let rendering = try! template.render(Box(dictionary)) 0533 0534 0535 You should not directly call the `mustacheBox` property. Always use the 0536 `Box()` function instead: 0537 0538 dictionary.mustacheBox // Valid, but discouraged 0539 Box(dictionary) // Preferred 0540 0541 0542 ### Rendering 0543 0544 - `{{dictionary}}` renders the result of the `description` method, HTML-escaped. 0545 0546 - `{{{dictionary}}}` renders the result of the `description` method, *not* HTML-escaped. 0547 0548 - `{{#dictionary}}...{{/dictionary}}` renders once, pushing `dictionary` on 0549 the top of the context stack. 0550 0551 - `{{^dictionary}}...{{/dictionary}}` does not render. 0552 0553 0554 In order to iterate over the key/value pairs of a dictionary, use the `each` 0555 filter from the Standard Library: 0556 0557 // Attach StandardLibrary.each to the key "each": 0558 let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>") 0559 template.registerInBaseContext("each", Box(StandardLibrary.each)) 0560 0561 // Renders "<name:Arthur, age:36, >" 0562 let dictionary = ["name": "Arthur", "age": 36] as NSDictionary 0563 let rendering = try! template.render(Box(["dictionary": dictionary])) 0564 0565 0566 ### Unwrapping from MustacheBox 0567 0568 Whenever you want to extract a dictionary of a MustacheBox, use the 0569 `dictionaryValue` property: it reliably returns an `[String: MustacheBox]` 0570 dictionary, whatever the actual type of the raw boxed value. 0571 */ 0572 public var mustacheBox: MustacheBox { 0573 var dictionaryValue: [String: MustacheBox] = [:] 0574 for (key, value) in self { 0575 if let k = key as? String { 0576 if let v = value as? MustacheBox { 0577 dictionaryValue[k] = v 0578 } else if let v = value as? MustacheBoxable { 0579 dictionaryValue[k] = Box(boxable: v) 0580 } else { 0581 print("Tried to use a dictionary as MustacheBoxable, but the value: \(value) is not boxable.") 0582 return Box() 0583 } 0584 } 0585 } 0586 0587 let dict = dictionaryValue 0588 0589 let keyedSubscript = { (subscriptKey: String) in 0590 dict[subscriptKey] ?? Box() 0591 } 0592 0593 return MustacheBox( 0594 converter: MustacheBox.Converter(dictionaryValue: dict), 0595 value: self, 0596 keyedSubscript: keyedSubscript 0597 ) 0598 } 0599 } 0600 0601 /** 0602 Values that conform to the `MustacheBoxable` protocol can feed Mustache 0603 templates. 0604 0605 - parameter boxable: An optional value that conform to the `MustacheBoxable` 0606 protocol. 0607 0608 - returns: A MustacheBox that wraps *boxable*. 0609 */ 0610 public func Box
Box.swift:611 return boxable?.mustacheBox ?? Box()Box.swift:740 return boxable.mustacheBox(boxable boxable: MustacheBoxable?) -> MustacheBox { 0611 return boxable?.mustacheBox ?? Box() 0612 } 0613 0614 // IMPLEMENTATION NOTE 0615 // 0616 // Why is there a BoxAnyObject(AnyObject?) function, but no Box(AnyObject?) 0617 // 0618 // GRMustache aims at having a single boxing function: Box(), with many 0619 // overloaded variants. This lets the user box anything, standard Swift types 0620 // (Bool, String, etc.), custom types, as well as opaque types (such as 0621 // StandardLibrary.javascriptEscape). 0622 // 0623 // For example: 0624 // 0625 // public func Box(boxable: MustacheBoxable?) -> MustacheBox 0626 // public func Box(filter: FilterFunction) -> MustacheBox 0627 // 0628 // Sometimes values come out of Foundation objects: 0629 // 0630 // class NSDictionary { 0631 // subscript (key: NSCopying) -> AnyObject? { get } 0632 // } 0633 // 0634 // So we need a Box(AnyObject?) function, right? 0635 // 0636 // Unfortunately, this will not work: 0637 // 0638 // protocol MustacheBoxable {} 0639 // class Thing: MustacheBoxable {} 0640 // 0641 // func Box(x: MustacheBoxable?) -> String { return "MustacheBoxable" } 0642 // func Box(x: AnyObject?) -> String { return "AnyObject" } 0643 // 0644 // // error: ambiguous use of 'Box' 0645 // Box(Thing()) 0646 // 0647 // Maybe if we turn the func Box(x: MustacheBoxable?) into a generic one? Well, 0648 // it does not make the job either: 0649 // 0650 // protocol MustacheBoxable {} 0651 // class Thing: MustacheBoxable {} 0652 // 0653 // func Box<T: MustacheBoxable>(x: T?) -> String { return "MustacheBoxable" } 0654 // func Box(x: AnyObject?) -> String { return "AnyObject" } 0655 // 0656 // // Wrong: uses the AnyObject variant 0657 // Box(Thing()) 0658 // 0659 // // Error: cannot find an overload for 'Box' that accepts an argument list of type '(MustacheBoxable)' 0660 // Box(Thing() as MustacheBoxable) 0661 // 0662 // // Error: Crash the compiler 0663 // Box(Thing() as MustacheBoxable?) 0664 // 0665 // And if we turn the func Box(x: AnyObject) into a generic one? Well, it gets 0666 // better: 0667 // 0668 // protocol MustacheBoxable {} 0669 // class Thing: MustacheBoxable {} 0670 // 0671 // func Box(x: MustacheBoxable?) -> String { return "MustacheBoxable" } 0672 // func Box<T:AnyObject>(object: T?) -> String { return "AnyObject" } 0673 // 0674 // // OK: uses the MustacheBox variant 0675 // Box(Thing()) 0676 // 0677 // // OK: uses the MustacheBox variant 0678 // Box(Thing() as MustacheBoxable) 0679 // 0680 // // OK: uses the MustacheBox variant 0681 // Box(Thing() as MustacheBoxable?) 0682 // 0683 // // OK: uses the AnyObject variant 0684 // Box(Thing() as AnyObject) 0685 // 0686 // // OK: uses the AnyObject variant 0687 // Box(Thing() as AnyObject?) 0688 // 0689 // This looks OK, doesn't it? Well, it's not satisfying yet. 0690 // 0691 // According to http://airspeedvelocity.net/2015/03/26/protocols-and-generics-2/ 0692 // there are reasons for preferring func Box<T: MustacheBoxable>(x: T?) over 0693 // func Box(x: MustacheBoxable?). The example above have shown that the boxing 0694 // of AnyObject with an overloaded version of Box() would make this choice for 0695 // us. 0696 // 0697 // It's better not to make any choice right now, until we have a better 0698 // knowledge of Swift performances and optimization, and of the way Swift 0699 // resolves overloaded functions. 0700 // 0701 // So let's avoid having any Box(AnyObject?) variant in the public API, and 0702 // let's expose the BoxAnyObject(object: AnyObject?) instead. 0703 0704 // IMPLEMENTATION NOTE 2 0705 // 0706 // BoxAnyObject has been made private. Now users get a compiler error when they 0707 // try to box AnyObject. 0708 // 0709 // Reasons for this removal from the public API: 0710 // 0711 // - Users will try Box() first, which will fail. Since they may not know 0712 // anything BoxAnyObject, BoxAnyObject is of little value anyway. 0713 // - BoxAnyObject is error-prone, since it accepts anything and fails at 0714 // runtime. 0715 // 0716 // It still exists because we need it to box Foundation collections like 0717 // NSArray, NSSet, NSDictionary. 0718 0719 /** 0720 `AnyObject` can feed Mustache templates. 0721 0722 Yet, due to constraints in the Swift language, there is no `Box(AnyObject)` 0723 function. Instead, you use `BoxAnyObject`: 0724 0725 let set = NSSet(object: "Mario") 0726 let object: AnyObject = set.anyObject() 0727 let box = BoxAnyObject(object) 0728 box.value as String // "Mario" 0729 0730 The object is tested at runtime whether it adopts the `MustacheBoxable` 0731 protocol. In this case, this function behaves just like `Box(MustacheBoxable)`. 0732 0733 Otherwise, GRMustache logs a warning, and returns the empty box. 0734 0735 - parameter object: An object. 0736 - returns: A MustacheBox that wraps *object*. 0737 */ 0738 private func BoxAnyObject(object: AnyObject?) -> MustacheBox { 0739 if let boxable = object as? MustacheBoxable { 0740 return boxable.mustacheBox 0741 } else if let object: AnyObject = object { 0742 0743 // IMPLEMENTATION NOTE 0744 // 0745 // In the example below, the Thing class can not be turned into any 0746 // relevant MustacheBox. 0747 // 0748 // Yet we can not prevent the user from trying to box it, because the 0749 // Thing class adopts the AnyObject protocol, just as all Swift classes. 0750 // 0751 // class Thing { } 0752 // 0753 // // Compilation error (OK): cannot find an overload for 'Box' that accepts an argument list of type '(Thing)' 0754 // Box(Thing()) 0755 // 0756 // // Runtime warning (Not OK but unavoidable): value `Thing` is not NSObject and does not conform to MustacheBoxable: it is discarded. 0757 // BoxAnyObject(Thing()) 0758 // 0759 // // Foundation collections can also contain unsupported classes: 0760 // let array = NSArray(object: Thing()) 0761 // 0762 // // Runtime warning (Not OK but unavoidable): value `Thing` is not NSObject and does not conform to MustacheBoxable: it is discarded. 0763 // Box(array) 0764 // 0765 // // Compilation error (OK): cannot find an overload for 'Box' that accepts an argument list of type '(AnyObject)' 0766 // Box(array[0]) 0767 // 0768 // // Runtime warning (Not OK but unavoidable): value `Thing` is not NSObject and does not conform to MustacheBoxable: it is discarded. 0769 // BoxAnyObject(array[0]) 0770 0771 print("Mustache.BoxAnyObject(): value `\(object)` is does not conform to MustacheBoxable: it is discarded.") 0772 return Box() 0773 } else { 0774 return Box() 0775 } 0776 } 0777 0778 0779 // ============================================================================= 0780 // MARK: - Boxing of Collections 0781 0782 0783 // IMPLEMENTATION NOTE 0784 // 0785 // We don't provide any boxing function for `SequenceType`, because this type 0786 // makes no requirement on conforming types regarding whether they will be 0787 // destructively "consumed" by iteration (as stated by documentation). 0788 // 0789 // Now we need to consume a sequence several times: 0790 // 0791 // - for converting it to an array for the arrayValue property. 0792 // - for consuming the first element to know if the sequence is empty or not. 0793 // - for rendering it. 0794 // 0795 // So we don't support boxing of sequences. 0796 0797 // Support for all collections 0798 extension CollectionType { 0799 0800 /** 0801 Concatenates the rendering of the collection items. 0802 0803 There are two tricks when rendering collections: 0804 0805 1. Items can render as Text or HTML, and our collection should render with 0806 the same type. It is an error to mix content types. 0807 0808 2. We have to tell items that they are rendered as an enumeration item. 0809 This allows collections to avoid enumerating their items when they are 0810 part of another collections: 0811 0812 {{# arrays }} // Each array renders as an enumeration item, and has itself enter the context stack. 0813 {{#.}} // Each array renders "normally", and enumerates its items 0814 ... 0815 {{/.}} 0816 {{/ arrays }} 0817 0818 - parameter info: A RenderingInfo 0819 - parameter box: A closure that turns collection items into a MustacheBox. 0820 It makes us able to provide a single implementation 0821 whatever the type of the collection items. 0822 - returns: A Rendering 0823 */ 0824 private func renderItems
EachFilter.swift:49 position["@index"] = Box(boxable: index)EachFilter.swift:50 position["@indexPlusOne"] = Box(boxable: index + 1)EachFilter.swift:51 position["@indexIsEven"] = Box(boxable: index % 2 == 0)EachFilter.swift:52 position["@first"] = Box(boxable: index == 0)EachFilter.swift:53 position["@last"] = Box(boxable: (index == count - 1))EachFilter.swift:54 position["@key"] = Box(boxable: element.key)EachFilter.swift:55 info.context = info.context.extendedContext(Box(boxable: position))EachFilter.swift:60 return Box(boxable: transformedBoxes)EachFilter.swift:82 position["@index"] = Box(boxable: index)EachFilter.swift:83 position["@indexPlusOne"] = Box(boxable: index + 1)EachFilter.swift:84 position["@indexIsEven"] = Box(boxable: index % 2 == 0)EachFilter.swift:85 position["@first"] = Box(boxable: index == 0)EachFilter.swift:86 position["@last"] = Box(boxable: (index == count - 1))EachFilter.swift:87 info.context = info.context.extendedContext(Box(boxable: position))EachFilter.swift:92 return Box(boxable: transformedBoxes)ZipFilter.swift:87 return Box(boxable: renderFunctions.map(Box))Box.swift:491 return a.mustacheBoxWithSetValue(a, box: { Box(boxable: $0) })Box.swift:511 return a.mustacheBoxWithArrayValue(a, box: { Box(boxable: $0) })Box.swift:579 dictionaryValue[k] = Box(boxable: v)Box.swift:1049 return set.mustacheBoxWithSetValue(set, box: { Box(boxable: $0) })Box.swift:1100 return array.mustacheBoxWithArrayValue(array, box: { Box(boxable: $0) })Box.swift:1151 return array.mustacheBoxWithArrayValue(array, box: { Box(boxable: $0) })Box.swift:1216 boxDictionary[item.key] = Box(boxable: item.value)Box.swift:1221 return Box(boxable: dictionary[key])Context.swift:87 self.init(type: .Root, registeredKeysContext: Context(Box(boxable: [key: box])))Context.swift:116 let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(boxable: [key: box]))(info: RenderingInfo, @noescape box: (Generator.Element) -> MustacheBox) throws -> Rendering { 0825 // Prepare the rendering. We don't known the contentType yet: it depends on items 0826 var info = info 0827 var buffer = "" 0828 var contentType: ContentType? = nil 0829 0830 // Tell items they are rendered as an enumeration item. 0831 // 0832 // Some values don't render the same whenever they render as an 0833 // enumeration item, or alone: {{# values }}...{{/ values }} vs. 0834 // {{# value }}...{{/ value }}. 0835 // 0836 // This is the case of Int, UInt, Double, Bool: they enter the context 0837 // stack when used in an iteration, and do not enter the context stack 0838 // when used as a boolean. 0839 // 0840 // This is also the case of collections: they enter the context stack 0841 // when used as an item of a collection, and enumerate their items when 0842 // used as a collection. 0843 0844 info.enumerationItem = true 0845 0846 for item in self { 0847 let boxRendering = try box(item).render(info: info) 0848 if contentType == nil 0849 { 0850 // First item: now we know our contentType 0851 contentType = boxRendering.contentType 0852 buffer += boxRendering.string 0853 } 0854 else if contentType == boxRendering.contentType 0855 { 0856 // Consistent content type: keep on buffering. 0857 buffer += boxRendering.string 0858 } 0859 else 0860 { 0861 // Inconsistent content type: this is an error. How are we 0862 // supposed to mix Text and HTML? 0863 throw MustacheError(kind: .RenderError, message: "Content type mismatch") 0864 } 0865 } 0866 0867 if let contentType = contentType { 0868 // {{ collection }} 0869 // {{# collection }}...{{/ collection }} 0870 // 0871 // We know our contentType, hence the collection is not empty and 0872 // we render our buffer. 0873 return Rendering(buffer, contentType) 0874 } else { 0875 // {{ collection }} 0876 // 0877 // We don't know our contentType, hence the collection is empty. 0878 // 0879 // Now this code is executed. This means that the collection is 0880 // rendered, despite its emptiness. 0881 // 0882 // We are not rendering a regular {{# section }} tag, because empty 0883 // collections have a false boolValue, and RenderingEngine would prevent 0884 // us to render. 0885 // 0886 // We are not rendering an inverted {{^ section }} tag, because 0887 // RenderingEngine takes care of the rendering of inverted sections. 0888 // 0889 // So we are rendering a {{ variable }} tag. As en empty collection, we 0890 // must return an empty rendering. 0891 // 0892 // Renderings have a content type. In order to render an empty 0893 // rendering that has the contentType of the tag, let's use the 0894 // `render` method of the tag. 0895 return try info.tag.render(info.context) 0896 } 0897 } 0898 } 0899 0900 // Support for Set 0901 extension CollectionType where Index.Distance == Int { 0902 /** 0903 This function returns a MustacheBox that wraps a set-like collection. 0904 0905 The returned box can be queried for the following keys: 0906 0907 - `first`: the first object in the collection 0908 - `count`: number of elements in the collection 0909 0910 - parameter value: the value of the returned box. 0911 - parameter box: A closure that turns collection items into a MustacheBox. 0912 It makes us able to provide a single implementation 0913 whatever the type of the collection items. 0914 - returns: A MustacheBox that wraps the collection. 0915 */ 0916 private func mustacheBoxWithSetValue
Box.swift:942 return try self.renderItems(info, box: box)Box.swift:998 return try self.renderItems(info, box: box)(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox { 0917 return MustacheBox( 0918 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })), 0919 value: value, 0920 boolValue: !isEmpty, 0921 keyedSubscript: { (key) in 0922 switch key { 0923 case "first": // C: CollectionType 0924 if let first = self.first { 0925 return box(first) 0926 } else { 0927 return Box() 0928 } 0929 case "count": // C.Index.Distance == Int 0930 return Box(value: self.count) 0931 default: 0932 return Box() 0933 } 0934 }, 0935 render: { (info: RenderingInfo) in 0936 if info.enumerationItem { 0937 // {{# collections }}...{{/ collections }} 0938 return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithSetValue(value, box: box))) 0939 } else { 0940 // {{ collection }} 0941 // {{# collection }}...{{/ collection }} 0942 return try self.renderItems(info, box: box) 0943 } 0944 } 0945 ) 0946 } 0947 } 0948 0949 // Support for Array 0950 extension CollectionType where Index.Distance == Int, Index: BidirectionalIndexType { 0951 /** 0952 This function returns a MustacheBox that wraps an array-like collection. 0953 0954 The returned box can be queried for the following keys: 0955 0956 - `first`: the first object in the collection 0957 - `count`: number of elements in the collection 0958 - `last`: the last object in the collection 0959 0960 - parameter value: the value of the returned box. 0961 - parameter box: A closure that turns collection items into a MustacheBox. 0962 It makes us able to provide a single implementation 0963 whatever the type of the collection items. 0964 - returns: A MustacheBox that wraps the collection. 0965 */ 0966 private func mustacheBoxWithArrayValue
Box.swift:491 return a.mustacheBoxWithSetValue(a, box: { Box(boxable: $0) })Box.swift:938 return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithSetValue(value, box: box)))Box.swift:1049 return set.mustacheBoxWithSetValue(set, box: { Box(boxable: $0) })(value: Any?, box: (Generator.Element) -> MustacheBox) -> MustacheBox { 0967 return MustacheBox( 0968 converter: MustacheBox.Converter(arrayValue: self.map({ box($0) })), 0969 value: value, 0970 boolValue: !isEmpty, 0971 keyedSubscript: { (key) in 0972 switch key { 0973 case "first": // C: CollectionType 0974 if let first = self.first { 0975 return box(first) 0976 } else { 0977 return Box() 0978 } 0979 case "last": // C.Index: BidirectionalIndexType 0980 if let last = self.last { 0981 return box(last) 0982 } else { 0983 return Box() 0984 } 0985 case "count": // C.Index.Distance == Int 0986 return Box(value: self.count) 0987 default: 0988 return Box() 0989 } 0990 }, 0991 render: { (info: RenderingInfo) in 0992 if info.enumerationItem { 0993 // {{# collections }}...{{/ collections }} 0994 return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithArrayValue(value, box: box))) 0995 } else { 0996 // {{ collection }} 0997 // {{# collection }}...{{/ collection }} 0998 return try self.renderItems(info, box: box) 0999 } 1000 } 1001 ) 1002 } 1003 } 1004 1005 /** 1006 Sets of `MustacheBoxable` can feed Mustache templates. 1007 1008 let set:Set<Int> = [1,2,3] 1009 1010 // Renders "132", or "231", etc. 1011 let template = try! Template(string: "{{#set}}{{.}}{{/set}}") 1012 try! template.render(Box(["set": Box(set)])) 1013 1014 1015 ### Rendering 1016 1017 - `{{set}}` renders the concatenation of the set items. 1018 1019 - `{{#set}}...{{/set}}` renders as many times as there are items in `set`, 1020 pushing each item on its turn on the top of the context stack. 1021 1022 - `{{^set}}...{{/set}}` renders if and only if `set` is empty. 1023 1024 1025 ### Keys exposed to templates 1026 1027 A set can be queried for the following keys: 1028 1029 - `count`: number of elements in the set 1030 - `first`: the first object in the set 1031 1032 Because 0 (zero) is falsey, `{{#set.count}}...{{/set.count}}` renders once, if 1033 and only if `set` is not empty. 1034 1035 1036 ### Unwrapping from MustacheBox 1037 1038 Whenever you want to extract a collection of a MustacheBox, use the `arrayValue` 1039 property: it returns an Array of MustacheBox, whatever the actual 1040 type of the raw boxed value (Array, Set, NSArray, NSSet, ...). 1041 1042 1043 - parameter array: An array of boxable values. 1044 1045 - returns: A MustacheBox that wraps *array*. 1046 */ 1047 public func Box<C: CollectionType where C.Generator.Element: MustacheBoxable, C.Index.Distance == Int>(set set: C?) -> MustacheBox { 1048 if let set = set { 1049 return set.mustacheBoxWithSetValue(set, box: { Box(boxable: $0) }) 1050 } else { 1051 return Box() 1052 } 1053 } 1054 1055 /** 1056 Arrays of `MustacheBoxable` can feed Mustache templates. 1057 1058 let array = [1,2,3] 1059 1060 // Renders "123" 1061 let template = try! Template(string: "{{#array}}{{.}}{{/array}}") 1062 try! template.render(Box(["array": Box(array)])) 1063 1064 1065 ### Rendering 1066 1067 - `{{array}}` renders the concatenation of the array items. 1068 1069 - `{{#array}}...{{/array}}` renders as many times as there are items in `array`, 1070 pushing each item on its turn on the top of the context stack. 1071 1072 - `{{^array}}...{{/array}}` renders if and only if `array` is empty. 1073 1074 1075 ### Keys exposed to templates 1076 1077 An array can be queried for the following keys: 1078 1079 - `count`: number of elements in the array 1080 - `first`: the first object in the array 1081 - `last`: the last object in the array 1082 1083 Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders once, 1084 if and only if `array` is not empty. 1085 1086 1087 ### Unwrapping from MustacheBox 1088 1089 Whenever you want to extract a collection of a MustacheBox, use the `arrayValue` 1090 property: it returns an Array of MustacheBox, whatever the actual 1091 type of the raw boxed value (Array, Set, NSArray, NSSet, ...). 1092 1093 1094 - parameter array: An array of boxable values. 1095 1096 - returns: A MustacheBox that wraps *array*. 1097 */ 1098 public func Box<C: CollectionType where C.Generator.Element: MustacheBoxable, C.Index: BidirectionalIndexType, C.Index.Distance == Int>(array array: C?) -> MustacheBox { 1099 if let array = array { 1100 return array.mustacheBoxWithArrayValue(array, box: { Box(boxable: $0) }) 1101 } else { 1102 return Box() 1103 } 1104 } 1105 1106 /** 1107 Arrays of `MustacheBoxable?` can feed Mustache templates. 1108 1109 let array = [1,2,nil] 1110 1111 // Renders "<1><2><>" 1112 let template = try! Template(string: "{{#array}}<{{.}}>{{/array}}") 1113 try! template.render(Box(["array": Box(array)])) 1114 1115 1116 ### Rendering 1117 1118 - `{{array}}` renders the concatenation of the array items. 1119 1120 - `{{#array}}...{{/array}}` renders as many times as there are items in `array`, 1121 pushing each item on its turn on the top of the context stack. 1122 1123 - `{{^array}}...{{/array}}` renders if and only if `array` is empty. 1124 1125 1126 ### Keys exposed to templates 1127 1128 An array can be queried for the following keys: 1129 1130 - `count`: number of elements in the array 1131 - `first`: the first object in the array 1132 - `last`: the last object in the array 1133 1134 Because 0 (zero) is falsey, `{{#array.count}}...{{/array.count}}` renders once, 1135 if and only if `array` is not empty. 1136 1137 1138 ### Unwrapping from MustacheBox 1139 1140 Whenever you want to extract a collection of a MustacheBox, use the `arrayValue` 1141 property: it returns an Array of MustacheBox, whatever the actual 1142 type of the raw boxed value (Array, Set, NSArray, NSSet, ...). 1143 1144 1145 - parameter array: An array of optional boxable values. 1146 1147 - returns: A MustacheBox that wraps *array*. 1148 */ 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 { 1150 if let array = array { 1151 return array.mustacheBoxWithArrayValue(array, box: { Box(boxable: $0) }) 1152 } else { 1153 return Box() 1154 } 1155 } 1156 1157 1158 // ============================================================================= 1159 // MARK: - Boxing of Dictionaries 1160 1161 1162 /** 1163 A dictionary of values that conform to the `MustacheBoxable` protocol can feed 1164 Mustache templates. It behaves exactly like Objective-C `NSDictionary`. 1165 1166 let dictionary: [String: String] = [ 1167 "firstName": "Freddy", 1168 "lastName": "Mercury"] 1169 1170 // Renders "Freddy Mercury" 1171 let template = try! Template(string: "{{firstName}} {{lastName}}") 1172 let rendering = try! template.render(Box(dictionary)) 1173 1174 1175 ### Rendering 1176 1177 - `{{dictionary}}` renders the built-in Swift String Interpolation of the 1178 dictionary. 1179 1180 - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the 1181 context stack, and renders the section once. 1182 1183 - `{{^dictionary}}...{{/dictionary}}` does not render. 1184 1185 1186 In order to iterate over the key/value pairs of a dictionary, use the `each` 1187 filter from the Standard Library: 1188 1189 // Register StandardLibrary.each for the key "each": 1190 let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>") 1191 template.registerInBaseContext("each", Box(StandardLibrary.each)) 1192 1193 // Renders "<firstName:Freddy, lastName:Mercury,>" 1194 let dictionary: [String: String] = ["firstName": "Freddy", "lastName": "Mercury"] 1195 let rendering = try! template.render(Box(["dictionary": dictionary])) 1196 1197 1198 ### Unwrapping from MustacheBox 1199 1200 Whenever you want to extract a dictionary of a MustacheBox, use the 1201 `dictionaryValue` property: it reliably returns an `[String: MustacheBox]` 1202 dictionary, whatever the actual type of the raw boxed value. 1203 1204 1205 - parameter dictionary: A dictionary of values that conform to the 1206 `MustacheBoxable` protocol. 1207 1208 - returns: A MustacheBox that wraps *dictionary*. 1209 */ 1210 public func Box<T: MustacheBoxable>(dictionary dictionary: [String: T]?) -> MustacheBox { 1211 if let dictionary = dictionary { 1212 return MustacheBox( 1213 converter: MustacheBox.Converter( 1214 dictionaryValue: dictionary.reduce([String: MustacheBox](), combine: { (b, item: (key: String, value: T)) in 1215 var boxDictionary = b 1216 boxDictionary[item.key] = Box(boxable: item.value) 1217 return boxDictionary 1218 })), 1219 value: dictionary, 1220 keyedSubscript: { (key: String) in 1221 return Box(boxable: dictionary[key]) 1222 }) 1223 } else { 1224 return Box() 1225 } 1226 } 1227 1228 /** 1229 A dictionary of optional values that conform to the `MustacheBoxable` protocol 1230 can feed Mustache templates. It behaves exactly like Objective-C `NSDictionary`. 1231 1232 let dictionary: [String: String?] = [ 1233 "firstName": nil, 1234 "lastName": "Zappa"] 1235 1236 // Renders " Zappa" 1237 let template = try! Template(string: "{{firstName}} {{lastName}}") 1238 let rendering = try! template.render(Box(dictionary)) 1239 1240 1241 ### Rendering 1242 1243 - `{{dictionary}}` renders the built-in Swift String Interpolation of the 1244 dictionary. 1245 1246 - `{{#dictionary}}...{{/dictionary}}` pushes the dictionary on the top of the 1247 context stack, and renders the section once. 1248 1249 - `{{^dictionary}}...{{/dictionary}}` does not render. 1250 1251 1252 In order to iterate over the key/value pairs of a dictionary, use the `each` 1253 filter from the Standard Library: 1254 1255 // Register StandardLibrary.each for the key "each": 1256 let template = try! Template(string: "<{{# each(dictionary) }}{{@key}}:{{.}}, {{/}}>") 1257 template.registerInBaseContext("each", Box(StandardLibrary.each)) 1258 1259 // Renders "<firstName:Freddy, lastName:Mercury,>" 1260 let dictionary: [String: String?] = ["firstName": "Freddy", "lastName": "Mercury"] 1261 let rendering = try! template.render(Box(["dictionary": dictionary])) 1262 1263 1264 ### Unwrapping from MustacheBox 1265 1266 Whenever you want to extract a dictionary of a MustacheBox, use the 1267 `dictionaryValue` property: it reliably returns an `[String: MustacheBox]` 1268 dictionary, whatever the actual type of the raw boxed value. 1269 1270 1271 - parameter dictionary: A dictionary of optional values that conform to the 1272 `MustacheBoxable` protocol. 1273 1274 - returns: A MustacheBox that wraps *dictionary*. 1275 */ 1276 public func Box<T: MustacheBoxable>(optionalDictionary dictionary: [String: T?]?) -> MustacheBox { 1277 if let dictionary = dictionary { 1278 return MustacheBox( 1279 converter: MustacheBox.Converter( 1280 dictionaryValue: dictionary.reduce([String: MustacheBox](), combine: { (b, item: (key: String, value: T?)) in 1281 var boxDictionary = b 1282 boxDictionary[item.key] = Box(value: item.value) 1283 return boxDictionary 1284 })), 1285 value: dictionary, 1286 keyedSubscript: { (key: String) in 1287 if let value = dictionary[key] { 1288 return Box(value: value) 1289 } else { 1290 return Box() 1291 } 1292 }) 1293 } else { 1294 return Box() 1295 } 1296 } 1297 1298 // ============================================================================= 1299 // MARK: - Boxing of Core Mustache functions 1300 1301 /** 1302 A function that wraps a `FilterFunction` into a `MustacheBox` so that it can 1303 feed template. 1304 1305 let square: FilterFunction = Filter { (x: Int?) in 1306 return Box(x! * x!) 1307 } 1308 1309 let template = try! Template(string: "{{ square(x) }}") 1310 template.registerInBaseContext("square", Box(square)) 1311 1312 // Renders "100" 1313 try! template.render(Box(["x": 10])) 1314 1315 - parameter filter: A FilterFunction. 1316 - returns: A MustacheBox that wraps *filter*. 1317 1318 See also: 1319 1320 - FilterFunction 1321 */ 1322 public func Box
Box.swift:511 return a.mustacheBoxWithArrayValue(a, box: { Box(boxable: $0) })Box.swift:994 return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithArrayValue(value, box: box)))Box.swift:1100 return array.mustacheBoxWithArrayValue(array, box: { Box(boxable: $0) })Box.swift:1151 return array.mustacheBoxWithArrayValue(array, box: { Box(boxable: $0) })(filter filter: FilterFunction) -> MustacheBox { 1323 return MustacheBox(filter: filter) 1324 } 1325 1326 /** 1327 A function that wraps a `RenderFunction` into a `MustacheBox` so that it can 1328 feed template. 1329 1330 let foo: RenderFunction = { (_) in Rendering("foo") } 1331 1332 // Renders "foo" 1333 let template = try! Template(string: "{{ foo }}") 1334 try! template.render(Box(["foo": Box(foo)])) 1335 1336 - parameter render: A RenderFunction. 1337 - returns: A MustacheBox that wraps *render*. 1338 1339 See also: 1340 1341 - RenderFunction 1342 */ 1343 public func Box
CoreFunctions.swift:224 return Box(filter: partialFilter(filter, arguments: arguments))(render render: RenderFunction) -> MustacheBox { 1344 return MustacheBox(render: render) 1345 } 1346 1347 /** 1348 A function that wraps a `WillRenderFunction` into a `MustacheBox` so that it can 1349 feed template. 1350 1351 let logTags: WillRenderFunction = { (tag: Tag, box: MustacheBox) in 1352 print("\(tag) will render \(box.value!)") 1353 return box 1354 } 1355 1356 // By entering the base context of the template, the logTags function 1357 // will be notified of all tags. 1358 let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}") 1359 template.extendBaseContext(Box(logTags)) 1360 1361 // Prints: 1362 // {{# user }} at line 1 will render { firstName = Errol; lastName = Flynn; } 1363 // {{ firstName }} at line 1 will render Errol 1364 // {{ lastName }} at line 1 will render Flynn 1365 let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]] 1366 try! template.render(Box(data)) 1367 1368 - parameter willRender: A WillRenderFunction 1369 - returns: A MustacheBox that wraps *willRender*. 1370 1371 See also: 1372 1373 - WillRenderFunction 1374 */ 1375 public func Box(willRender willRender: WillRenderFunction) -> MustacheBox { 1376 return MustacheBox(willRender: willRender) 1377 } 1378 1379 /** 1380 A function that wraps a `DidRenderFunction` into a `MustacheBox` so that it can 1381 feed template. 1382 1383 let logRenderings: DidRenderFunction = { (tag: Tag, box: MustacheBox, string: String?) in 1384 print("\(tag) did render \(box.value!) as `\(string!)`") 1385 } 1386 1387 // By entering the base context of the template, the logRenderings function 1388 // will be notified of all tags. 1389 let template = try! Template(string: "{{# user }}{{ firstName }} {{ lastName }}{{/ user }}") 1390 template.extendBaseContext(Box(logRenderings)) 1391 1392 // Renders "Errol Flynn" 1393 // 1394 // Prints: 1395 // {{ firstName }} at line 1 did render Errol as `Errol` 1396 // {{ lastName }} at line 1 did render Flynn as `Flynn` 1397 // {{# user }} at line 1 did render { firstName = Errol; lastName = Flynn; } as `Errol Flynn` 1398 let data = ["user": ["firstName": "Errol", "lastName": "Flynn"]] 1399 try! template.render(Box(data)) 1400 1401 - parameter didRender: A DidRenderFunction/ 1402 - returns: A MustacheBox that wraps *didRender*. 1403 1404 See also: 1405 1406 - DidRenderFunction 1407 */ 1408 public func Box(didRender didRender: DidRenderFunction) -> MustacheBox { 1409 return MustacheBox(didRender: didRender) 1410 } 1411 1412 ///** 1413 //The empty box, the box that represents missing values. 1414 //*/ 1415 //public func Box() -> MustacheBox { 1416 // return EmptyBox 1417 //} 1418 // 1419 //private let EmptyBox = MustacheBox() 1420 1421 // ============================================================================= 1422 // MARK: - Boxing of multi-facetted values 1423 1424 1425 /** 1426 This function is the most low-level function that lets you build MustacheBox 1427 for feeding templates. 1428 1429 It is suited for building "advanced" boxes. There are other simpler versions of 1430 the `Box` function that may well better suit your need: you should check them. 1431 1432 It can take up to seven parameters, all optional, that define how the box 1433 interacts with the Mustache engine: 1434 1435 - `boolValue`: an optional boolean value for the Box. 1436 - `value`: an optional boxed value 1437 - `keyedSubscript`: an optional KeyedSubscriptFunction 1438 - `filter`: an optional FilterFunction 1439 - `render`: an optional RenderFunction 1440 - `willRender`: an optional WillRenderFunction 1441 - `didRender`: an optional DidRenderFunction 1442 1443 1444 To illustrate the usage of all those parameters, let's look at how the 1445 `{{f(a)}}` tag is rendered. 1446 1447 First the `a` and `f` expressions are evaluated. The Mustache engine looks in 1448 the context stack for boxes whose *keyedSubscript* return non-empty boxes for 1449 the keys "a" and "f". Let's call them aBox and fBox. 1450 1451 Then the *filter* of the fBox is evaluated with aBox as an argument. It is 1452 likely that the result depends on the *value* of the aBox: it is the resultBox. 1453 1454 Then the Mustache engine is ready to render resultBox. It looks in the context 1455 stack for boxes whose *willRender* function is defined. Those willRender 1456 functions have the opportunity to process the resultBox, and eventually provide 1457 the box that will be actually rendered: the renderedBox. 1458 1459 The renderedBox has a *render* function: it is evaluated by the Mustache engine 1460 which appends its result to the final rendering. 1461 1462 Finally the Mustache engine looks in the context stack for boxes whose 1463 *didRender* function is defined, and call them. 1464 1465 1466 ### boolValue 1467 1468 The optional `boolValue` parameter tells whether the Box should trigger or 1469 prevent the rendering of regular `{{#section}}...{{/}}` and inverted 1470 `{{^section}}...{{/}}` tags. The default value is true, unless the function is 1471 called without argument to build the empty box: `Box()`. 1472 1473 // Render "true", "false" 1474 let template = try! Template(string:"{{#.}}true{{/.}}{{^.}}false{{/.}}") 1475 template.render(Box(boolValue: true))! 1476 template.render(Box(boolValue: false))! 1477 1478 1479 ### value 1480 1481 The optional `value` parameter gives the boxed value. The value is used when the 1482 box is rendered (unless you provide a custom RenderFunction). 1483 1484 let aBox = Box(value: 1) 1485 1486 // Renders "1" 1487 let template = try! Template(string: "{{a}}") 1488 try! template.render(Box(["a": aBox])) 1489 1490 1491 ### keyedSubscript 1492 1493 The optional `keyedSubscript` parameter is a `KeyedSubscriptFunction` that lets 1494 the Mustache engine extract keys out of the box. For example, the `{{a}}` tag 1495 would call the subscript function with `"a"` as an argument, and render the 1496 returned box. 1497 1498 The default value is nil, which means that no key can be extracted. 1499 1500 See `KeyedSubscriptFunction` for a full discussion of this type. 1501 1502 let box = Box(keyedSubscript: { (key: String) in 1503 return Box("key:\(key)") 1504 }) 1505 1506 // Renders "key:a" 1507 let template = try! Template(string:"{{a}}") 1508 try! template.render(box) 1509 1510 1511 ### filter 1512 1513 The optional `filter` parameter is a `FilterFunction` that lets the Mustache 1514 engine evaluate filtered expression that involve the box. The default value is 1515 nil, which means that the box can not be used as a filter. 1516 1517 See `FilterFunction` for a full discussion of this type. 1518 1519 let box = Box(filter: Filter { (x: Int?, _) in 1520 return Box(x! * x!) 1521 }) 1522 1523 // Renders "100" 1524 let template = try! Template(string:"{{square(x)}}") 1525 try! template.render(Box(["square": box, "x": Box(10)])) 1526 1527 1528 ### render 1529 1530 The optional `render` parameter is a `RenderFunction` that is evaluated when the 1531 Box is rendered. 1532 1533 The default value is nil, which makes the box perform default Mustache 1534 rendering: 1535 1536 - `{{box}}` renders the built-in Swift String Interpolation of the value, 1537 HTML-escaped. 1538 1539 - `{{{box}}}` renders the built-in Swift String Interpolation of the value, 1540 not HTML-escaped. 1541 1542 - `{{#box}}...{{/box}}` does not render if `boolValue` is false. Otherwise, it 1543 pushes the box on the top of the context stack, and renders the section once. 1544 1545 - `{{^box}}...{{/box}}` renders once if `boolValue` is false. Otherwise, it 1546 does not render. 1547 1548 See `RenderFunction` for a full discussion of this type. 1549 1550 let box = Box(render: { (info: RenderingInfo) in 1551 return Rendering("foo") 1552 }) 1553 1554 // Renders "foo" 1555 let template = try! Template(string:"{{.}}") 1556 try! template.render(box) 1557 1558 1559 ### willRender, didRender 1560 1561 The optional `willRender` and `didRender` parameters are a `WillRenderFunction` 1562 and `DidRenderFunction` that are evaluated for all tags as long as the box is in 1563 the context stack. 1564 1565 See `WillRenderFunction` and `DidRenderFunction` for a full discussion of those 1566 types. 1567 1568 let box = Box(willRender: { (tag: Tag, box: MustacheBox) in 1569 return Box("baz") 1570 }) 1571 1572 // Renders "baz baz" 1573 let template = try! Template(string:"{{#.}}{{foo}} {{bar}}{{/.}}") 1574 try! template.render(box) 1575 1576 1577 ### Multi-facetted boxes 1578 1579 By mixing all those parameters, you can finely tune the behavior of a box. 1580 1581 GRMustache source code ships a few multi-facetted boxes, which may inspire you. 1582 See for example: 1583 1584 - NSFormatter.mustacheBox 1585 - HTMLEscape.mustacheBox 1586 - StandardLibrary.Localizer.mustacheBox 1587 1588 Let's give an example: 1589 1590 // A regular type: 1591 1592 struct Person { 1593 let firstName: String 1594 let lastName: String 1595 } 1596 1597 We want: 1598 1599 1. `{{person.firstName}}` and `{{person.lastName}}` should render the matching 1600 properties. 1601 2. `{{person}}` should render the concatenation of the first and last names. 1602 1603 We'll provide a `KeyedSubscriptFunction` to implement 1, and a `RenderFunction` 1604 to implement 2: 1605 1606 // Have Person conform to MustacheBoxable so that we can box people, and 1607 // render them: 1608 1609 extension Person : MustacheBoxable { 1610 1611 // MustacheBoxable protocol requires objects to implement this property 1612 // and return a MustacheBox: 1613 1614 var mustacheBox: MustacheBox { 1615 1616 // A person is a multi-facetted object: 1617 return Box( 1618 // It has a value: 1619 value: self, 1620 1621 // It lets Mustache extracts properties by name: 1622 keyedSubscript: { (key: String) -> MustacheBox in 1623 switch key { 1624 case "firstName": return Box(self.firstName) 1625 case "lastName": return Box(self.lastName) 1626 default: return Box() 1627 } 1628 }, 1629 1630 // It performs custom rendering: 1631 render: { (info: RenderingInfo) -> Rendering in 1632 switch info.tag.type { 1633 case .Variable: 1634 // {{ person }} 1635 return Rendering("\(self.firstName) \(self.lastName)") 1636 case .Section: 1637 // {{# person }}...{{/}} 1638 // 1639 // Perform the default rendering: push self on the top 1640 // of the context stack, and render the section: 1641 let context = info.context.extendedContext(Box(self)) 1642 return try info.tag.render(context) 1643 } 1644 } 1645 ) 1646 } 1647 } 1648 1649 // Renders "The person is Errol Flynn" 1650 let person = Person(firstName: "Errol", lastName: "Flynn") 1651 let template = try! Template(string: "{{# person }}The person is {{.}}{{/ person }}") 1652 try! template.render(Box(["person": person])) 1653 1654 - parameter value: An optional boxed value. 1655 - parameter boolValue: An optional boolean value for the Box. 1656 - parameter keyedSubscript: An optional `KeyedSubscriptFunction`. 1657 - parameter filter: An optional `FilterFunction`. 1658 - parameter render: An optional `RenderFunction`. 1659 - parameter willRender: An optional `WillRenderFunction`. 1660 - parameter didRender: An optional `DidRenderFunction`. 1661 - returns: A MustacheBox. 1662 */ 1663 public func Box
EachFilter.swift:58 return Box(render: customRenderFunction)EachFilter.swift:90 return Box(render: customRenderFunction)HTMLEscapeHelper.swift:55 return Box(render: { (info: RenderingInfo) -> Rendering inJavascriptEscapeHelper.swift:54 return Box(render: { (info: RenderingInfo) -> Rendering inZipFilter.swift:87 return Box(boxable: renderFunctions.map(Box))CoreFunctions.swift:271 return Box { (info: RenderingInfo) inCoreFunctions.swift:298 return Box { (info: RenderingInfo) inCoreFunctions.swift:345 return Box { (info: RenderingInfo) inCoreFunctions.swift:688 let context = info.context.extendedContext(Box(render: Lambda(lambda)))( 1664 value value: Any? = nil, 1665 boolValue: Bool? = nil, 1666 keyedSubscript: KeyedSubscriptFunction? = nil, 1667 filter: FilterFunction? = nil, 1668 render: RenderFunction? = nil, 1669 willRender: WillRenderFunction? = nil, 1670 didRender: DidRenderFunction? = nil) -> MustacheBox 1671 { 1672 1673 return MustacheBox( 1674 value: value, 1675 boolValue: boolValue, 1676 keyedSubscript: keyedSubscript, 1677 filter: filter, 1678 render: render, 1679 willRender: willRender, 1680 didRender: didRender) 1681 1682 }
Box.swift:186 return try info.tag.render(info.context.extendedContext(Box(boolValue: self)))Box.swift:243 return try info.tag.render(info.context.extendedContext(Box(value: self)))Box.swift:300 return try info.tag.render(info.context.extendedContext(Box(value: self)))Box.swift:357 return try info.tag.render(info.context.extendedContext(Box(value: self)))Box.swift:420 return Box(value: self.characters.count)Box.swift:422 return Box()Box.swift:487 return Box()Box.swift:507 return Box()Box.swift:582 return Box()Box.swift:590 dict[subscriptKey] ?? Box()Box.swift:611 return boxable?.mustacheBox ?? Box()Box.swift:772 return Box()Box.swift:774 return Box()Box.swift:927 return Box()Box.swift:930 return Box(value: self.count)Box.swift:932 return Box()Box.swift:977 return Box()Box.swift:983 return Box()Box.swift:986 return Box(value: self.count)Box.swift:988 return Box()Box.swift:1051 return Box()Box.swift:1102 return Box()Box.swift:1153 return Box()Box.swift:1224 return Box()Box.swift:1282 boxDictionary[item.key] = Box(value: item.value)Box.swift:1288 return Box(value: value)Box.swift:1290 return Box()Box.swift:1294 return Box()Context.swift:131 return Box()Context.swift:173 return Box()MustacheBox.swift:149 return keyedSubscript?(key: key) ?? Box()Template.swift:57 public func render(box: MustacheBox = Box()) throws -> String {