Skip to content

Commit 83b221a

Browse files
authored
Merge pull request #176 from Mr0grog/87-yes-you-can-iterate-over-the-root
Clarify that the context root can be iterated over
2 parents 982aa9b + cb02286 commit 83b221a

File tree

3 files changed

+74
-9
lines changed

3 files changed

+74
-9
lines changed

specs/sections.json

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.",
3-
"overview": "Section tags and End Section tags are used in combination to wrap a section\nof the template for iteration\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Section tag MUST be followed\nby an End Section tag with the same content within the same section.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nFor each element in the data list, the element MUST be pushed onto the\ncontext stack, the section MUST be rendered, and the element MUST be popped\noff the context stack.\n\nSection and End Section tags SHOULD be treated as standalone when\nappropriate.\n",
3+
"overview": "Section tags and End Section tags are used in combination to wrap a section\nof the template for iteration.\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Section tag MUST be followed\nby an End Section tag with the same content within the same section.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) If the name is a single period (.), the data is the item currently\n sitting atop the context stack. Skip the rest of these steps.\n 2) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 3) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 4) If the context is a hash, the data is the value associated with the\n name.\n 5) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 6) Otherwise, the data is the value returned by calling the method with\n the given name.\n 7) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\n\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nFor each element in the data list, the element MUST be pushed onto the\ncontext stack, the section MUST be rendered, and the element MUST be popped\noff the context stack.\n\nSection and End Section tags SHOULD be treated as standalone when\nappropriate.\n",
44
"tests": [
55
{
66
"name": "Truthy",
@@ -246,6 +246,62 @@
246246
"template": "\"{{#list}}({{#.}}{{.}}{{/.}}){{/list}}\"",
247247
"expected": "\"(123)(abc)\""
248248
},
249+
{
250+
"name": "Implicit Iterator - HTML Escaping",
251+
"desc": "Implicit iterators with basic interpolation should be HTML escaped.",
252+
"data": {
253+
"list": [
254+
"&",
255+
"\"",
256+
"<",
257+
">"
258+
]
259+
},
260+
"template": "\"{{#list}}({{.}}){{/list}}\"",
261+
"expected": "\"(&amp;)(&quot;)(&lt;)(&gt;)\""
262+
},
263+
{
264+
"name": "Implicit Iterator - Triple mustache",
265+
"desc": "Implicit iterators in triple mustache should interpolate without HTML escaping.",
266+
"data": {
267+
"list": [
268+
"&",
269+
"\"",
270+
"<",
271+
">"
272+
]
273+
},
274+
"template": "\"{{#list}}({{{.}}}){{/list}}\"",
275+
"expected": "\"(&)(\")(<)(>)\""
276+
},
277+
{
278+
"name": "Implicit Iterator - Ampersand",
279+
"desc": "Implicit iterators in an Ampersand tag should interpolate without HTML escaping.",
280+
"data": {
281+
"list": [
282+
"&",
283+
"\"",
284+
"<",
285+
">"
286+
]
287+
},
288+
"template": "\"{{#list}}({{&.}}){{/list}}\"",
289+
"expected": "\"(&)(\")(<)(>)\""
290+
},
291+
{
292+
"name": "Implicit Iterator - Root-level",
293+
"desc": "Implicit iterators should work on root-level lists.",
294+
"data": [
295+
{
296+
"value": "a"
297+
},
298+
{
299+
"value": "b"
300+
}
301+
],
302+
"template": "\"{{#.}}({{value}}){{/.}}\"",
303+
"expected": "\"(a)(b)\""
304+
},
249305
{
250306
"name": "Dotted Names - Truthy",
251307
"desc": "Dotted names should be valid for Section tags.",

specs/sections.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
overview: |
22
Section tags and End Section tags are used in combination to wrap a section
3-
of the template for iteration
3+
of the template for iteration.
44
55
These tags' content MUST be a non-whitespace character sequence NOT
66
containing the current closing delimiter; each Section tag MUST be followed
77
by an End Section tag with the same content within the same section.
88
99
This tag's content names the data to replace the tag. Name resolution is as
1010
follows:
11-
1) Split the name on periods; the first part is the name to resolve, any
11+
1) If the name is a single period (.), the data is the item currently
12+
sitting atop the context stack. Skip the rest of these steps.
13+
2) Split the name on periods; the first part is the name to resolve, any
1214
remaining parts should be retained.
13-
2) Walk the context stack from top to bottom, finding the first context
15+
3) Walk the context stack from top to bottom, finding the first context
1416
that is a) a hash containing the name as a key OR b) an object responding
1517
to a method with the given name.
16-
3) If the context is a hash, the data is the value associated with the
18+
4) If the context is a hash, the data is the value associated with the
1719
name.
18-
4) If the context is an object and the method with the given name has an
20+
5) If the context is an object and the method with the given name has an
1921
arity of 1, the method SHOULD be called with a String containing the
2022
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
23+
6) Otherwise, the data is the value returned by calling the method with
2224
the given name.
23-
6) If any name parts were retained in step 1, each should be resolved
25+
7) If any name parts were retained in step 1, each should be resolved
2426
against a context stack containing only the result from the former
2527
resolution. If any part fails resolution, the result should be considered
2628
falsey, and should interpolate as the empty string.
29+
2730
If the data is not of a list type, it is coerced into a list as follows: if
2831
the data is truthy (e.g. `!!data == true`), use a single-element list
2932
containing the data, otherwise use an empty list.
@@ -227,6 +230,12 @@ tests:
227230
template: '"{{#list}}({{&.}}){{/list}}"'
228231
expected: '"(&)(")(<)(>)"'
229232

233+
- name: Implicit Iterator - Root-level
234+
desc: Implicit iterators should work on root-level lists.
235+
data: [ { value: 'a' }, { value: 'b' } ]
236+
template: '"{{#.}}({{value}}){{/.}}"'
237+
expected: '"(a)(b)"'
238+
230239
# Dotted Names
231240

232241
- name: Dotted Names - Truthy

specs/~lambdas.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
"clojure": "(fn [text] (if (= text \"{{x}}\") \"yes\" \"no\"))",
126126
"lisp": "(lambda (text) (if (string= text \"{{x}}\") \"yes\" \"no\"))",
127127
"pwsh": "if ($args[0] -eq \"{{x}}\") {\"yes\"} else {\"no\"}",
128-
"go": "func(text string) string { if text == \"{{x}}\" { return \"yes\" } else { return \"no\" } }"
128+
"go": "func(text string) string { if text == \"{{x}}\" { return \"yes\" } else { return \"no\" } }"
129129
}
130130
},
131131
"template": "<{{#lambda}}{{x}}{{/lambda}}>",

0 commit comments

Comments
 (0)