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 A Context represents a state of the Mustache "context stack". 0025 0026 The context stack grows and shrinks as the Mustache engine enters and leaves 0027 Mustache sections. 0028 0029 The top of the context stack is called the "current context". It is the value 0030 rendered by the `{{.}}` tag: 0031 0032 // Renders "Kitty, Pussy, Melba, " 0033 let template = try! Template(string: "{{#cats}}{{.}}, {{/cats}}") 0034 try! template.render(Box(["cats": ["Kitty", "Pussy", "Melba"]])) 0035 0036 Key lookup starts with the current context and digs down the stack until if 0037 finds a value: 0038 0039 // Renders "<child>, <parent>, " 0040 let template = try! Template(string: "{{#children}}<{{name}}>, {{/children}}") 0041 let data = [ 0042 "name": "parent", 0043 "children": [ 0044 ["name": "child"], 0045 [:] // a child without a name 0046 ] 0047 ] 0048 try! template.render(Box(data)) 0049 0050 See also: 0051 0052 - Configuration 0053 - TemplateRepository 0054 - RenderFunction 0055 */ 0056 final public class Context{ 0057 0058 // ========================================================================= 0059 // MARK: - Creating Contexts 0060 0061 /** 0062 Builds an empty Context. 0063 */ 0064 public convenience init
Configuration.swift:100 baseContext = Context()Configuration.swift:202 public var baseContext: ContextContext.swift:75 self.init(type: .Box(box: box, parent: Context()))Context.swift:87 self.init(type: .Root, registeredKeysContext: Context(Box(boxable: [key: box])))Context.swift:102 public func extendedContext(box: MustacheBox) -> Context {Context.swift:103 return Context(type: .Box(box: box, parent: self), registeredKeysContext: registeredKeysContext)Context.swift:115 public func contextWithRegisteredKey(key: String, box: MustacheBox) -> Context {Context.swift:116 let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(boxable: [key: box]))Context.swift:117 return Context(type: self.type, registeredKeysContext: registeredKeysContext)Context.swift:215 case Box(box: MustacheBox, parent: Context)Context.swift:216 case PartialOverride(partialOverride: TemplateASTNode.PartialOverride, parent: Context)Context.swift:219 private var registeredKeysContext: Context?Context.swift:263 private init(type: ContextType, registeredKeysContext: Context? = nil) {Context.swift:268 func extendedContext(partialOverride partialOverride: TemplateASTNode.PartialOverride) -> Context {Context.swift:269 return Context(type: .PartialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext)Context.swift:273 extension Context: CustomDebugStringConvertible {CoreFunctions.swift:753 public var context: ContextExpressionInvocation.swift:26 func invokeWithContext(context: Context) throws -> MustacheBox {ExpressionInvocation.swift:30 private func evaluate(context context: Context, expression: Expression) throws -> MustacheBox {RenderingEngine.swift:25 init(templateAST: TemplateAST, context: Context) {RenderingEngine.swift:41 private let baseContext: ContextRenderingEngine.swift:44 private func renderTemplateAST(templateAST: TemplateAST, inContext context: Context) throws {RenderingEngine.swift:82 private func renderNode(node: TemplateASTNode, inContext context: Context) throws {RenderingEngine.swift:127 private func renderTag(tag: LocatedTag, escapesHTML: Bool, inverted: Bool, expression: Expression, inContext context: Context) throws {RenderingEngine.swift:209 private func resolveBlock(block: TemplateASTNode.Block, inContext context: Context) -> TemplateASTNode.Block {Template.swift:80 public func render(context: Context) throws -> Rendering {Template.swift:118 public var baseContext: ContextTemplate.swift:207 init(repository: TemplateRepository, templateAST: TemplateAST, baseContext: Context) {SectionTag.swift:47 func render(context: Context) throws -> Rendering {Tag.swift:147 func render(context: Context) throws -> RenderingVariableTag.swift:46 func render(context: Context) throws -> Rendering {() { 0065 self.init(type: .Root) 0066 } 0067 0068 /** 0069 Builds a context that contains the provided box. 0070 0071 - parameter box: A box. 0072 - returns: A new context that contains *box*. 0073 */ 0074 public convenience init
Configuration.swift:100 baseContext = Context()Context.swift:75 self.init(type: .Box(box: box, parent: Context()))Context.swift:116 let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(boxable: [key: box]))(_ box: MustacheBox) { 0075 self.init(type: .Box(box: box, parent: Context())) 0076 } 0077 0078 /** 0079 Builds a context with a registered key. Registered keys are looked up first 0080 when evaluating Mustache tags. 0081 0082 - parameter key: An identifier. 0083 - parameter box: A box. 0084 - returns: A new context with *box* registered for *key*. 0085 */ 0086 public convenience init(registeredKey key: String, box: MustacheBox) { 0087 self.init(type: .Root, registeredKeysContext: Context(Box(boxable: [key: box]))) 0088 } 0089 0090 0091 // ========================================================================= 0092 // MARK: - Deriving New Contexts 0093 0094 /** 0095 Returns a new context with the provided box pushed at the top of the context 0096 stack. 0097 0098 - parameter box: A box. 0099 - returns: A new context with *box* pushed at the top of the stack. 0100 */ 0101 @warn_unused_result(message="Context.extendedContext returns a new Context.") 0102 public func extendedContext
Context.swift:87 self.init(type: .Root, registeredKeysContext: Context(Box(boxable: [key: box])))(box: MustacheBox) -> Context { 0103 return Context(type: .Box(box: box, parent: self), registeredKeysContext: registeredKeysContext) 0104 } 0105 0106 /** 0107 Returns a new context with the provided box at the top of the context stack. 0108 Registered keys are looked up first when evaluating Mustache tags. 0109 0110 - parameter key: An identifier. 0111 - parameter box: A box. 0112 - returns: A new context with *box* registered for *key*. 0113 */ 0114 @warn_unused_result(message="Context.contextWithRegisteredKey returns a new Context.") 0115 public func contextWithRegisteredKey
Configuration.swift:241 baseContext = baseContext.extendedContext(box)EachFilter.swift:55 info.context = info.context.extendedContext(Box(boxable: position))EachFilter.swift:87 info.context = info.context.extendedContext(Box(boxable: position))ZipFilter.swift:77 var context = zippedBoxes.reduce(info.context) { (context, box) in context.extendedContext(box) }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:938 return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithSetValue(value, box: box)))Box.swift:994 return try info.tag.render(info.context.extendedContext(self.mustacheBoxWithArrayValue(value, box: box)))Context.swift:116 let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(boxable: [key: box]))CoreFunctions.swift:688 let context = info.context.extendedContext(Box(render: Lambda(lambda)))MustacheBox.swift:491 let context = info.context.extendedContext(self)Template.swift:58 let rendering = try render(baseContext.extendedContext(box))Template.swift:136 baseContext = baseContext.extendedContext(box)(key: String, box: MustacheBox) -> Context { 0116 let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(boxable: [key: box])) 0117 return Context(type: self.type, registeredKeysContext: registeredKeysContext) 0118 } 0119 0120 0121 // ========================================================================= 0122 // MARK: - Fetching Values from the Context Stack 0123 0124 /** 0125 Returns the top box of the context stack, the one that would be rendered by 0126 the `{{.}}` tag. 0127 */ 0128 public var topBox
Configuration.swift:288 baseContext = baseContext.contextWithRegisteredKey(key, box: box)Template.swift:160 baseContext = baseContext.contextWithRegisteredKey(key, box: box): MustacheBox { 0129 switch type { 0130 case .Root: 0131 return Box() 0132 case .Box(box: let box, parent: _): 0133 return box 0134 case .PartialOverride(partialOverride: _, parent: let parent): 0135 return parent.topBox 0136 } 0137 } 0138 0139 /** 0140 Returns the boxed value stored in the context stack for the given key. 0141 0142 The following search pattern is used: 0143 0144 1. If the key is "registered", returns the registered box for that key. 0145 0146 2. Otherwise, searches the context stack for a box that has a non-empty 0147 box for the key (see `InspectFunction`). 0148 0149 3. If none of the above situations occurs, returns the empty box. 0150 0151 let data = ["name": "Groucho Marx"] 0152 let context = Context(Box(data)) 0153 0154 // "Groucho Marx" 0155 context.mustacheBoxForKey("name").value 0156 0157 If you want the value for a full Mustache expression such as `user.name` or 0158 `uppercase(user.name)`, use the `mustacheBoxForExpression` method. 0159 0160 - parameter key: A key. 0161 - returns: The MustacheBox for *key*. 0162 */ 0163 public func mustacheBoxForKey
Context.swift:135 return parent.topBoxExpressionInvocation.swift:35 return context.topBox(key: String) -> MustacheBox { 0164 if let registeredKeysContext = registeredKeysContext { 0165 let box = registeredKeysContext.mustacheBoxForKey(key) 0166 if !box.isEmpty { 0167 return box 0168 } 0169 } 0170 0171 switch type { 0172 case .Root: 0173 return Box() 0174 case .Box(box: let box, parent: let parent): 0175 let innerBox = box.mustacheBoxForKey(key) 0176 if innerBox.isEmpty { 0177 return parent.mustacheBoxForKey(key) 0178 } else { 0179 return innerBox 0180 } 0181 case .PartialOverride(partialOverride: _, parent: let parent): 0182 return parent.mustacheBoxForKey(key) 0183 } 0184 } 0185 0186 /** 0187 Evaluates a Mustache expression such as `name`, or `uppercase(user.name)`. 0188 0189 let data = ["person": ["name": "Albert Einstein"]] 0190 let context = Context(Box(data)) 0191 0192 // "Albert Einstein" 0193 try! context.mustacheBoxForExpression("person.name").value 0194 0195 - parameter string: The expression string. 0196 - parameter error: If there is a problem parsing or evaluating the 0197 expression, throws an error that describes the problem. 0198 0199 - returns: The value of the expression. 0200 */ 0201 public func mustacheBoxForExpression(string: String) throws -> MustacheBox { 0202 let parser = ExpressionParser() 0203 var empty = false 0204 let expression = try parser.parse(string, empty: &empty) 0205 let invocation = ExpressionInvocation(expression: expression) 0206 return try invocation.invokeWithContext(self) 0207 } 0208 0209 0210 // ========================================================================= 0211 // MARK: - Not public 0212 0213 private enum ContextType
Context.swift:165 let box = registeredKeysContext.mustacheBoxForKey(key)Context.swift:177 return parent.mustacheBoxForKey(key)Context.swift:182 return parent.mustacheBoxForKey(key)ExpressionInvocation.swift:40 return context.mustacheBoxForKey(identifier){ 0214 case Root
Context.swift:220 private let type: ContextTypeContext.swift:263 private init(type: ContextType, registeredKeysContext: Context? = nil) {0215 case Box
Context.swift:65 self.init(type: .Root)Context.swift:87 self.init(type: .Root, registeredKeysContext: Context(Box(boxable: [key: box])))Context.swift:130 case .Root:Context.swift:172 case .Root:Context.swift:224 case .Root:Context.swift:239 case .Root:Context.swift:254 case .Root:Context.swift:277 case .Root:(box: MustacheBox, parent: Context) 0216 case PartialOverride
Context.swift:75 self.init(type: .Box(box: box, parent: Context()))Context.swift:103 return Context(type: .Box(box: box, parent: self), registeredKeysContext: registeredKeysContext)Context.swift:132 case .Box(box: let box, parent: _):Context.swift:174 case .Box(box: let box, parent: let parent):Context.swift:226 case .Box(box: let box, parent: let parent):Context.swift:241 case .Box(box: let box, parent: let parent):Context.swift:256 case .Box(box: _, parent: let parent):Context.swift:279 case .Box(box: let box, parent: let parent):(partialOverride: TemplateASTNode.PartialOverride, parent: Context) 0217 } 0218 0219 private var registeredKeysContext
Context.swift:134 case .PartialOverride(partialOverride: _, parent: let parent):Context.swift:181 case .PartialOverride(partialOverride: _, parent: let parent):Context.swift:232 case .PartialOverride(partialOverride: _, parent: let parent):Context.swift:247 case .PartialOverride(partialOverride: _, parent: let parent):Context.swift:258 case .PartialOverride(partialOverride: let partialOverride, parent: let parent):Context.swift:269 return Context(type: .PartialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext)Context.swift:281 case .PartialOverride(partialOverride: _, parent: let parent):: Context? 0220 private let type
Context.swift:103 return Context(type: .Box(box: box, parent: self), registeredKeysContext: registeredKeysContext)Context.swift:116 let registeredKeysContext = (self.registeredKeysContext ?? Context()).extendedContext(Box(boxable: [key: box]))Context.swift:164 if let registeredKeysContext = registeredKeysContext {Context.swift:265 self.registeredKeysContext = registeredKeysContextContext.swift:269 return Context(type: .PartialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext): ContextType 0221 0222 var willRenderStack
Context.swift:117 return Context(type: self.type, registeredKeysContext: registeredKeysContext)Context.swift:129 switch type {Context.swift:171 switch type {Context.swift:223 switch type {Context.swift:238 switch type {Context.swift:253 switch type {Context.swift:264 self.type = typeContext.swift:276 switch type {: [WillRenderFunction] { 0223 switch type { 0224 case .Root: 0225 return [] 0226 case .Box(box: let box, parent: let parent): 0227 if let willRender = box.willRender { 0228 return [willRender] + parent.willRenderStack 0229 } else { 0230 return parent.willRenderStack 0231 } 0232 case .PartialOverride(partialOverride: _, parent: let parent): 0233 return parent.willRenderStack 0234 } 0235 } 0236 0237 var didRenderStack
Context.swift:228 return [willRender] + parent.willRenderStackContext.swift:230 return parent.willRenderStackContext.swift:233 return parent.willRenderStackRenderingEngine.swift:150 for willRender in context.willRenderStack {: [DidRenderFunction] { 0238 switch type { 0239 case .Root: 0240 return [] 0241 case .Box(box: let box, parent: let parent): 0242 if let didRender = box.didRender { 0243 return parent.didRenderStack + [didRender] 0244 } else { 0245 return parent.didRenderStack 0246 } 0247 case .PartialOverride(partialOverride: _, parent: let parent): 0248 return parent.didRenderStack 0249 } 0250 } 0251 0252 var partialOverrideStack
Context.swift:243 return parent.didRenderStack + [didRender]Context.swift:245 return parent.didRenderStackContext.swift:248 return parent.didRenderStackRenderingEngine.swift:180 for didRender in context.didRenderStack {RenderingEngine.swift:201 for didRender in context.didRenderStack {: [TemplateASTNode.PartialOverride] { 0253 switch type { 0254 case .Root: 0255 return [] 0256 case .Box(box: _, parent: let parent): 0257 return parent.partialOverrideStack 0258 case .PartialOverride(partialOverride: let partialOverride, parent: let parent): 0259 return [partialOverride] + parent.partialOverrideStack 0260 } 0261 } 0262 0263 private init
Context.swift:257 return parent.partialOverrideStackContext.swift:259 return [partialOverride] + parent.partialOverrideStackRenderingEngine.swift:217 return context.partialOverrideStack.reduce(block) { (block, partialOverride) in(type: ContextType, registeredKeysContext: Context? = nil) { 0264 self.type = type 0265 self.registeredKeysContext = registeredKeysContext 0266 } 0267 0268 func extendedContext
Context.swift:65 self.init(type: .Root)Context.swift:75 self.init(type: .Box(box: box, parent: Context()))Context.swift:87 self.init(type: .Root, registeredKeysContext: Context(Box(boxable: [key: box])))Context.swift:103 return Context(type: .Box(box: box, parent: self), registeredKeysContext: registeredKeysContext)Context.swift:117 return Context(type: self.type, registeredKeysContext: registeredKeysContext)Context.swift:269 return Context(type: .PartialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext)(partialOverride partialOverride: TemplateASTNode.PartialOverride) -> Context { 0269 return Context(type: .PartialOverride(partialOverride: partialOverride, parent: self), registeredKeysContext: registeredKeysContext) 0270 } 0271 } 0272 0273 extension Context: CustomDebugStringConvertible { 0274 /// A textual representation of `self`, suitable for debugging. 0275 public var debugDescription
RenderingEngine.swift:95 let context = context.extendedContext(partialOverride: partialOverride): String { 0276 switch type { 0277 case .Root: 0278 return "Context.Root" 0279 case .Box(box: let box, parent: let parent): 0280 return "Context.Box(\(box)):\(parent.debugDescription)" 0281 case .PartialOverride(partialOverride: _, parent: let parent): 0282 return "Context.PartialOverride:\(parent.debugDescription)" 0283 } 0284 } 0285 }
Context.swift:280 return "Context.Box(\(box)):\(parent.debugDescription)"Context.swift:282 return "Context.PartialOverride:\(parent.debugDescription)"