OLD | NEW |
(Empty) | |
| 1 overview: | |
| 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. |
| 37 tests: |
| 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: '|=|' |
OLD | NEW |