1overview: | 2 Section tags and End Section tags are used in combination to wrap a section 3 of the template for iteration 4 5 These tags' content MUST be a non-whitespace character sequence NOT 6 containing the current closing delimiter; each Section tag MUST be followed 7 by an End Section tag with the same content within the same section. 8 9 This tag's content names the data to replace the tag. Name resolution is as 10 follows: 11 1) Split the name on periods; the first part is the name to resolve, any 12 remaining parts should be retained. 13 2) Walk the context stack from top to bottom, finding the first context 14 that is a) a hash containing the name as a key OR b) an object responding 15 to a method with the given name. 16 3) If the context is a hash, the data is the value associated with the 17 name. 18 4) If the context is an object and the method with the given name has an 19 arity of 1, the method SHOULD be called with a String containing the 20 unprocessed contents of the sections; the data is the value returned. 21 5) Otherwise, the data is the value returned by calling the method with 22 the given name. 23 6) If any name parts were retained in step 1, each should be resolved 24 against a context stack containing only the result from the former 25 resolution. If any part fails resolution, the result should be considered 26 falsey, and should interpolate as the empty string. 27 If the data is not of a list type, it is coerced into a list as follows: if 28 the data is truthy (e.g. `!!data == true`), use a single-element list 29 containing the data, otherwise use an empty list. 30 31 For each element in the data list, the element MUST be pushed onto the 32 context stack, the section MUST be rendered, and the element MUST be popped 33 off the context stack. 34 35 Section and End Section tags SHOULD be treated as standalone when 36 appropriate. 37tests: 38 - name: Truthy 39 desc: Truthy sections should have their contents rendered. 40 data: { boolean: true } 41 template: '"{{#boolean}}This should be rendered.{{/boolean}}"' 42 expected: '"This should be rendered."' 43 44 - name: Falsey 45 desc: Falsey sections should have their contents omitted. 46 data: { boolean: false } 47 template: '"{{#boolean}}This should not be rendered.{{/boolean}}"' 48 expected: '""' 49 50 - name: Context 51 desc: Objects and hashes should be pushed onto the context stack. 52 data: { context: { name: 'Joe' } } 53 template: '"{{#context}}Hi {{name}}.{{/context}}"' 54 expected: '"Hi Joe."' 55 56 - name: Deeply Nested Contexts 57 desc: All elements on the context stack should be accessible. 58 data: 59 a: { one: 1 } 60 b: { two: 2 } 61 c: { three: 3 } 62 d: { four: 4 } 63 e: { five: 5 } 64 template: | 65 {{#a}} 66 {{one}} 67 {{#b}} 68 {{one}}{{two}}{{one}} 69 {{#c}} 70 {{one}}{{two}}{{three}}{{two}}{{one}} 71 {{#d}} 72 {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}} 73 {{#e}} 74 {{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}} 75 {{/e}} 76 {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}} 77 {{/d}} 78 {{one}}{{two}}{{three}}{{two}}{{one}} 79 {{/c}} 80 {{one}}{{two}}{{one}} 81 {{/b}} 82 {{one}} 83 {{/a}} 84 expected: | 85 1 86 121 87 12321 88 1234321 89 123454321 90 1234321 91 12321 92 121 93 1 94 95 - name: List 96 desc: Lists should be iterated; list items should visit the context stack. 97 data: { list: [ { item: 1 }, { item: 2 }, { item: 3 } ] } 98 template: '"{{#list}}{{item}}{{/list}}"' 99 expected: '"123"' 100 101 - name: Empty List 102 desc: Empty lists should behave like falsey values. 103 data: { list: [ ] } 104 template: '"{{#list}}Yay lists!{{/list}}"' 105 expected: '""' 106 107 - name: Doubled 108 desc: Multiple sections per template should be permitted. 109 data: { bool: true, two: 'second' } 110 template: | 111 {{#bool}} 112 * first 113 {{/bool}} 114 * {{two}} 115 {{#bool}} 116 * third 117 {{/bool}} 118 expected: | 119 * first 120 * second 121 * third 122 123 - name: Nested (Truthy) 124 desc: Nested truthy sections should have their contents rendered. 125 data: { bool: true } 126 template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |" 127 expected: "| A B C D E |" 128 129 - name: Nested (Falsey) 130 desc: Nested falsey sections should be omitted. 131 data: { bool: false } 132 template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |" 133 expected: "| A E |" 134 135 - name: Context Misses 136 desc: Failed context lookups should be considered falsey. 137 data: { } 138 template: "[{{#missing}}Found key 'missing'!{{/missing}}]" 139 expected: "[]" 140 141 # Implicit Iterators 142 143 - name: Implicit Iterator - String 144 desc: Implicit iterators should directly interpolate strings. 145 data: 146 list: [ 'a', 'b', 'c', 'd', 'e' ] 147 template: '"{{#list}}({{.}}){{/list}}"' 148 expected: '"(a)(b)(c)(d)(e)"' 149 150 - name: Implicit Iterator - Integer 151 desc: Implicit iterators should cast integers to strings and interpolate. 152 data: 153 list: [ 1, 2, 3, 4, 5 ] 154 template: '"{{#list}}({{.}}){{/list}}"' 155 expected: '"(1)(2)(3)(4)(5)"' 156 157 - name: Implicit Iterator - Decimal 158 desc: Implicit iterators should cast decimals to strings and interpolate. 159 data: 160 list: [ 1.10, 2.20, 3.30, 4.40, 5.50 ] 161 template: '"{{#list}}({{.}}){{/list}}"' 162 expected: '"(1.1)(2.2)(3.3)(4.4)(5.5)"' 163 164 - name: Implicit Iterator - Array 165 desc: Implicit iterators should allow iterating over nested arrays. 166 data: 167 list: [ [1, 2, 3], ['a', 'b', 'c'] ] 168 template: '"{{#list}}({{#.}}{{.}}{{/.}}){{/list}}"' 169 expected: '"(123)(abc)"' 170 171 # Dotted Names 172 173 - name: Dotted Names - Truthy 174 desc: Dotted names should be valid for Section tags. 175 data: { a: { b: { c: true } } } 176 template: '"{{#a.b.c}}Here{{/a.b.c}}" == "Here"' 177 expected: '"Here" == "Here"' 178 179 - name: Dotted Names - Falsey 180 desc: Dotted names should be valid for Section tags. 181 data: { a: { b: { c: false } } } 182 template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""' 183 expected: '"" == ""' 184 185 - name: Dotted Names - Broken Chains 186 desc: Dotted names that cannot be resolved should be considered falsey. 187 data: { a: { } } 188 template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""' 189 expected: '"" == ""' 190 191 # Whitespace Sensitivity 192 193 - name: Surrounding Whitespace 194 desc: Sections should not alter surrounding whitespace. 195 data: { boolean: true } 196 template: " | {{#boolean}}\t|\t{{/boolean}} | \n" 197 expected: " | \t|\t | \n" 198 199 - name: Internal Whitespace 200 desc: Sections should not alter internal whitespace. 201 data: { boolean: true } 202 template: " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n" 203 expected: " | \n | \n" 204 205 - name: Indented Inline Sections 206 desc: Single-line sections should not alter surrounding whitespace. 207 data: { boolean: true } 208 template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n" 209 expected: " YES\n GOOD\n" 210 211 - name: Standalone Lines 212 desc: Standalone lines should be removed from the template. 213 data: { boolean: true } 214 template: | 215 | This Is 216 {{#boolean}} 217 | 218 {{/boolean}} 219 | A Line 220 expected: | 221 | This Is 222 | 223 | A Line 224 225 - name: Indented Standalone Lines 226 desc: Indented standalone lines should be removed from the template. 227 data: { boolean: true } 228 template: | 229 | This Is 230 {{#boolean}} 231 | 232 {{/boolean}} 233 | A Line 234 expected: | 235 | This Is 236 | 237 | A Line 238 239 - name: Standalone Line Endings 240 desc: '"\r\n" should be considered a newline for standalone tags.' 241 data: { boolean: true } 242 template: "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|" 243 expected: "|\r\n|" 244 245 - name: Standalone Without Previous Line 246 desc: Standalone tags should not require a newline to precede them. 247 data: { boolean: true } 248 template: " {{#boolean}}\n#{{/boolean}}\n/" 249 expected: "#\n/" 250 251 - name: Standalone Without Newline 252 desc: Standalone tags should not require a newline to follow them. 253 data: { boolean: true } 254 template: "#{{#boolean}}\n/\n {{/boolean}}" 255 expected: "#\n/\n" 256 257 # Whitespace Insensitivity 258 259 - name: Padding 260 desc: Superfluous in-tag whitespace should be ignored. 261 data: { boolean: true } 262 template: '|{{# boolean }}={{/ boolean }}|' 263 expected: '|=|' 264