mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
257 lines
8.0 KiB
YAML
257 lines
8.0 KiB
YAML
overview: |
|
||
Section tags and End Section tags are used in combination to wrap a section
|
||
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
|
||
remaining parts should be retained.
|
||
2) 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
|
||
name.
|
||
4) 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
|
||
the given name.
|
||
6) 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.
|
||
|
||
For each element in the data list, the element MUST be pushed onto the
|
||
context stack, the section MUST be rendered, and the element MUST be popped
|
||
off the context stack.
|
||
|
||
Section and End Section tags SHOULD be treated as standalone when
|
||
appropriate.
|
||
tests:
|
||
- name: Truthy
|
||
desc: Truthy sections should have their contents rendered.
|
||
data: { boolean: true }
|
||
template: '"{{#boolean}}This should be rendered.{{/boolean}}"'
|
||
expected: '"This should be rendered."'
|
||
|
||
- name: Falsey
|
||
desc: Falsey sections should have their contents omitted.
|
||
data: { boolean: false }
|
||
template: '"{{#boolean}}This should not be rendered.{{/boolean}}"'
|
||
expected: '""'
|
||
|
||
- name: Context
|
||
desc: Objects and hashes should be pushed onto the context stack.
|
||
data: { context: { name: 'Joe' } }
|
||
template: '"{{#context}}Hi {{name}}.{{/context}}"'
|
||
expected: '"Hi Joe."'
|
||
|
||
- name: Deeply Nested Contexts
|
||
desc: All elements on the context stack should be accessible.
|
||
data:
|
||
a: { one: 1 }
|
||
b: { two: 2 }
|
||
c: { three: 3 }
|
||
d: { four: 4 }
|
||
e: { five: 5 }
|
||
template: |
|
||
{{#a}}
|
||
{{one}}
|
||
{{#b}}
|
||
{{one}}{{two}}{{one}}
|
||
{{#c}}
|
||
{{one}}{{two}}{{three}}{{two}}{{one}}
|
||
{{#d}}
|
||
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
|
||
{{#e}}
|
||
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
|
||
{{/e}}
|
||
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
|
||
{{/d}}
|
||
{{one}}{{two}}{{three}}{{two}}{{one}}
|
||
{{/c}}
|
||
{{one}}{{two}}{{one}}
|
||
{{/b}}
|
||
{{one}}
|
||
{{/a}}
|
||
expected: |
|
||
1
|
||
121
|
||
12321
|
||
1234321
|
||
123454321
|
||
1234321
|
||
12321
|
||
121
|
||
1
|
||
|
||
- name: List
|
||
desc: Lists should be iterated; list items should visit the context stack.
|
||
data: { list: [ { item: 1 }, { item: 2 }, { item: 3 } ] }
|
||
template: '"{{#list}}{{item}}{{/list}}"'
|
||
expected: '"123"'
|
||
|
||
- name: Empty List
|
||
desc: Empty lists should behave like falsey values.
|
||
data: { list: [ ] }
|
||
template: '"{{#list}}Yay lists!{{/list}}"'
|
||
expected: '""'
|
||
|
||
- name: Doubled
|
||
desc: Multiple sections per template should be permitted.
|
||
data: { bool: true, two: 'second' }
|
||
template: |
|
||
{{#bool}}
|
||
* first
|
||
{{/bool}}
|
||
* {{two}}
|
||
{{#bool}}
|
||
* third
|
||
{{/bool}}
|
||
expected: |
|
||
* first
|
||
* second
|
||
* third
|
||
|
||
- name: Nested (Truthy)
|
||
desc: Nested truthy sections should have their contents rendered.
|
||
data: { bool: true }
|
||
template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"
|
||
expected: "| A B C D E |"
|
||
|
||
- name: Nested (Falsey)
|
||
desc: Nested falsey sections should be omitted.
|
||
data: { bool: false }
|
||
template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"
|
||
expected: "| A E |"
|
||
|
||
- name: Context Misses
|
||
desc: Failed context lookups should be considered falsey.
|
||
data: { }
|
||
template: "[{{#missing}}Found key 'missing'!{{/missing}}]"
|
||
expected: "[]"
|
||
|
||
# Implicit Iterators
|
||
|
||
- name: Implicit Iterator - String
|
||
desc: Implicit iterators should directly interpolate strings.
|
||
data:
|
||
list: [ 'a', 'b', 'c', 'd', 'e' ]
|
||
template: '"{{#list}}({{.}}){{/list}}"'
|
||
expected: '"(a)(b)(c)(d)(e)"'
|
||
|
||
- name: Implicit Iterator - Integer
|
||
desc: Implicit iterators should cast integers to strings and interpolate.
|
||
data:
|
||
list: [ 1, 2, 3, 4, 5 ]
|
||
template: '"{{#list}}({{.}}){{/list}}"'
|
||
expected: '"(1)(2)(3)(4)(5)"'
|
||
|
||
- name: Implicit Iterator - Decimal
|
||
desc: Implicit iterators should cast decimals to strings and interpolate.
|
||
data:
|
||
list: [ 1.10, 2.20, 3.30, 4.40, 5.50 ]
|
||
template: '"{{#list}}({{.}}){{/list}}"'
|
||
expected: '"(1.1)(2.2)(3.3)(4.4)(5.5)"'
|
||
|
||
# Dotted Names
|
||
|
||
- name: Dotted Names - Truthy
|
||
desc: Dotted names should be valid for Section tags.
|
||
data: { a: { b: { c: true } } }
|
||
template: '"{{#a.b.c}}Here{{/a.b.c}}" == "Here"'
|
||
expected: '"Here" == "Here"'
|
||
|
||
- name: Dotted Names - Falsey
|
||
desc: Dotted names should be valid for Section tags.
|
||
data: { a: { b: { c: false } } }
|
||
template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""'
|
||
expected: '"" == ""'
|
||
|
||
- name: Dotted Names - Broken Chains
|
||
desc: Dotted names that cannot be resolved should be considered falsey.
|
||
data: { a: { } }
|
||
template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""'
|
||
expected: '"" == ""'
|
||
|
||
# Whitespace Sensitivity
|
||
|
||
- name: Surrounding Whitespace
|
||
desc: Sections should not alter surrounding whitespace.
|
||
data: { boolean: true }
|
||
template: " | {{#boolean}}\t|\t{{/boolean}} | \n"
|
||
expected: " | \t|\t | \n"
|
||
|
||
- name: Internal Whitespace
|
||
desc: Sections should not alter internal whitespace.
|
||
data: { boolean: true }
|
||
template: " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"
|
||
expected: " | \n | \n"
|
||
|
||
- name: Indented Inline Sections
|
||
desc: Single-line sections should not alter surrounding whitespace.
|
||
data: { boolean: true }
|
||
template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n"
|
||
expected: " YES\n GOOD\n"
|
||
|
||
- name: Standalone Lines
|
||
desc: Standalone lines should be removed from the template.
|
||
data: { boolean: true }
|
||
template: |
|
||
| This Is
|
||
{{#boolean}}
|
||
|
|
||
{{/boolean}}
|
||
| A Line
|
||
expected: |
|
||
| This Is
|
||
|
|
||
| A Line
|
||
|
||
- name: Indented Standalone Lines
|
||
desc: Indented standalone lines should be removed from the template.
|
||
data: { boolean: true }
|
||
template: |
|
||
| This Is
|
||
{{#boolean}}
|
||
|
|
||
{{/boolean}}
|
||
| A Line
|
||
expected: |
|
||
| This Is
|
||
|
|
||
| A Line
|
||
|
||
- name: Standalone Line Endings
|
||
desc: '"\r\n" should be considered a newline for standalone tags.'
|
||
data: { boolean: true }
|
||
template: "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|"
|
||
expected: "|\r\n|"
|
||
|
||
- name: Standalone Without Previous Line
|
||
desc: Standalone tags should not require a newline to precede them.
|
||
data: { boolean: true }
|
||
template: " {{#boolean}}\n#{{/boolean}}\n/"
|
||
expected: "#\n/"
|
||
|
||
- name: Standalone Without Newline
|
||
desc: Standalone tags should not require a newline to follow them.
|
||
data: { boolean: true }
|
||
template: "#{{#boolean}}\n/\n {{/boolean}}"
|
||
expected: "#\n/\n"
|
||
|
||
# Whitespace Insensitivity
|
||
|
||
- name: Padding
|
||
desc: Superfluous in-tag whitespace should be ignored.
|
||
data: { boolean: true }
|
||
template: '|{{# boolean }}={{/ boolean }}|'
|
||
expected: '|=|'
|