sections.yml 8.0 KB

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