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 # Dotted Names 165 166 - name: Dotted Names - Truthy 167 desc: Dotted names should be valid for Section tags. 168 data: { a: { b: { c: true } } } 169 template: '"{{#a.b.c}}Here{{/a.b.c}}" == "Here"' 170 expected: '"Here" == "Here"' 171 172 - name: Dotted Names - Falsey 173 desc: Dotted names should be valid for Section tags. 174 data: { a: { b: { c: false } } } 175 template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""' 176 expected: '"" == ""' 177 178 - name: Dotted Names - Broken Chains 179 desc: Dotted names that cannot be resolved should be considered falsey. 180 data: { a: { } } 181 template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""' 182 expected: '"" == ""' 183 184 # Whitespace Sensitivity 185 186 - name: Surrounding Whitespace 187 desc: Sections should not alter surrounding whitespace. 188 data: { boolean: true } 189 template: " | {{#boolean}}\t|\t{{/boolean}} | \n" 190 expected: " | \t|\t | \n" 191 192 - name: Internal Whitespace 193 desc: Sections should not alter internal whitespace. 194 data: { boolean: true } 195 template: " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n" 196 expected: " | \n | \n" 197 198 - name: Indented Inline Sections 199 desc: Single-line sections should not alter surrounding whitespace. 200 data: { boolean: true } 201 template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n" 202 expected: " YES\n GOOD\n" 203 204 - name: Standalone Lines 205 desc: Standalone lines should be removed from the template. 206 data: { boolean: true } 207 template: | 208 | This Is 209 {{#boolean}} 210 | 211 {{/boolean}} 212 | A Line 213 expected: | 214 | This Is 215 | 216 | A Line 217 218 - name: Indented Standalone Lines 219 desc: Indented standalone lines should be removed from the template. 220 data: { boolean: true } 221 template: | 222 | This Is 223 {{#boolean}} 224 | 225 {{/boolean}} 226 | A Line 227 expected: | 228 | This Is 229 | 230 | A Line 231 232 - name: Standalone Line Endings 233 desc: '"\r\n" should be considered a newline for standalone tags.' 234 data: { boolean: true } 235 template: "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|" 236 expected: "|\r\n|" 237 238 - name: Standalone Without Previous Line 239 desc: Standalone tags should not require a newline to precede them. 240 data: { boolean: true } 241 template: " {{#boolean}}\n#{{/boolean}}\n/" 242 expected: "#\n/" 243 244 - name: Standalone Without Newline 245 desc: Standalone tags should not require a newline to follow them. 246 data: { boolean: true } 247 template: "#{{#boolean}}\n/\n {{/boolean}}" 248 expected: "#\n/\n" 249 250 # Whitespace Insensitivity 251 252 - name: Padding 253 desc: Superfluous in-tag whitespace should be ignored. 254 data: { boolean: true } 255 template: '|{{# boolean }}={{/ boolean }}|' 256 expected: '|=|' 257