Skip to content

Commit

Permalink
Merge pull request #176 from Mr0grog/87-yes-you-can-iterate-over-the-…
Browse files Browse the repository at this point in the history
…root

Clarify that the context root can be iterated over
  • Loading branch information
jgonggrijp authored Nov 16, 2023
2 parents 982aa9b + cb02286 commit 83b221a
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 9 deletions.
58 changes: 57 additions & 1 deletion specs/sections.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.",
"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",
"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",
"tests": [
{
"name": "Truthy",
Expand Down Expand Up @@ -246,6 +246,62 @@
"template": "\"{{#list}}({{#.}}{{.}}{{/.}}){{/list}}\"",
"expected": "\"(123)(abc)\""
},
{
"name": "Implicit Iterator - HTML Escaping",
"desc": "Implicit iterators with basic interpolation should be HTML escaped.",
"data": {
"list": [
"&",
"\"",
"<",
">"
]
},
"template": "\"{{#list}}({{.}}){{/list}}\"",
"expected": "\"(&amp;)(&quot;)(&lt;)(&gt;)\""
},
{
"name": "Implicit Iterator - Triple mustache",
"desc": "Implicit iterators in triple mustache should interpolate without HTML escaping.",
"data": {
"list": [
"&",
"\"",
"<",
">"
]
},
"template": "\"{{#list}}({{{.}}}){{/list}}\"",
"expected": "\"(&)(\")(<)(>)\""
},
{
"name": "Implicit Iterator - Ampersand",
"desc": "Implicit iterators in an Ampersand tag should interpolate without HTML escaping.",
"data": {
"list": [
"&",
"\"",
"<",
">"
]
},
"template": "\"{{#list}}({{&.}}){{/list}}\"",
"expected": "\"(&)(\")(<)(>)\""
},
{
"name": "Implicit Iterator - Root-level",
"desc": "Implicit iterators should work on root-level lists.",
"data": [
{
"value": "a"
},
{
"value": "b"
}
],
"template": "\"{{#.}}({{value}}){{/.}}\"",
"expected": "\"(a)(b)\""
},
{
"name": "Dotted Names - Truthy",
"desc": "Dotted names should be valid for Section tags.",
Expand Down
23 changes: 16 additions & 7 deletions specs/sections.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
overview: |
Section tags and End Section tags are used in combination to wrap a section
of the template for iteration
of the template for iteration.
These tags' content MUST be a non-whitespace character sequence NOT
containing the current closing delimiter; each Section tag MUST be followed
by an End Section tag with the same content within the same section.
This tag's content names the data to replace the tag. Name resolution is as
follows:
1) Split the name on periods; the first part is the name to resolve, any
1) If the name is a single period (.), the data is the item currently
sitting atop the context stack. Skip the rest of these steps.
2) Split the name on periods; the first part is the name to resolve, any
remaining parts should be retained.
2) Walk the context stack from top to bottom, finding the first context
3) Walk the context stack from top to bottom, finding the first context
that is a) a hash containing the name as a key OR b) an object responding
to a method with the given name.
3) If the context is a hash, the data is the value associated with the
4) If the context is a hash, the data is the value associated with the
name.
4) If the context is an object and the method with the given name has an
5) If the context is an object and the method with the given name has an
arity of 1, the method SHOULD be called with a String containing the
unprocessed contents of the sections; the data is the value returned.
5) Otherwise, the data is the value returned by calling the method with
6) Otherwise, the data is the value returned by calling the method with
the given name.
6) If any name parts were retained in step 1, each should be resolved
7) If any name parts were retained in step 1, each should be resolved
against a context stack containing only the result from the former
resolution. If any part fails resolution, the result should be considered
falsey, and should interpolate as the empty string.
If the data is not of a list type, it is coerced into a list as follows: if
the data is truthy (e.g. `!!data == true`), use a single-element list
containing the data, otherwise use an empty list.
Expand Down Expand Up @@ -227,6 +230,12 @@ tests:
template: '"{{#list}}({{&.}}){{/list}}"'
expected: '"(&)(")(<)(>)"'

- name: Implicit Iterator - Root-level
desc: Implicit iterators should work on root-level lists.
data: [ { value: 'a' }, { value: 'b' } ]
template: '"{{#.}}({{value}}){{/.}}"'
expected: '"(a)(b)"'

# Dotted Names

- name: Dotted Names - Truthy
Expand Down
2 changes: 1 addition & 1 deletion specs/~lambdas.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"clojure": "(fn [text] (if (= text \"{{x}}\") \"yes\" \"no\"))",
"lisp": "(lambda (text) (if (string= text \"{{x}}\") \"yes\" \"no\"))",
"pwsh": "if ($args[0] -eq \"{{x}}\") {\"yes\"} else {\"no\"}",
"go": "func(text string) string { if text == \"{{x}}\" { return \"yes\" } else { return \"no\" } }"
"go": "func(text string) string { if text == \"{{x}}\" { return \"yes\" } else { return \"no\" } }"
}
},
"template": "<{{#lambda}}{{x}}{{/lambda}}>",
Expand Down

0 comments on commit 83b221a

Please sign in to comment.