From 45650e76ef5e8fc961337f5a8f0d2a774b6e5722 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Sat, 4 Dec 2021 17:14:16 +0100 Subject: Dynamic partials --- specs/dynamic.yml | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 specs/dynamic.yml diff --git a/specs/dynamic.yml b/specs/dynamic.yml new file mode 100644 index 0000000..83e428e --- /dev/null +++ b/specs/dynamic.yml @@ -0,0 +1,126 @@ +overview: | + Dynamic partials tags are used to dynamically expand an external template into + the current template. + + The tag's content MUST be a non-whitespace character sequence NOT containing + the current closing delimiter. + + This tag's content names a key in the context whose value is the name of the + partial that will be loaded. If the dynamically named partial cannot be found, + the empty string SHOULD be used instead, as in interpolations. Set Delimiter + tags MUST NOT affect the parsing of a partial. The partial MUST be rendered + against the context stack local to the tag. Failed resolutions of the key + (context lookups) should be considered falsey and should interpolate as the + empty string. If the partial, whose name is retrieved from the context stack, + cannot be found, the empty string SHOULD be used instead, as in + interpolations. + + Dynamic partial tags SHOULD be treated as standalone when appropriate. If + this tag is used standalone, any whitespace preceding the tag should treated + as indentation, and prepended to each line of the partial before rendering. +tests: + - name: Basic Behavior + desc: The asterisk operator is used for dynamic partials. + data: { dynamic: 'content' } + template: '"{{*dynamic}}"' + partials: { content: 'Hello, world!' } + expected: '"Hello, world!"' + + - name: Context Misses + desc: Failed context lookups should be considered falsey. + data: { } + template: '"{{*missing}}"' + partials: { } + expected: '""' + + - name: Failed Lookup + desc: The empty string should be used when the named partial is not found. + data: { dynamic: 'content' } + template: '"{{*dynamic}}"' + partials: { foobar: 'Hello, world!' } + expected: '""' + + - name: Context + desc: The asterisk operator should operate within the current context. + data: { text: 'Hello, world!', example: 'partial' } + template: '"{{*example}}"' + partials: { partial: '*{{text}}*' } + expected: '"*Hello, world!*"' + + - name: Recursion + desc: The asterisk operator should properly recurse. + data: | + { + template: 'node', + content: 'X', + nodes: [ { content: 'Y', nodes: [] } ] + } + template: '{{*template}}' + partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' } + expected: 'X>' + + # Whitespace Sensitivity + + - name: Surrounding Whitespace + desc: The asterisk operator should not alter surrounding whitespace. + data: { partial: 'foobar' } + template: '| {{*partial}} |' + partials: { foobar: "\t|\t" } + expected: "| \t|\t |" + + - name: Inline Indentation + desc: Whitespace should be left untouched. + data: { dynamic: 'partial', data: '|' } + template: " {{data}} {{* partial}}\n" + partials: { partial: ">\n>" } + expected: " | >\n>\n" + + - name: Standalone Line Endings + desc: '"\r\n" should be considered a newline for standalone tags.' + data: { dynamic: 'partial' } + template: "|\r\n{{*dynamic}}\r\n|" + partials: { partial: ">" } + expected: "|\r\n>|" + + - name: Standalone Without Previous Line + desc: Standalone tags should not require a newline to precede them. + data: { dynamic: 'partial' } + template: " {{*dynamic}}\n>" + partials: { partial: ">\n>"} + expected: " >\n >>" + + - name: Standalone Without Newline + desc: Standalone tags should not require a newline to follow them. + data: { dynamic: 'partial' } + template: ">\n {{*dynamic}}" + partials: { partial: ">\n>" } + expected: ">\n >\n >" + + - name: Standalone Indentation + desc: Each line of the partial should be indented before rendering. + data: { dynamic: 'partial', content: "<\n->" } + template: | + \ + {{*dynamic}} + / + partials: + partial: | + | + {{{content}}} + | + expected: | + \ + | + < + -> + | + / + + # Whitespace Insensitivity + + - name: Padding Whitespace + desc: Superfluous in-tag whitespace should be ignored. + data: { dynamic: 'partial', boolean: true } + template: "|{{* dynamic }}|" + partials: { partial: "[]" } + expected: '|[]|' -- cgit v1.2.1 From 7da3ef2cfea6bc3d66a095ec3ae1e75bff9dcbbe Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Sat, 4 Dec 2021 17:15:10 +0100 Subject: Dynamic partials (json) --- specs/dynamic.json | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 specs/dynamic.json diff --git a/specs/dynamic.json b/specs/dynamic.json new file mode 100644 index 0000000..8fa5cf4 --- /dev/null +++ b/specs/dynamic.json @@ -0,0 +1,147 @@ +{ + "overview": "Dynamic partials tags are used to dynamically expand an external template into\nthe current template.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names a key in the context whose value is the name of the\npartial that will be loaded. If the dynamically named partial cannot be found,\nthe empty string SHOULD be used instead, as in interpolations. Set Delimiter\ntags MUST NOT affect the parsing of a partial. The partial MUST be rendered\nagainst the context stack local to the tag. Failed resolutions of the key\n(context lookups) should be considered falsey and should interpolate as the\nempty string. If the partial, whose name is retrieved from the context stack,\ncannot be found, the empty string SHOULD be used instead, as in\ninterpolations.\n\nDynamic partial tags SHOULD be treated as standalone when appropriate. If\nthis tag is used standalone, any whitespace preceding the tag should treated\nas indentation, and prepended to each line of the partial before rendering.\n", + "tests": [ + { + "name": "Basic Behavior", + "desc": "The asterisk operator is used for dynamic partials.", + "data": { + "dynamic": "content" + }, + "template": "\"{{*dynamic}}\"", + "partials": { + "content": "Hello, world!" + }, + "expected": "\"Hello, world!\"" + }, + { + "name": "Context Misses", + "desc": "Failed context lookups should be considered falsey.", + "data": {}, + "template": "\"{{*missing}}\"", + "partials": {}, + "expected": "\"\"" + }, + { + "name": "Failed Lookup", + "desc": "The empty string should be used when the named partial is not found.", + "data": { + "dynamic": "content" + }, + "template": "\"{{*dynamic}}\"", + "partials": { + "foobar": "Hello, world!" + }, + "expected": "\"\"" + }, + { + "name": "Context", + "desc": "The asterisk operator should operate within the current context.", + "data": { + "text": "Hello, world!", + "example": "partial" + }, + "template": "\"{{*example}}\"", + "partials": { + "partial": "*{{text}}*" + }, + "expected": "\"*Hello, world!*\"" + }, + { + "name": "Recursion", + "desc": "The asterisk operator should properly recurse.", + "data": "{\n template: 'node',\n content: 'X',\n nodes: [ { content: 'Y', nodes: [] } ]\n}\n", + "template": "{{*template}}", + "partials": { + "node": "{{content}}<{{#nodes}}{{>node}}{{/nodes}}>" + }, + "expected": "X>" + }, + { + "name": "Surrounding Whitespace", + "desc": "The asterisk operator should not alter surrounding whitespace.", + "data": { + "partial": "foobar" + }, + "template": "| {{*partial}} |", + "partials": { + "foobar": "\t|\t" + }, + "expected": "| \t|\t |" + }, + { + "name": "Inline Indentation", + "desc": "Whitespace should be left untouched.", + "data": { + "dynamic": "partial", + "data": "|" + }, + "template": " {{data}} {{* partial}}\n", + "partials": { + "partial": ">\n>" + }, + "expected": " | >\n>\n" + }, + { + "name": "Standalone Line Endings", + "desc": "\"\\r\\n\" should be considered a newline for standalone tags.", + "data": { + "dynamic": "partial" + }, + "template": "|\r\n{{*dynamic}}\r\n|", + "partials": { + "partial": ">" + }, + "expected": "|\r\n>|" + }, + { + "name": "Standalone Without Previous Line", + "desc": "Standalone tags should not require a newline to precede them.", + "data": { + "dynamic": "partial" + }, + "template": " {{*dynamic}}\n>", + "partials": { + "partial": ">\n>" + }, + "expected": " >\n >>" + }, + { + "name": "Standalone Without Newline", + "desc": "Standalone tags should not require a newline to follow them.", + "data": { + "dynamic": "partial" + }, + "template": ">\n {{*dynamic}}", + "partials": { + "partial": ">\n>" + }, + "expected": ">\n >\n >" + }, + { + "name": "Standalone Indentation", + "desc": "Each line of the partial should be indented before rendering.", + "data": { + "dynamic": "partial", + "content": "<\n->" + }, + "template": "\\\n {{*dynamic}}\n/\n", + "partials": { + "partial": "|\n{{{content}}}\n|\n" + }, + "expected": "\\\n |\n <\n->\n |\n/\n" + }, + { + "name": "Padding Whitespace", + "desc": "Superfluous in-tag whitespace should be ignored.", + "data": { + "dynamic": "partial", + "boolean": true + }, + "template": "|{{* dynamic }}|", + "partials": { + "partial": "[]" + }, + "expected": "|[]|" + } + ] +} -- cgit v1.2.1 From b5ec88ffdde7fdc168a89aefeaa08a0f0051357b Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 6 Dec 2021 13:45:48 +0100 Subject: Update specs/dynamic.yml Co-authored-by: Julian Gonggrijp --- specs/dynamic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/dynamic.yml b/specs/dynamic.yml index 83e428e..1b2390c 100644 --- a/specs/dynamic.yml +++ b/specs/dynamic.yml @@ -71,7 +71,7 @@ tests: - name: Inline Indentation desc: Whitespace should be left untouched. data: { dynamic: 'partial', data: '|' } - template: " {{data}} {{* partial}}\n" + template: " {{data}} {{* dynamic}}\n" partials: { partial: ">\n>" } expected: " | >\n>\n" -- cgit v1.2.1 From 2c585cace9264b8543df44ca3ce56257425ac41c Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 6 Dec 2021 14:29:37 +0100 Subject: Update dynamic.yml Update dynamic.yml --- specs/dynamic.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/specs/dynamic.yml b/specs/dynamic.yml index 1b2390c..c61113e 100644 --- a/specs/dynamic.yml +++ b/specs/dynamic.yml @@ -17,7 +17,9 @@ overview: | Dynamic partial tags SHOULD be treated as standalone when appropriate. If this tag is used standalone, any whitespace preceding the tag should treated - as indentation, and prepended to each line of the partial before rendering. + as indentation, and prepended to each line of the partial before rendering: + whitespace handling around dynamic partials SHOULD be identical to whitespace + handling around static (normal) partials. tests: - name: Basic Behavior desc: The asterisk operator is used for dynamic partials. @@ -33,6 +35,13 @@ tests: partials: { } expected: '""' + - name: Context Misses Again + desc: Failed context lookups should be considered falsey. + data: { } + template: '"{{*missing}}"' + partials: { missing: 'Hello, world!' } + expected: '""' + - name: Failed Lookup desc: The empty string should be used when the named partial is not found. data: { dynamic: 'content' } @@ -56,20 +65,25 @@ tests: nodes: [ { content: 'Y', nodes: [] } ] } template: '{{*template}}' - partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' } + partials: { node: '{{content}}<{{#nodes}}{{*template}}{{/nodes}}>' } expected: 'X>' # Whitespace Sensitivity - name: Surrounding Whitespace - desc: The asterisk operator should not alter surrounding whitespace. + desc: | + The asterisk operator should not alter surrounding whitespace; any + whitespace preceding the tag should treated as indentation while any + whitepsace succeding the tag should be left untouched. data: { partial: 'foobar' } template: '| {{*partial}} |' partials: { foobar: "\t|\t" } expected: "| \t|\t |" - name: Inline Indentation - desc: Whitespace should be left untouched. + desc: | + Whitespace should be left untouched: whitespaces preceding the tag + should be treated as indentation. data: { dynamic: 'partial', data: '|' } template: " {{data}} {{* dynamic}}\n" partials: { partial: ">\n>" } -- cgit v1.2.1 From a15b333b08e3f0db5a62baf6d6d55f364f380a5a Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 6 Dec 2021 14:43:18 +0100 Subject: Renaming dynamic partials spec --- specs/dynamic.json | 147 ---------------------------------------- specs/dynamic.yml | 140 -------------------------------------- specs/~dynamic-partials.json | 157 +++++++++++++++++++++++++++++++++++++++++++ specs/~dynamic-partials.yml | 140 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 297 insertions(+), 287 deletions(-) delete mode 100644 specs/dynamic.json delete mode 100644 specs/dynamic.yml create mode 100644 specs/~dynamic-partials.json create mode 100644 specs/~dynamic-partials.yml diff --git a/specs/dynamic.json b/specs/dynamic.json deleted file mode 100644 index 8fa5cf4..0000000 --- a/specs/dynamic.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "overview": "Dynamic partials tags are used to dynamically expand an external template into\nthe current template.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names a key in the context whose value is the name of the\npartial that will be loaded. If the dynamically named partial cannot be found,\nthe empty string SHOULD be used instead, as in interpolations. Set Delimiter\ntags MUST NOT affect the parsing of a partial. The partial MUST be rendered\nagainst the context stack local to the tag. Failed resolutions of the key\n(context lookups) should be considered falsey and should interpolate as the\nempty string. If the partial, whose name is retrieved from the context stack,\ncannot be found, the empty string SHOULD be used instead, as in\ninterpolations.\n\nDynamic partial tags SHOULD be treated as standalone when appropriate. If\nthis tag is used standalone, any whitespace preceding the tag should treated\nas indentation, and prepended to each line of the partial before rendering.\n", - "tests": [ - { - "name": "Basic Behavior", - "desc": "The asterisk operator is used for dynamic partials.", - "data": { - "dynamic": "content" - }, - "template": "\"{{*dynamic}}\"", - "partials": { - "content": "Hello, world!" - }, - "expected": "\"Hello, world!\"" - }, - { - "name": "Context Misses", - "desc": "Failed context lookups should be considered falsey.", - "data": {}, - "template": "\"{{*missing}}\"", - "partials": {}, - "expected": "\"\"" - }, - { - "name": "Failed Lookup", - "desc": "The empty string should be used when the named partial is not found.", - "data": { - "dynamic": "content" - }, - "template": "\"{{*dynamic}}\"", - "partials": { - "foobar": "Hello, world!" - }, - "expected": "\"\"" - }, - { - "name": "Context", - "desc": "The asterisk operator should operate within the current context.", - "data": { - "text": "Hello, world!", - "example": "partial" - }, - "template": "\"{{*example}}\"", - "partials": { - "partial": "*{{text}}*" - }, - "expected": "\"*Hello, world!*\"" - }, - { - "name": "Recursion", - "desc": "The asterisk operator should properly recurse.", - "data": "{\n template: 'node',\n content: 'X',\n nodes: [ { content: 'Y', nodes: [] } ]\n}\n", - "template": "{{*template}}", - "partials": { - "node": "{{content}}<{{#nodes}}{{>node}}{{/nodes}}>" - }, - "expected": "X>" - }, - { - "name": "Surrounding Whitespace", - "desc": "The asterisk operator should not alter surrounding whitespace.", - "data": { - "partial": "foobar" - }, - "template": "| {{*partial}} |", - "partials": { - "foobar": "\t|\t" - }, - "expected": "| \t|\t |" - }, - { - "name": "Inline Indentation", - "desc": "Whitespace should be left untouched.", - "data": { - "dynamic": "partial", - "data": "|" - }, - "template": " {{data}} {{* partial}}\n", - "partials": { - "partial": ">\n>" - }, - "expected": " | >\n>\n" - }, - { - "name": "Standalone Line Endings", - "desc": "\"\\r\\n\" should be considered a newline for standalone tags.", - "data": { - "dynamic": "partial" - }, - "template": "|\r\n{{*dynamic}}\r\n|", - "partials": { - "partial": ">" - }, - "expected": "|\r\n>|" - }, - { - "name": "Standalone Without Previous Line", - "desc": "Standalone tags should not require a newline to precede them.", - "data": { - "dynamic": "partial" - }, - "template": " {{*dynamic}}\n>", - "partials": { - "partial": ">\n>" - }, - "expected": " >\n >>" - }, - { - "name": "Standalone Without Newline", - "desc": "Standalone tags should not require a newline to follow them.", - "data": { - "dynamic": "partial" - }, - "template": ">\n {{*dynamic}}", - "partials": { - "partial": ">\n>" - }, - "expected": ">\n >\n >" - }, - { - "name": "Standalone Indentation", - "desc": "Each line of the partial should be indented before rendering.", - "data": { - "dynamic": "partial", - "content": "<\n->" - }, - "template": "\\\n {{*dynamic}}\n/\n", - "partials": { - "partial": "|\n{{{content}}}\n|\n" - }, - "expected": "\\\n |\n <\n->\n |\n/\n" - }, - { - "name": "Padding Whitespace", - "desc": "Superfluous in-tag whitespace should be ignored.", - "data": { - "dynamic": "partial", - "boolean": true - }, - "template": "|{{* dynamic }}|", - "partials": { - "partial": "[]" - }, - "expected": "|[]|" - } - ] -} diff --git a/specs/dynamic.yml b/specs/dynamic.yml deleted file mode 100644 index c61113e..0000000 --- a/specs/dynamic.yml +++ /dev/null @@ -1,140 +0,0 @@ -overview: | - Dynamic partials tags are used to dynamically expand an external template into - the current template. - - The tag's content MUST be a non-whitespace character sequence NOT containing - the current closing delimiter. - - This tag's content names a key in the context whose value is the name of the - partial that will be loaded. If the dynamically named partial cannot be found, - the empty string SHOULD be used instead, as in interpolations. Set Delimiter - tags MUST NOT affect the parsing of a partial. The partial MUST be rendered - against the context stack local to the tag. Failed resolutions of the key - (context lookups) should be considered falsey and should interpolate as the - empty string. If the partial, whose name is retrieved from the context stack, - cannot be found, the empty string SHOULD be used instead, as in - interpolations. - - Dynamic partial tags SHOULD be treated as standalone when appropriate. If - this tag is used standalone, any whitespace preceding the tag should treated - as indentation, and prepended to each line of the partial before rendering: - whitespace handling around dynamic partials SHOULD be identical to whitespace - handling around static (normal) partials. -tests: - - name: Basic Behavior - desc: The asterisk operator is used for dynamic partials. - data: { dynamic: 'content' } - template: '"{{*dynamic}}"' - partials: { content: 'Hello, world!' } - expected: '"Hello, world!"' - - - name: Context Misses - desc: Failed context lookups should be considered falsey. - data: { } - template: '"{{*missing}}"' - partials: { } - expected: '""' - - - name: Context Misses Again - desc: Failed context lookups should be considered falsey. - data: { } - template: '"{{*missing}}"' - partials: { missing: 'Hello, world!' } - expected: '""' - - - name: Failed Lookup - desc: The empty string should be used when the named partial is not found. - data: { dynamic: 'content' } - template: '"{{*dynamic}}"' - partials: { foobar: 'Hello, world!' } - expected: '""' - - - name: Context - desc: The asterisk operator should operate within the current context. - data: { text: 'Hello, world!', example: 'partial' } - template: '"{{*example}}"' - partials: { partial: '*{{text}}*' } - expected: '"*Hello, world!*"' - - - name: Recursion - desc: The asterisk operator should properly recurse. - data: | - { - template: 'node', - content: 'X', - nodes: [ { content: 'Y', nodes: [] } ] - } - template: '{{*template}}' - partials: { node: '{{content}}<{{#nodes}}{{*template}}{{/nodes}}>' } - expected: 'X>' - - # Whitespace Sensitivity - - - name: Surrounding Whitespace - desc: | - The asterisk operator should not alter surrounding whitespace; any - whitespace preceding the tag should treated as indentation while any - whitepsace succeding the tag should be left untouched. - data: { partial: 'foobar' } - template: '| {{*partial}} |' - partials: { foobar: "\t|\t" } - expected: "| \t|\t |" - - - name: Inline Indentation - desc: | - Whitespace should be left untouched: whitespaces preceding the tag - should be treated as indentation. - data: { dynamic: 'partial', data: '|' } - template: " {{data}} {{* dynamic}}\n" - partials: { partial: ">\n>" } - expected: " | >\n>\n" - - - name: Standalone Line Endings - desc: '"\r\n" should be considered a newline for standalone tags.' - data: { dynamic: 'partial' } - template: "|\r\n{{*dynamic}}\r\n|" - partials: { partial: ">" } - expected: "|\r\n>|" - - - name: Standalone Without Previous Line - desc: Standalone tags should not require a newline to precede them. - data: { dynamic: 'partial' } - template: " {{*dynamic}}\n>" - partials: { partial: ">\n>"} - expected: " >\n >>" - - - name: Standalone Without Newline - desc: Standalone tags should not require a newline to follow them. - data: { dynamic: 'partial' } - template: ">\n {{*dynamic}}" - partials: { partial: ">\n>" } - expected: ">\n >\n >" - - - name: Standalone Indentation - desc: Each line of the partial should be indented before rendering. - data: { dynamic: 'partial', content: "<\n->" } - template: | - \ - {{*dynamic}} - / - partials: - partial: | - | - {{{content}}} - | - expected: | - \ - | - < - -> - | - / - - # Whitespace Insensitivity - - - name: Padding Whitespace - desc: Superfluous in-tag whitespace should be ignored. - data: { dynamic: 'partial', boolean: true } - template: "|{{* dynamic }}|" - partials: { partial: "[]" } - expected: '|[]|' diff --git a/specs/~dynamic-partials.json b/specs/~dynamic-partials.json new file mode 100644 index 0000000..f260a15 --- /dev/null +++ b/specs/~dynamic-partials.json @@ -0,0 +1,157 @@ +{ + "overview": "Dynamic partials tags are used to dynamically expand an external template into\nthe current template.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names a key in the context whose value is the name of the\npartial that will be loaded. If the dynamically named partial cannot be found,\nthe empty string SHOULD be used instead, as in interpolations. Set Delimiter\ntags MUST NOT affect the parsing of a partial. The partial MUST be rendered\nagainst the context stack local to the tag. Failed resolutions of the key\n(context lookups) should be considered falsey and should interpolate as the\nempty string. If the partial, whose name is retrieved from the context stack,\ncannot be found, the empty string SHOULD be used instead, as in\ninterpolations.\n\nDynamic partial tags SHOULD be treated as standalone when appropriate. If\nthis tag is used standalone, any whitespace preceding the tag should treated\nas indentation, and prepended to each line of the partial before rendering:\nwhitespace handling around dynamic partials SHOULD be identical to whitespace\nhandling around static (normal) partials.\n", + "tests": [ + { + "name": "Basic Behavior", + "desc": "The asterisk operator is used for dynamic partials.", + "data": { + "dynamic": "content" + }, + "template": "\"{{*dynamic}}\"", + "partials": { + "content": "Hello, world!" + }, + "expected": "\"Hello, world!\"" + }, + { + "name": "Context Misses", + "desc": "Failed context lookups should be considered falsey.", + "data": {}, + "template": "\"{{*missing}}\"", + "partials": {}, + "expected": "\"\"" + }, + { + "name": "Context Misses Again", + "desc": "Failed context lookups should be considered falsey.", + "data": {}, + "template": "\"{{*missing}}\"", + "partials": { + "missing": "Hello, world!" + }, + "expected": "\"\"" + }, + { + "name": "Failed Lookup", + "desc": "The empty string should be used when the named partial is not found.", + "data": { + "dynamic": "content" + }, + "template": "\"{{*dynamic}}\"", + "partials": { + "foobar": "Hello, world!" + }, + "expected": "\"\"" + }, + { + "name": "Context", + "desc": "The asterisk operator should operate within the current context.", + "data": { + "text": "Hello, world!", + "example": "partial" + }, + "template": "\"{{*example}}\"", + "partials": { + "partial": "*{{text}}*" + }, + "expected": "\"*Hello, world!*\"" + }, + { + "name": "Recursion", + "desc": "The asterisk operator should properly recurse.", + "data": "{\n template: 'node',\n content: 'X',\n nodes: [ { content: 'Y', nodes: [] } ]\n}\n", + "template": "{{*template}}", + "partials": { + "node": "{{content}}<{{#nodes}}{{*template}}{{/nodes}}>" + }, + "expected": "X>" + }, + { + "name": "Surrounding Whitespace", + "desc": "The asterisk operator should not alter surrounding whitespace; any\nwhitespace preceding the tag should treated as indentation while any\nwhitepsace succeding the tag should be left untouched.\n", + "data": { + "partial": "foobar" + }, + "template": "| {{*partial}} |", + "partials": { + "foobar": "\t|\t" + }, + "expected": "| \t|\t |" + }, + { + "name": "Inline Indentation", + "desc": "Whitespace should be left untouched: whitespaces preceding the tag\nshould be treated as indentation.\n", + "data": { + "dynamic": "partial", + "data": "|" + }, + "template": " {{data}} {{* dynamic}}\n", + "partials": { + "partial": ">\n>" + }, + "expected": " | >\n>\n" + }, + { + "name": "Standalone Line Endings", + "desc": "\"\\r\\n\" should be considered a newline for standalone tags.", + "data": { + "dynamic": "partial" + }, + "template": "|\r\n{{*dynamic}}\r\n|", + "partials": { + "partial": ">" + }, + "expected": "|\r\n>|" + }, + { + "name": "Standalone Without Previous Line", + "desc": "Standalone tags should not require a newline to precede them.", + "data": { + "dynamic": "partial" + }, + "template": " {{*dynamic}}\n>", + "partials": { + "partial": ">\n>" + }, + "expected": " >\n >>" + }, + { + "name": "Standalone Without Newline", + "desc": "Standalone tags should not require a newline to follow them.", + "data": { + "dynamic": "partial" + }, + "template": ">\n {{*dynamic}}", + "partials": { + "partial": ">\n>" + }, + "expected": ">\n >\n >" + }, + { + "name": "Standalone Indentation", + "desc": "Each line of the partial should be indented before rendering.", + "data": { + "dynamic": "partial", + "content": "<\n->" + }, + "template": "\\\n {{*dynamic}}\n/\n", + "partials": { + "partial": "|\n{{{content}}}\n|\n" + }, + "expected": "\\\n |\n <\n->\n |\n/\n" + }, + { + "name": "Padding Whitespace", + "desc": "Superfluous in-tag whitespace should be ignored.", + "data": { + "dynamic": "partial", + "boolean": true + }, + "template": "|{{* dynamic }}|", + "partials": { + "partial": "[]" + }, + "expected": "|[]|" + } + ] +} diff --git a/specs/~dynamic-partials.yml b/specs/~dynamic-partials.yml new file mode 100644 index 0000000..c61113e --- /dev/null +++ b/specs/~dynamic-partials.yml @@ -0,0 +1,140 @@ +overview: | + Dynamic partials tags are used to dynamically expand an external template into + the current template. + + The tag's content MUST be a non-whitespace character sequence NOT containing + the current closing delimiter. + + This tag's content names a key in the context whose value is the name of the + partial that will be loaded. If the dynamically named partial cannot be found, + the empty string SHOULD be used instead, as in interpolations. Set Delimiter + tags MUST NOT affect the parsing of a partial. The partial MUST be rendered + against the context stack local to the tag. Failed resolutions of the key + (context lookups) should be considered falsey and should interpolate as the + empty string. If the partial, whose name is retrieved from the context stack, + cannot be found, the empty string SHOULD be used instead, as in + interpolations. + + Dynamic partial tags SHOULD be treated as standalone when appropriate. If + this tag is used standalone, any whitespace preceding the tag should treated + as indentation, and prepended to each line of the partial before rendering: + whitespace handling around dynamic partials SHOULD be identical to whitespace + handling around static (normal) partials. +tests: + - name: Basic Behavior + desc: The asterisk operator is used for dynamic partials. + data: { dynamic: 'content' } + template: '"{{*dynamic}}"' + partials: { content: 'Hello, world!' } + expected: '"Hello, world!"' + + - name: Context Misses + desc: Failed context lookups should be considered falsey. + data: { } + template: '"{{*missing}}"' + partials: { } + expected: '""' + + - name: Context Misses Again + desc: Failed context lookups should be considered falsey. + data: { } + template: '"{{*missing}}"' + partials: { missing: 'Hello, world!' } + expected: '""' + + - name: Failed Lookup + desc: The empty string should be used when the named partial is not found. + data: { dynamic: 'content' } + template: '"{{*dynamic}}"' + partials: { foobar: 'Hello, world!' } + expected: '""' + + - name: Context + desc: The asterisk operator should operate within the current context. + data: { text: 'Hello, world!', example: 'partial' } + template: '"{{*example}}"' + partials: { partial: '*{{text}}*' } + expected: '"*Hello, world!*"' + + - name: Recursion + desc: The asterisk operator should properly recurse. + data: | + { + template: 'node', + content: 'X', + nodes: [ { content: 'Y', nodes: [] } ] + } + template: '{{*template}}' + partials: { node: '{{content}}<{{#nodes}}{{*template}}{{/nodes}}>' } + expected: 'X>' + + # Whitespace Sensitivity + + - name: Surrounding Whitespace + desc: | + The asterisk operator should not alter surrounding whitespace; any + whitespace preceding the tag should treated as indentation while any + whitepsace succeding the tag should be left untouched. + data: { partial: 'foobar' } + template: '| {{*partial}} |' + partials: { foobar: "\t|\t" } + expected: "| \t|\t |" + + - name: Inline Indentation + desc: | + Whitespace should be left untouched: whitespaces preceding the tag + should be treated as indentation. + data: { dynamic: 'partial', data: '|' } + template: " {{data}} {{* dynamic}}\n" + partials: { partial: ">\n>" } + expected: " | >\n>\n" + + - name: Standalone Line Endings + desc: '"\r\n" should be considered a newline for standalone tags.' + data: { dynamic: 'partial' } + template: "|\r\n{{*dynamic}}\r\n|" + partials: { partial: ">" } + expected: "|\r\n>|" + + - name: Standalone Without Previous Line + desc: Standalone tags should not require a newline to precede them. + data: { dynamic: 'partial' } + template: " {{*dynamic}}\n>" + partials: { partial: ">\n>"} + expected: " >\n >>" + + - name: Standalone Without Newline + desc: Standalone tags should not require a newline to follow them. + data: { dynamic: 'partial' } + template: ">\n {{*dynamic}}" + partials: { partial: ">\n>" } + expected: ">\n >\n >" + + - name: Standalone Indentation + desc: Each line of the partial should be indented before rendering. + data: { dynamic: 'partial', content: "<\n->" } + template: | + \ + {{*dynamic}} + / + partials: + partial: | + | + {{{content}}} + | + expected: | + \ + | + < + -> + | + / + + # Whitespace Insensitivity + + - name: Padding Whitespace + desc: Superfluous in-tag whitespace should be ignored. + data: { dynamic: 'partial', boolean: true } + template: "|{{* dynamic }}|" + partials: { partial: "[]" } + expected: '|[]|' -- cgit v1.2.1 From 22c3bf1ad7983360e4f20b953cd3ab88d941d5d7 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Sun, 17 Jul 2022 12:01:16 +0200 Subject: Dynamic partials update (squashed) (coauthored) Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp --- specs/~dynamic-names.json | 176 +++++++++++++++++++++++++++++++++++++++++++ specs/~dynamic-names.yml | 158 ++++++++++++++++++++++++++++++++++++++ specs/~dynamic-partials.json | 157 -------------------------------------- specs/~dynamic-partials.yml | 140 ---------------------------------- 4 files changed, 334 insertions(+), 297 deletions(-) create mode 100644 specs/~dynamic-names.json create mode 100644 specs/~dynamic-names.yml delete mode 100644 specs/~dynamic-partials.json delete mode 100644 specs/~dynamic-partials.yml diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json new file mode 100644 index 0000000..57c1665 --- /dev/null +++ b/specs/~dynamic-names.json @@ -0,0 +1,176 @@ +{ + "overview": "Dynamic Names are a special way to dynamically refer to a tag name.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The name resolution is\nas follows:\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, the data is the value returned by the\n method with the given name.\n 5) 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.\nThe resolved data should be coerced into a string before being used as\ncontent.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be rendered against the context stack local to the tag.\nFailed resolutions of the key (context lookups) should be considered falsey\nand should interpolate as the empty string.\n\nDynamic Names can be combined with every other tag: those tags' content is the\nresolution of the Dynamic Name. Dynamic Names cannot be resolved more than\nonce (Dynamic Names cannot be nested).\n", + "tests": [ + { + "name": "Basic Behavior - Interpolation", + "desc": "The asterisk operator is used for dynamic names.", + "data": { + "dynamic": "Hello, world!" + }, + "template": "\"{{*dynamic}}\"", + "partials": {}, + "expected": "\"Hello, world!\"" + }, + { + "name": "Basic Behavior - Partial", + "desc": "The asterisk operator is used for dynamic partials.", + "data": { + "dynamic": "content" + }, + "template": "\"{{>*dynamic}}\"", + "partials": { + "content": "Hello, world!" + }, + "expected": "\"Hello, world!\"" + }, + { + "name": "Context Misses - Interpolation", + "desc": "Failed context lookups should be considered falsey.", + "data": {}, + "template": "\"{{*missing}}\"", + "partials": {}, + "expected": "\"\"" + }, + { + "name": "Context Misses - Partial", + "desc": "Failed context lookups should be considered falsey.", + "data": {}, + "template": "\"{{>*missing}}\"", + "partials": { + "missing": "Hello, world!" + }, + "expected": "\"\"" + }, + { + "name": "Failed Lookup - Partial", + "desc": "The empty string should be used when the named partial is not found.", + "data": { + "dynamic": "content" + }, + "template": "\"{{>*dynamic}}\"", + "partials": { + "foobar": "Hello, world!" + }, + "expected": "\"\"" + }, + { + "name": "Context", + "desc": "The asterisk operator should operate within the current context.", + "data": { + "text": "Hello, world!", + "example": "partial" + }, + "template": "\"{{*example}}\"", + "partials": { + "partial": "*{{text}}*" + }, + "expected": "\"*Hello, world!*\"" + }, + { + "name": "Recursion", + "desc": "The asterisk operator should properly recurse.", + "data": { + "template": "node", + "content": "X", + "nodes": [ + { + "content": "Y", + "nodes": [] + } + ] + }, + "template": "{{*template}}", + "partials": { + "node": "{{content}}<{{#nodes}}{{*template}}{{/nodes}}>" + }, + "expected": "X>" + }, + { + "name": "Surrounding Whitespace", + "desc": "The asterisk operator should not alter surrounding whitespace; any\nwhitespace preceding the tag should treated as indentation while any\nwhitepsace succeding the tag should be left untouched.\n", + "data": { + "partial": "foobar" + }, + "template": "| {{*partial}} |", + "partials": { + "foobar": "\t|\t" + }, + "expected": "| \t|\t |" + }, + { + "name": "Inline Indentation", + "desc": "Whitespace should be left untouched: whitespaces preceding the tag\nshould be treated as indentation.\n", + "data": { + "dynamic": "partial", + "data": "|" + }, + "template": " {{data}} {{* dynamic}}\n", + "partials": { + "partial": ">\n>" + }, + "expected": " | >\n>\n" + }, + { + "name": "Standalone Line Endings", + "desc": "\"\\r\\n\" should be considered a newline for standalone tags.", + "data": { + "dynamic": "partial" + }, + "template": "|\r\n{{*dynamic}}\r\n|", + "partials": { + "partial": ">" + }, + "expected": "|\r\n>|" + }, + { + "name": "Standalone Without Previous Line", + "desc": "Standalone tags should not require a newline to precede them.", + "data": { + "dynamic": "partial" + }, + "template": " {{*dynamic}}\n>", + "partials": { + "partial": ">\n>" + }, + "expected": " >\n >>" + }, + { + "name": "Standalone Without Newline", + "desc": "Standalone tags should not require a newline to follow them.", + "data": { + "dynamic": "partial" + }, + "template": ">\n {{*dynamic}}", + "partials": { + "partial": ">\n>" + }, + "expected": ">\n >\n >" + }, + { + "name": "Standalone Indentation", + "desc": "Each line of the partial should be indented before rendering.", + "data": { + "dynamic": "partial", + "content": "<\n->" + }, + "template": "\\\n {{*dynamic}}\n/\n", + "partials": { + "partial": "|\n{{{content}}}\n|\n" + }, + "expected": "\\\n |\n <\n->\n |\n/\n" + }, + { + "name": "Padding Whitespace", + "desc": "Superfluous in-tag whitespace should be ignored.", + "data": { + "dynamic": "partial", + "boolean": true + }, + "template": "|{{* dynamic }}|", + "partials": { + "partial": "[]" + }, + "expected": "|[]|" + } + ] +} diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml new file mode 100644 index 0000000..3005907 --- /dev/null +++ b/specs/~dynamic-names.yml @@ -0,0 +1,158 @@ +overview: | + Dynamic Names are a special notation to dynamically determine a tag's content. + + Dynamic Names MUST be a non-whitespace character sequence NOT containing + the current closing delimiter. A Dynamic Name consists of an asterisk, + followed by a dotted name. The latter follows the same notation as in an + Interpolation tag. + + This tag's content refers to a key in the context whose value will be used in + place of the Dynamic Name itself as content of the tag. The name resolution is + identical to name resolution in Interpolation tags, 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, the data is the value returned by the + method with the given name. + 5) 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. + The resolved data should be coerced into a string before being used as + content. + + Set Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The + Dynamic Names MUST be resolved against the context stack local to the tag. + Failed resolution of the dynamic name should result in nothing being rendered. + + Engines that implement Dynamic Names MUST support their use in Partial tags. + In engines that also implement the optional inheritance spec, Dynamic Names + inside Parent tags should be supported as well. Dynamic Names cannot be + resolved more than once (Dynamic Names cannot be nested). +tests: + - name: Basic Behavior - Interpolation + desc: The asterisk operator is used for dynamic names. + data: { dynamic: 'Hello, world!' } + template: '"{{*dynamic}}"' + partials: { } + expected: '"Hello, world!"' + + - name: Basic Behavior - Partial + desc: The asterisk operator is used for dynamic partials. + data: { dynamic: 'content' } + template: '"{{>*dynamic}}"' + partials: { content: 'Hello, world!' } + expected: '"Hello, world!"' + + - name: Context Misses - Interpolation + desc: Failed context lookups should be considered falsey. + data: { } + template: '"{{*missing}}"' + partials: { } + expected: '""' + + - name: Context Misses - Partial + desc: Failed context lookups should be considered falsey. + data: { } + template: '"{{>*missing}}"' + partials: { missing: 'Hello, world!' } + expected: '""' + + - name: Failed Lookup - Partial + desc: The empty string should be used when the named partial is not found. + data: { dynamic: 'content' } + template: '"{{>*dynamic}}"' + partials: { foobar: 'Hello, world!' } + expected: '""' + + - name: Context + desc: The dynamic partial should operate within the current context. + data: { text: 'Hello, world!', example: 'partial' } + template: '"{{>*example}}"' + partials: { partial: '*{{text}}*' } + expected: '"*Hello, world!*"' + + - name: Recursion + desc: Dynamic partials should properly recurse. + data: + template: 'node' + content: 'X' + nodes: [ { content: 'Y', nodes: [] } ] + template: '{{>*template}}' + partials: { node: '{{content}}<{{#nodes}}{{>*template}}{{/nodes}}>' } + expected: 'X>' + + # Whitespace Sensitivity + + - name: Surrounding Whitespace + desc: | + A dynamic partials should not alter surrounding whitespace; any + whitespace preceding the tag should treated as indentation while any + whitepsace succeding the tag should be left untouched. + data: { partial: 'foobar' } + template: '| {{>*partial}} |' + partials: { foobar: "\t|\t" } + expected: "| \t|\t |" + + - name: Inline Indentation + desc: | + Whitespace should be left untouched: whitespaces preceding the tag + should be treated as indentation. + data: { dynamic: 'partial', data: '|' } + template: " {{data}} {{>* dynamic}}\n" + partials: { partial: ">\n>" } + expected: " | >\n>\n" + + - name: Standalone Line Endings + desc: '"\r\n" should be considered a newline for standalone tags.' + data: { dynamic: 'partial' } + template: "|\r\n{{>*dynamic}}\r\n|" + partials: { partial: ">" } + expected: "|\r\n>|" + + - name: Standalone Without Previous Line + desc: Standalone tags should not require a newline to precede them. + data: { dynamic: 'partial' } + template: " {{>*dynamic}}\n>" + partials: { partial: ">\n>"} + expected: " >\n >>" + + - name: Standalone Without Newline + desc: Standalone tags should not require a newline to follow them. + data: { dynamic: 'partial' } + template: ">\n {{>*dynamic}}" + partials: { partial: ">\n>" } + expected: ">\n >\n >" + + - name: Standalone Indentation + desc: Each line of the partial should be indented before rendering. + data: { dynamic: 'partial', content: "<\n->" } + template: | + \ + {{>*dynamic}} + / + partials: + partial: | + | + {{{content}}} + | + expected: | + \ + | + < + -> + | + / + + # Whitespace Insensitivity + + - name: Padding Whitespace + desc: Superfluous in-tag whitespace should be ignored. + data: { dynamic: 'partial', boolean: true } + template: "|{{> *dynamic }}|" + partials: { partial: "[]" } + expected: '|[]|' diff --git a/specs/~dynamic-partials.json b/specs/~dynamic-partials.json deleted file mode 100644 index f260a15..0000000 --- a/specs/~dynamic-partials.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "overview": "Dynamic partials tags are used to dynamically expand an external template into\nthe current template.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names a key in the context whose value is the name of the\npartial that will be loaded. If the dynamically named partial cannot be found,\nthe empty string SHOULD be used instead, as in interpolations. Set Delimiter\ntags MUST NOT affect the parsing of a partial. The partial MUST be rendered\nagainst the context stack local to the tag. Failed resolutions of the key\n(context lookups) should be considered falsey and should interpolate as the\nempty string. If the partial, whose name is retrieved from the context stack,\ncannot be found, the empty string SHOULD be used instead, as in\ninterpolations.\n\nDynamic partial tags SHOULD be treated as standalone when appropriate. If\nthis tag is used standalone, any whitespace preceding the tag should treated\nas indentation, and prepended to each line of the partial before rendering:\nwhitespace handling around dynamic partials SHOULD be identical to whitespace\nhandling around static (normal) partials.\n", - "tests": [ - { - "name": "Basic Behavior", - "desc": "The asterisk operator is used for dynamic partials.", - "data": { - "dynamic": "content" - }, - "template": "\"{{*dynamic}}\"", - "partials": { - "content": "Hello, world!" - }, - "expected": "\"Hello, world!\"" - }, - { - "name": "Context Misses", - "desc": "Failed context lookups should be considered falsey.", - "data": {}, - "template": "\"{{*missing}}\"", - "partials": {}, - "expected": "\"\"" - }, - { - "name": "Context Misses Again", - "desc": "Failed context lookups should be considered falsey.", - "data": {}, - "template": "\"{{*missing}}\"", - "partials": { - "missing": "Hello, world!" - }, - "expected": "\"\"" - }, - { - "name": "Failed Lookup", - "desc": "The empty string should be used when the named partial is not found.", - "data": { - "dynamic": "content" - }, - "template": "\"{{*dynamic}}\"", - "partials": { - "foobar": "Hello, world!" - }, - "expected": "\"\"" - }, - { - "name": "Context", - "desc": "The asterisk operator should operate within the current context.", - "data": { - "text": "Hello, world!", - "example": "partial" - }, - "template": "\"{{*example}}\"", - "partials": { - "partial": "*{{text}}*" - }, - "expected": "\"*Hello, world!*\"" - }, - { - "name": "Recursion", - "desc": "The asterisk operator should properly recurse.", - "data": "{\n template: 'node',\n content: 'X',\n nodes: [ { content: 'Y', nodes: [] } ]\n}\n", - "template": "{{*template}}", - "partials": { - "node": "{{content}}<{{#nodes}}{{*template}}{{/nodes}}>" - }, - "expected": "X>" - }, - { - "name": "Surrounding Whitespace", - "desc": "The asterisk operator should not alter surrounding whitespace; any\nwhitespace preceding the tag should treated as indentation while any\nwhitepsace succeding the tag should be left untouched.\n", - "data": { - "partial": "foobar" - }, - "template": "| {{*partial}} |", - "partials": { - "foobar": "\t|\t" - }, - "expected": "| \t|\t |" - }, - { - "name": "Inline Indentation", - "desc": "Whitespace should be left untouched: whitespaces preceding the tag\nshould be treated as indentation.\n", - "data": { - "dynamic": "partial", - "data": "|" - }, - "template": " {{data}} {{* dynamic}}\n", - "partials": { - "partial": ">\n>" - }, - "expected": " | >\n>\n" - }, - { - "name": "Standalone Line Endings", - "desc": "\"\\r\\n\" should be considered a newline for standalone tags.", - "data": { - "dynamic": "partial" - }, - "template": "|\r\n{{*dynamic}}\r\n|", - "partials": { - "partial": ">" - }, - "expected": "|\r\n>|" - }, - { - "name": "Standalone Without Previous Line", - "desc": "Standalone tags should not require a newline to precede them.", - "data": { - "dynamic": "partial" - }, - "template": " {{*dynamic}}\n>", - "partials": { - "partial": ">\n>" - }, - "expected": " >\n >>" - }, - { - "name": "Standalone Without Newline", - "desc": "Standalone tags should not require a newline to follow them.", - "data": { - "dynamic": "partial" - }, - "template": ">\n {{*dynamic}}", - "partials": { - "partial": ">\n>" - }, - "expected": ">\n >\n >" - }, - { - "name": "Standalone Indentation", - "desc": "Each line of the partial should be indented before rendering.", - "data": { - "dynamic": "partial", - "content": "<\n->" - }, - "template": "\\\n {{*dynamic}}\n/\n", - "partials": { - "partial": "|\n{{{content}}}\n|\n" - }, - "expected": "\\\n |\n <\n->\n |\n/\n" - }, - { - "name": "Padding Whitespace", - "desc": "Superfluous in-tag whitespace should be ignored.", - "data": { - "dynamic": "partial", - "boolean": true - }, - "template": "|{{* dynamic }}|", - "partials": { - "partial": "[]" - }, - "expected": "|[]|" - } - ] -} diff --git a/specs/~dynamic-partials.yml b/specs/~dynamic-partials.yml deleted file mode 100644 index c61113e..0000000 --- a/specs/~dynamic-partials.yml +++ /dev/null @@ -1,140 +0,0 @@ -overview: | - Dynamic partials tags are used to dynamically expand an external template into - the current template. - - The tag's content MUST be a non-whitespace character sequence NOT containing - the current closing delimiter. - - This tag's content names a key in the context whose value is the name of the - partial that will be loaded. If the dynamically named partial cannot be found, - the empty string SHOULD be used instead, as in interpolations. Set Delimiter - tags MUST NOT affect the parsing of a partial. The partial MUST be rendered - against the context stack local to the tag. Failed resolutions of the key - (context lookups) should be considered falsey and should interpolate as the - empty string. If the partial, whose name is retrieved from the context stack, - cannot be found, the empty string SHOULD be used instead, as in - interpolations. - - Dynamic partial tags SHOULD be treated as standalone when appropriate. If - this tag is used standalone, any whitespace preceding the tag should treated - as indentation, and prepended to each line of the partial before rendering: - whitespace handling around dynamic partials SHOULD be identical to whitespace - handling around static (normal) partials. -tests: - - name: Basic Behavior - desc: The asterisk operator is used for dynamic partials. - data: { dynamic: 'content' } - template: '"{{*dynamic}}"' - partials: { content: 'Hello, world!' } - expected: '"Hello, world!"' - - - name: Context Misses - desc: Failed context lookups should be considered falsey. - data: { } - template: '"{{*missing}}"' - partials: { } - expected: '""' - - - name: Context Misses Again - desc: Failed context lookups should be considered falsey. - data: { } - template: '"{{*missing}}"' - partials: { missing: 'Hello, world!' } - expected: '""' - - - name: Failed Lookup - desc: The empty string should be used when the named partial is not found. - data: { dynamic: 'content' } - template: '"{{*dynamic}}"' - partials: { foobar: 'Hello, world!' } - expected: '""' - - - name: Context - desc: The asterisk operator should operate within the current context. - data: { text: 'Hello, world!', example: 'partial' } - template: '"{{*example}}"' - partials: { partial: '*{{text}}*' } - expected: '"*Hello, world!*"' - - - name: Recursion - desc: The asterisk operator should properly recurse. - data: | - { - template: 'node', - content: 'X', - nodes: [ { content: 'Y', nodes: [] } ] - } - template: '{{*template}}' - partials: { node: '{{content}}<{{#nodes}}{{*template}}{{/nodes}}>' } - expected: 'X>' - - # Whitespace Sensitivity - - - name: Surrounding Whitespace - desc: | - The asterisk operator should not alter surrounding whitespace; any - whitespace preceding the tag should treated as indentation while any - whitepsace succeding the tag should be left untouched. - data: { partial: 'foobar' } - template: '| {{*partial}} |' - partials: { foobar: "\t|\t" } - expected: "| \t|\t |" - - - name: Inline Indentation - desc: | - Whitespace should be left untouched: whitespaces preceding the tag - should be treated as indentation. - data: { dynamic: 'partial', data: '|' } - template: " {{data}} {{* dynamic}}\n" - partials: { partial: ">\n>" } - expected: " | >\n>\n" - - - name: Standalone Line Endings - desc: '"\r\n" should be considered a newline for standalone tags.' - data: { dynamic: 'partial' } - template: "|\r\n{{*dynamic}}\r\n|" - partials: { partial: ">" } - expected: "|\r\n>|" - - - name: Standalone Without Previous Line - desc: Standalone tags should not require a newline to precede them. - data: { dynamic: 'partial' } - template: " {{*dynamic}}\n>" - partials: { partial: ">\n>"} - expected: " >\n >>" - - - name: Standalone Without Newline - desc: Standalone tags should not require a newline to follow them. - data: { dynamic: 'partial' } - template: ">\n {{*dynamic}}" - partials: { partial: ">\n>" } - expected: ">\n >\n >" - - - name: Standalone Indentation - desc: Each line of the partial should be indented before rendering. - data: { dynamic: 'partial', content: "<\n->" } - template: | - \ - {{*dynamic}} - / - partials: - partial: | - | - {{{content}}} - | - expected: | - \ - | - < - -> - | - / - - # Whitespace Insensitivity - - - name: Padding Whitespace - desc: Superfluous in-tag whitespace should be ignored. - data: { dynamic: 'partial', boolean: true } - template: "|{{* dynamic }}|" - partials: { partial: "[]" } - expected: '|[]|' -- cgit v1.2.1 From b32a4aef1fae82cb08c0b6c8e2e7584ef5ceb12d Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 18 Jul 2022 11:07:52 +0200 Subject: Removing interpolation example and adding JSON spec Removing interpolation test --- specs/~dynamic-names.json | 46 ++++++++++++++-------------------------------- specs/~dynamic-names.yml | 14 -------------- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 57c1665..03f1c7a 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,16 +1,6 @@ { - "overview": "Dynamic Names are a special way to dynamically refer to a tag name.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The name resolution is\nas follows:\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, the data is the value returned by the\n method with the given name.\n 5) 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.\nThe resolved data should be coerced into a string before being used as\ncontent.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be rendered against the context stack local to the tag.\nFailed resolutions of the key (context lookups) should be considered falsey\nand should interpolate as the empty string.\n\nDynamic Names can be combined with every other tag: those tags' content is the\nresolution of the Dynamic Name. Dynamic Names cannot be resolved more than\nonce (Dynamic Names cannot be nested).\n", + "overview": "Dynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's content refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The name resolution is\nidentical to name resolution in Interpolation tags, as follows:\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, the data is the value returned by the\n method with the given name.\n 5) 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.\nThe resolved data should be coerced into a string before being used as\ncontent.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ - { - "name": "Basic Behavior - Interpolation", - "desc": "The asterisk operator is used for dynamic names.", - "data": { - "dynamic": "Hello, world!" - }, - "template": "\"{{*dynamic}}\"", - "partials": {}, - "expected": "\"Hello, world!\"" - }, { "name": "Basic Behavior - Partial", "desc": "The asterisk operator is used for dynamic partials.", @@ -23,14 +13,6 @@ }, "expected": "\"Hello, world!\"" }, - { - "name": "Context Misses - Interpolation", - "desc": "Failed context lookups should be considered falsey.", - "data": {}, - "template": "\"{{*missing}}\"", - "partials": {}, - "expected": "\"\"" - }, { "name": "Context Misses - Partial", "desc": "Failed context lookups should be considered falsey.", @@ -55,12 +37,12 @@ }, { "name": "Context", - "desc": "The asterisk operator should operate within the current context.", + "desc": "The dynamic partial should operate within the current context.", "data": { "text": "Hello, world!", "example": "partial" }, - "template": "\"{{*example}}\"", + "template": "\"{{>*example}}\"", "partials": { "partial": "*{{text}}*" }, @@ -68,7 +50,7 @@ }, { "name": "Recursion", - "desc": "The asterisk operator should properly recurse.", + "desc": "Dynamic partials should properly recurse.", "data": { "template": "node", "content": "X", @@ -79,19 +61,19 @@ } ] }, - "template": "{{*template}}", + "template": "{{>*template}}", "partials": { - "node": "{{content}}<{{#nodes}}{{*template}}{{/nodes}}>" + "node": "{{content}}<{{#nodes}}{{>*template}}{{/nodes}}>" }, "expected": "X>" }, { "name": "Surrounding Whitespace", - "desc": "The asterisk operator should not alter surrounding whitespace; any\nwhitespace preceding the tag should treated as indentation while any\nwhitepsace succeding the tag should be left untouched.\n", + "desc": "A dynamic partials should not alter surrounding whitespace; any\nwhitespace preceding the tag should treated as indentation while any\nwhitepsace succeding the tag should be left untouched.\n", "data": { "partial": "foobar" }, - "template": "| {{*partial}} |", + "template": "| {{>*partial}} |", "partials": { "foobar": "\t|\t" }, @@ -104,7 +86,7 @@ "dynamic": "partial", "data": "|" }, - "template": " {{data}} {{* dynamic}}\n", + "template": " {{data}} {{>* dynamic}}\n", "partials": { "partial": ">\n>" }, @@ -116,7 +98,7 @@ "data": { "dynamic": "partial" }, - "template": "|\r\n{{*dynamic}}\r\n|", + "template": "|\r\n{{>*dynamic}}\r\n|", "partials": { "partial": ">" }, @@ -128,7 +110,7 @@ "data": { "dynamic": "partial" }, - "template": " {{*dynamic}}\n>", + "template": " {{>*dynamic}}\n>", "partials": { "partial": ">\n>" }, @@ -140,7 +122,7 @@ "data": { "dynamic": "partial" }, - "template": ">\n {{*dynamic}}", + "template": ">\n {{>*dynamic}}", "partials": { "partial": ">\n>" }, @@ -153,7 +135,7 @@ "dynamic": "partial", "content": "<\n->" }, - "template": "\\\n {{*dynamic}}\n/\n", + "template": "\\\n {{>*dynamic}}\n/\n", "partials": { "partial": "|\n{{{content}}}\n|\n" }, @@ -166,7 +148,7 @@ "dynamic": "partial", "boolean": true }, - "template": "|{{* dynamic }}|", + "template": "|{{> *dynamic }}|", "partials": { "partial": "[]" }, diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 3005907..5923b13 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -34,13 +34,6 @@ overview: | inside Parent tags should be supported as well. Dynamic Names cannot be resolved more than once (Dynamic Names cannot be nested). tests: - - name: Basic Behavior - Interpolation - desc: The asterisk operator is used for dynamic names. - data: { dynamic: 'Hello, world!' } - template: '"{{*dynamic}}"' - partials: { } - expected: '"Hello, world!"' - - name: Basic Behavior - Partial desc: The asterisk operator is used for dynamic partials. data: { dynamic: 'content' } @@ -48,13 +41,6 @@ tests: partials: { content: 'Hello, world!' } expected: '"Hello, world!"' - - name: Context Misses - Interpolation - desc: Failed context lookups should be considered falsey. - data: { } - template: '"{{*missing}}"' - partials: { } - expected: '""' - - name: Context Misses - Partial desc: Failed context lookups should be considered falsey. data: { } -- cgit v1.2.1 From c1449c20794609c21794d163a17b97370af67b92 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Wed, 20 Jul 2022 16:47:05 +0200 Subject: Dotted names and context stacking tests --- specs/~dynamic-names.json | 109 ++++++++++++++++++++++++++++++++++++++++++++++ specs/~dynamic-names.yml | 61 ++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 03f1c7a..c6aade6 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -48,6 +48,115 @@ }, "expected": "\"*Hello, world!*\"" }, + { + "name": "Dotted Names", + "desc": "The dynamic partial should operate within the current context.", + "data": { + "text": "Hello, world!", + "foo": { + "bar": { + "baz": "partial" + } + } + }, + "template": "\"{{>*foo.bar.baz}}\"", + "partials": { + "partial": "*{{text}}*" + }, + "expected": "\"*Hello, world!*\"" + }, + { + "name": "Dotted Names - Failed Lookup", + "desc": "The dynamic partial should operate within the current context.", + "data": { + "text": "Hello, world!", + "foo": "test", + "test": { + "bar": { + "baz": "partial" + } + } + }, + "template": "\"{{>*foo.bar.baz}}\"", + "partials": { + "partial": "*{{text}}*" + }, + "expected": "\"\"" + }, + { + "name": "Dotted Names - Failed Lookup", + "desc": "The dynamic partial should operate within the current context.", + "data": { + "foo": { + "text": "Hello, world!", + "bar": { + "baz": "partial" + } + } + }, + "template": "\"{{>*foo.bar.baz}}\"", + "partials": { + "partial": "*{{text}}*" + }, + "expected": "\"**\"" + }, + { + "name": "Dotted names - Context Stacking", + "desc": "Dotted names should not push a new frame on the context stack.", + "data": { + "section1": { + "value": "section1" + }, + "section2": { + "dynamic": "partial", + "value": "section2" + } + }, + "template": "{{#section1}}{{>*section2.dynamic}}{{/section1}}", + "partials": { + "partial": "{{value}}" + }, + "expected": "\"section1\"" + }, + { + "name": "Dotted names - Context Stacking Under Repetition", + "desc": "Dotted names should not push a new frame on the context stack.", + "data": { + "value": "test", + "section1": [ + 1, + 2 + ], + "section2": { + "dynamic": "partial", + "value": "section2" + } + }, + "template": "{{#section1}}{{>*section2.dynamic}}{{/section1}}", + "partials": { + "partial": "{{value}}" + }, + "expected": "testtest" + }, + { + "name": "Dotted names - Context Stacking Failed Lookup", + "desc": "Dotted names should resolve against the proper context stack.", + "data": { + "section1": [ + 1, + 2 + ], + "section2": { + "dynamic": "partial", + "value": "section2" + } + }, + "template": "{{#section1}}{{>*section2.dynamic}}{{/section1}}", + "partials": { + "partial": "\"{{value}}\"" + }, + "expected": "\"\"" + }, { "name": "Recursion", "desc": "Dynamic partials should properly recurse.", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 5923b13..a74cfc2 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -62,6 +62,67 @@ tests: partials: { partial: '*{{text}}*' } expected: '"*Hello, world!*"' + - name: Dotted Names + desc: The dynamic partial should operate within the current context. + data: { text: 'Hello, world!', foo: { bar: { baz: 'partial' } } } + template: '"{{>*foo.bar.baz}}"' + partials: { partial: '*{{text}}*' } + expected: '"*Hello, world!*"' + + - name: Dotted Names - Failed Lookup + desc: The dynamic partial should operate within the current context. + data: + text: 'Hello, world!' + foo: 'test' + test: + bar: + baz: 'partial' + template: '"{{>*foo.bar.baz}}"' + partials: { partial: '*{{text}}*' } + expected: '""' + + - name: Dotted Names - Failed Lookup + desc: The dynamic partial should operate within the current context. + data: + foo: + text: 'Hello, world!' + bar: + baz: 'partial' + template: '"{{>*foo.bar.baz}}"' + partials: { partial: '*{{text}}*' } + expected: '"**"' + + - name: Dotted names - Context Stacking + desc: Dotted names should not push a new frame on the context stack. + data: + section1: { value: 'section1' } + section2: { dynamic: 'partial', value: 'section2' } + template: "{{#section1}}{{>*section2.dynamic}}{{/section1}}" + partials: + partial: "{{value}}" + expected: '"section1"' + + - name: Dotted names - Context Stacking Under Repetition + desc: Dotted names should not push a new frame on the context stack. + data: + value: 'test' + section1: [ 1, 2 ] + section2: { dynamic: 'partial', value: 'section2' } + template: "{{#section1}}{{>*section2.dynamic}}{{/section1}}" + partials: + partial: "{{value}}" + expected: "testtest" + + - name: Dotted names - Context Stacking Failed Lookup + desc: Dotted names should resolve against the proper context stack. + data: + section1: [ 1, 2 ] + section2: { dynamic: 'partial', value: 'section2' } + template: "{{#section1}}{{>*section2.dynamic}}{{/section1}}" + partials: + partial: '"{{value}}"' + expected: '""' + - name: Recursion desc: Dynamic partials should properly recurse. data: -- cgit v1.2.1 From 7e356c558a3250a475a4a5824ea5ae2c165352ab Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Wed, 20 Jul 2022 17:37:14 +0200 Subject: Fixing typo on test result --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index c6aade6..dead87d 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -114,7 +114,7 @@ }, "template": "{{#section1}}{{>*section2.dynamic}}{{/section1}}", "partials": { - "partial": "{{value}}" + "partial": "\"{{value}}\"" }, "expected": "\"section1\"" }, diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index a74cfc2..87a2c5f 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -99,7 +99,7 @@ tests: section2: { dynamic: 'partial', value: 'section2' } template: "{{#section1}}{{>*section2.dynamic}}{{/section1}}" partials: - partial: "{{value}}" + partial: '"{{value}}"' expected: '"section1"' - name: Dotted names - Context Stacking Under Repetition -- cgit v1.2.1 From d7edba39661a134cd8bdee8d4cdc147b41a5104d Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Wed, 20 Jul 2022 17:43:52 +0200 Subject: Fixing test result and updating the spec (squashed) (coauthored) Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index dead87d..3f3931b 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -155,7 +155,7 @@ "partials": { "partial": "\"{{value}}\"" }, - "expected": "\"\"" + "expected": "\"\"\"\"" }, { "name": "Recursion", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 87a2c5f..6c2a2d4 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -33,6 +33,7 @@ overview: | In engines that also implement the optional inheritance spec, Dynamic Names inside Parent tags should be supported as well. Dynamic Names cannot be resolved more than once (Dynamic Names cannot be nested). + tests: - name: Basic Behavior - Partial desc: The asterisk operator is used for dynamic partials. @@ -69,7 +70,7 @@ tests: partials: { partial: '*{{text}}*' } expected: '"*Hello, world!*"' - - name: Dotted Names - Failed Lookup + - name: Dotted Names - Operator Precedence desc: The dynamic partial should operate within the current context. data: text: 'Hello, world!' @@ -121,7 +122,7 @@ tests: template: "{{#section1}}{{>*section2.dynamic}}{{/section1}}" partials: partial: '"{{value}}"' - expected: '""' + expected: '""""' - name: Recursion desc: Dynamic partials should properly recurse. @@ -137,9 +138,9 @@ tests: - name: Surrounding Whitespace desc: | - A dynamic partials should not alter surrounding whitespace; any - whitespace preceding the tag should treated as indentation while any - whitepsace succeding the tag should be left untouched. + A dynamic partial should not alter surrounding whitespace; any + whitespace preceding the tag should be treated as indentation while any + whitespace succeding the tag should be left untouched. data: { partial: 'foobar' } template: '| {{>*partial}} |' partials: { foobar: "\t|\t" } -- cgit v1.2.1 From 69487d554800a8de5bc1390e636a7290af25f015 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Wed, 20 Jul 2022 21:46:35 +0200 Subject: Editing tag description and fixing whitespaces in tests --- specs/~dynamic-names.json | 10 +++++----- specs/~dynamic-names.yml | 10 ++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 3f3931b..8c325d0 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Dynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's content refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The name resolution is\nidentical to name resolution in Interpolation tags, as follows:\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, the data is the value returned by the\n method with the given name.\n 5) 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.\nThe resolved data should be coerced into a string before being used as\ncontent.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Dynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nThis tag's content refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The name resolution is\nidentical to name resolution in Interpolation tags, as follows:\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, the data is the value returned by the\n method with the given name.\n 5) 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.\nThe resolved data should be coerced into a string before being used as\ncontent.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", @@ -66,7 +66,7 @@ "expected": "\"*Hello, world!*\"" }, { - "name": "Dotted Names - Failed Lookup", + "name": "Dotted Names - Operator Precedence", "desc": "The dynamic partial should operate within the current context.", "data": { "text": "Hello, world!", @@ -178,7 +178,7 @@ }, { "name": "Surrounding Whitespace", - "desc": "A dynamic partials should not alter surrounding whitespace; any\nwhitespace preceding the tag should treated as indentation while any\nwhitepsace succeding the tag should be left untouched.\n", + "desc": "A dynamic partial should not alter surrounding whitespace; any\nwhitespace preceding the tag should be treated as indentation while any\nwhitespace succeding the tag should be left untouched.\n", "data": { "partial": "foobar" }, @@ -195,7 +195,7 @@ "dynamic": "partial", "data": "|" }, - "template": " {{data}} {{>* dynamic}}\n", + "template": " {{data}} {{>*dynamic}}\n", "partials": { "partial": ">\n>" }, @@ -257,7 +257,7 @@ "dynamic": "partial", "boolean": true }, - "template": "|{{> *dynamic }}|", + "template": "|{{> * dynamic }}|", "partials": { "partial": "[]" }, diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 6c2a2d4..062fbcd 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -6,6 +6,12 @@ overview: | followed by a dotted name. The latter follows the same notation as in an Interpolation tag. + This tag's dotted name, which is equal to the Dynamic Name excluding the + leading asterisk, refers to a key in the context whose value will be used in + place of the Dynamic Name itself as content of the tag. The dotted name + resolution produces the same value as an Interpolation tag and does not affect + the context for further processing. + This tag's content refers to a key in the context whose value will be used in place of the Dynamic Name itself as content of the tag. The name resolution is identical to name resolution in Interpolation tags, as follows: @@ -151,7 +157,7 @@ tests: Whitespace should be left untouched: whitespaces preceding the tag should be treated as indentation. data: { dynamic: 'partial', data: '|' } - template: " {{data}} {{>* dynamic}}\n" + template: " {{data}} {{>*dynamic}}\n" partials: { partial: ">\n>" } expected: " | >\n>\n" @@ -201,6 +207,6 @@ tests: - name: Padding Whitespace desc: Superfluous in-tag whitespace should be ignored. data: { dynamic: 'partial', boolean: true } - template: "|{{> *dynamic }}|" + template: "|{{> * dynamic }}|" partials: { partial: "[]" } expected: '|[]|' -- cgit v1.2.1 From 956cfa41988cc1b59892a495d5072adaa3791e38 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Wed, 20 Jul 2022 22:58:53 +0200 Subject: Removing redundant paragraph --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 19 ------------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 8c325d0..93ea278 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Dynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nThis tag's content refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The name resolution is\nidentical to name resolution in Interpolation tags, as follows:\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, the data is the value returned by the\n method with the given name.\n 5) 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.\nThe resolved data should be coerced into a string before being used as\ncontent.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Dynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 062fbcd..a9bf913 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -12,25 +12,6 @@ overview: | resolution produces the same value as an Interpolation tag and does not affect the context for further processing. - This tag's content refers to a key in the context whose value will be used in - place of the Dynamic Name itself as content of the tag. The name resolution is - identical to name resolution in Interpolation tags, 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, the data is the value returned by the - method with the given name. - 5) 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. - The resolved data should be coerced into a string before being used as - content. - Set Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The Dynamic Names MUST be resolved against the context stack local to the tag. Failed resolution of the dynamic name should result in nothing being rendered. -- cgit v1.2.1 From 6e9b75d723b081048dd5dd863c31cd650a67b2a0 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:45:23 +0200 Subject: adding rationale and a new test --- specs/~dynamic-names.json | 16 +++++++++++++++- specs/~dynamic-names.yml | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 93ea278..84a5473 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Dynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced specifically to allow the\ndynamic loading of partials. Although there are already solutions that allow\nto dynamically load partials possible, these solutions either require the use\nof optional (and complex) features such as lambdas, or require mechanisms that\nin interpreted programming languages are rather tedious to implement and are\nalso rather inefficient, both in terms of space, due to a possible necessary\noverhead of the included partials, and in terms of computational efficiency,\nregarding a possible necessary pre-buffering.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", @@ -13,6 +13,20 @@ }, "expected": "\"Hello, world!\"" }, + { + "name": "Basic Behavior - Name Resolution", + "desc": "The asterisk is not part of the name that will be resolved in the contex.\n", + "data": { + "dynamic": "content", + "*dynamic": "wrong" + }, + "template": "\"{{>*dynamic}}\"", + "partials": { + "content": "Hello, world!", + "wrong": "Invisible" + }, + "expected": "\"Hello, world!\"" + }, { "name": "Context Misses - Partial", "desc": "Failed context lookups should be considered falsey.", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index a9bf913..c7bf7ab 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -1,4 +1,13 @@ overview: | + Rationale: this special notation was introduced specifically to allow the + dynamic loading of partials. Although there are already solutions that allow + to dynamically load partials possible, these solutions either require the use + of optional (and complex) features such as lambdas, or require mechanisms that + in interpreted programming languages are rather tedious to implement and are + also rather inefficient, both in terms of space, due to a possible necessary + overhead of the included partials, and in terms of computational efficiency, + regarding a possible necessary pre-buffering. + Dynamic Names are a special notation to dynamically determine a tag's content. Dynamic Names MUST be a non-whitespace character sequence NOT containing @@ -29,6 +38,14 @@ tests: partials: { content: 'Hello, world!' } expected: '"Hello, world!"' + - name: Basic Behavior - Name Resolution + desc: | + The asterisk is not part of the name that will be resolved in the contex. + data: { dynamic: 'content', '*dynamic': 'wrong' } + template: '"{{>*dynamic}}"' + partials: { content: 'Hello, world!', wrong: 'Invisible' } + expected: '"Hello, world!"' + - name: Context Misses - Partial desc: Failed context lookups should be considered falsey. data: { } -- cgit v1.2.1 From dcc145e50cd966da90b4b506b18359245613ec72 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Fri, 22 Jul 2022 11:55:28 +0200 Subject: Updating rationale (squashed) (coauthored) Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 84a5473..9c80402 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced specifically to allow the\ndynamic loading of partials. Although there are already solutions that allow\nto dynamically load partials possible, these solutions either require the use\nof optional (and complex) features such as lambdas, or require mechanisms that\nin interpreted programming languages are rather tedious to implement and are\nalso rather inefficient, both in terms of space, due to a possible necessary\noverhead of the included partials, and in terms of computational efficiency,\nregarding a possible necessary pre-buffering.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced specifically to allow the\ndynamic loading of partials. The main advantage that this new special notation\noffers is to allow dynamic loading of partials, which is particularly useful\nin cases where there needs to be rendered polymorphic data or in cases where\nthere is a child template shared by multiple parent templates; cases which\notherwise would be possible to render only by using either complex (and\noptional) features such as lambdas, or by using solutions that are inefficient\nboth in terms of space and in terms of computational efficiency, such as:\noverloading the template with if blocks or preprocessing the template.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index c7bf7ab..d93fe4f 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -1,12 +1,13 @@ overview: | Rationale: this special notation was introduced specifically to allow the - dynamic loading of partials. Although there are already solutions that allow - to dynamically load partials possible, these solutions either require the use - of optional (and complex) features such as lambdas, or require mechanisms that - in interpreted programming languages are rather tedious to implement and are - also rather inefficient, both in terms of space, due to a possible necessary - overhead of the included partials, and in terms of computational efficiency, - regarding a possible necessary pre-buffering. + dynamic loading of partials. The main advantage that this notation + offers is to allow dynamic loading of partials, which is particularly useful + in cases where polymorphic data needs to be rendered, or in cases where + a child template is shared by multiple parent templates; cases which + otherwise would be possible to render only by using either complex (and + optional) features such as lambdas, or by using solutions that are inefficient + both in terms of space and in terms of computational efficiency, such as: + overloading the template with if blocks or preprocessing the template. Dynamic Names are a special notation to dynamically determine a tag's content. @@ -15,15 +16,15 @@ overview: | followed by a dotted name. The latter follows the same notation as in an Interpolation tag. - This tag's dotted name, which is equal to the Dynamic Name excluding the - leading asterisk, refers to a key in the context whose value will be used in + This tag's dotted name, which is the Dynamic Name excluding the + leading asterisk, references a key in the context whose value will be used in place of the Dynamic Name itself as content of the tag. The dotted name resolution produces the same value as an Interpolation tag and does not affect the context for further processing. Set Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The Dynamic Names MUST be resolved against the context stack local to the tag. - Failed resolution of the dynamic name should result in nothing being rendered. + Failed resolution of the dynamic name SHOULD result in nothing being rendered. Engines that implement Dynamic Names MUST support their use in Partial tags. In engines that also implement the optional inheritance spec, Dynamic Names -- cgit v1.2.1 From 736472c77b398771c711f91e62b4feb05bd7f5ea Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 25 Jul 2022 19:40:17 +0200 Subject: Update specs/~dynamic-names.yml Co-authored-by: Justin Hileman --- specs/~dynamic-names.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index d93fe4f..a1177e7 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -28,7 +28,7 @@ overview: | Engines that implement Dynamic Names MUST support their use in Partial tags. In engines that also implement the optional inheritance spec, Dynamic Names - inside Parent tags should be supported as well. Dynamic Names cannot be + inside Parent tags SHOULD be supported as well. Dynamic Names cannot be resolved more than once (Dynamic Names cannot be nested). tests: -- cgit v1.2.1 From 2030fff3d513b93f527711afc7467a89cab88737 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 25 Jul 2022 20:20:39 +0200 Subject: Adding two more tests, updating operator precedence desc + typo fix --- specs/~dynamic-names.json | 37 ++++++++++++++++++++++++++++++++++--- specs/~dynamic-names.yml | 20 +++++++++++++++++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 9c80402..194162d 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced specifically to allow the\ndynamic loading of partials. The main advantage that this new special notation\noffers is to allow dynamic loading of partials, which is particularly useful\nin cases where there needs to be rendered polymorphic data or in cases where\nthere is a child template shared by multiple parent templates; cases which\notherwise would be possible to render only by using either complex (and\noptional) features such as lambdas, or by using solutions that are inefficient\nboth in terms of space and in terms of computational efficiency, such as:\noverloading the template with if blocks or preprocessing the template.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The latter follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is equal to the Dynamic Name excluding the\nleading asterisk, refers to a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name should result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags should be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced specifically to allow the\ndynamic loading of partials. The main advantage that this notation\noffers is to allow dynamic loading of partials, which is particularly useful\nin cases where polymorphic data needs to be rendered, or in cases where\na child template is shared by multiple parent templates; cases which\notherwise would be possible to render only by using either complex (and\noptional) features such as lambdas, or by using solutions that are inefficient\nboth in terms of space and in terms of computational efficiency, such as:\noverloading the template with if blocks or preprocessing the template.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", @@ -15,7 +15,7 @@ }, { "name": "Basic Behavior - Name Resolution", - "desc": "The asterisk is not part of the name that will be resolved in the contex.\n", + "desc": "The asterisk is not part of the name that will be resolved in the context.\n", "data": { "dynamic": "content", "*dynamic": "wrong" @@ -81,7 +81,7 @@ }, { "name": "Dotted Names - Operator Precedence", - "desc": "The dynamic partial should operate within the current context.", + "desc": "The dotted name should be resolved entirely before being dereferenced.", "data": { "text": "Hello, world!", "foo": "test", @@ -190,6 +190,37 @@ }, "expected": "X>" }, + { + "name": "Dynamic Names - Dobule Dereferencing", + "desc": "Dynamic Names can't be dereferenced more than once.", + "data": { + "dynamic": "test", + "test": "content" + }, + "template": "\"{{>**dynamic}}\"", + "partials": { + "content": "Hello, world!" + }, + "expected": "\"\"" + }, + { + "name": "Dynamic Names - Composed Dereferencing", + "desc": "Dynamic Names cannot have composed dereferencing.", + "data": { + "foo": "fizz", + "bar": "buzz", + "fizz": { + "buzz": { + "content": null + } + } + }, + "template": "\"{{>*foo.*bar}}\"", + "partials": { + "content": "Hello, world!" + }, + "expected": "\"\"" + }, { "name": "Surrounding Whitespace", "desc": "A dynamic partial should not alter surrounding whitespace; any\nwhitespace preceding the tag should be treated as indentation while any\nwhitespace succeding the tag should be left untouched.\n", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index a1177e7..d60ef2b 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -13,7 +13,7 @@ overview: | Dynamic Names MUST be a non-whitespace character sequence NOT containing the current closing delimiter. A Dynamic Name consists of an asterisk, - followed by a dotted name. The latter follows the same notation as in an + followed by a dotted name. The dotted name follows the same notation as in an Interpolation tag. This tag's dotted name, which is the Dynamic Name excluding the @@ -41,7 +41,7 @@ tests: - name: Basic Behavior - Name Resolution desc: | - The asterisk is not part of the name that will be resolved in the contex. + The asterisk is not part of the name that will be resolved in the context. data: { dynamic: 'content', '*dynamic': 'wrong' } template: '"{{>*dynamic}}"' partials: { content: 'Hello, world!', wrong: 'Invisible' } @@ -76,7 +76,7 @@ tests: expected: '"*Hello, world!*"' - name: Dotted Names - Operator Precedence - desc: The dynamic partial should operate within the current context. + desc: The dotted name should be resolved entirely before being dereferenced. data: text: 'Hello, world!' foo: 'test' @@ -139,6 +139,20 @@ tests: partials: { node: '{{content}}<{{#nodes}}{{>*template}}{{/nodes}}>' } expected: 'X>' + - name: Dynamic Names - Dobule Dereferencing + desc: Dynamic Names can't be dereferenced more than once. + data: { dynamic: 'test', 'test': 'content' } + template: '"{{>**dynamic}}"' + partials: { content: 'Hello, world!' } + expected: '""' + + - name: Dynamic Names - Composed Dereferencing + desc: Dynamic Names cannot have composed dereferencing. + data: { foo: 'fizz', bar: 'buzz', fizz: { buzz: { 'content' } } } + template: '"{{>*foo.*bar}}"' + partials: { content: 'Hello, world!' } + expected: '""' + # Whitespace Sensitivity - name: Surrounding Whitespace -- cgit v1.2.1 From 80395e474812871fecf35af476153f6b304dacc6 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 25 Jul 2022 23:51:35 +0200 Subject: Updating rationale. --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 194162d..d019d4f 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced specifically to allow the\ndynamic loading of partials. The main advantage that this notation\noffers is to allow dynamic loading of partials, which is particularly useful\nin cases where polymorphic data needs to be rendered, or in cases where\na child template is shared by multiple parent templates; cases which\notherwise would be possible to render only by using either complex (and\noptional) features such as lambdas, or by using solutions that are inefficient\nboth in terms of space and in terms of computational efficiency, such as:\noverloading the template with if blocks or preprocessing the template.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced primarly to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways, or in cases where a\npartial template needs to be included by multiple parent templates; cases\nwhich would otherwise be possible to render only with solutions that are\nconvoluted, inefficient, or both.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index d60ef2b..7307fc3 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -1,13 +1,11 @@ overview: | - Rationale: this special notation was introduced specifically to allow the - dynamic loading of partials. The main advantage that this notation - offers is to allow dynamic loading of partials, which is particularly useful - in cases where polymorphic data needs to be rendered, or in cases where - a child template is shared by multiple parent templates; cases which - otherwise would be possible to render only by using either complex (and - optional) features such as lambdas, or by using solutions that are inefficient - both in terms of space and in terms of computational efficiency, such as: - overloading the template with if blocks or preprocessing the template. + Rationale: this special notation was introduced primarly to allow the dynamic + loading of partials. The main advantage that this notation offers is to allow + dynamic loading of partials, which is particularly useful in cases where + polymorphic data needs to be rendered in different ways, or in cases where a + partial template needs to be included by multiple parent templates; cases + which would otherwise be possible to render only with solutions that are + convoluted, inefficient, or both. Dynamic Names are a special notation to dynamically determine a tag's content. -- cgit v1.2.1 From f297e363465fee3421b0d2ecac7d603481de4596 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 25 Jul 2022 22:20:58 +0200 Subject: Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp --- specs/~dynamic-names.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 7307fc3..7783f3f 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -145,7 +145,7 @@ tests: expected: '""' - name: Dynamic Names - Composed Dereferencing - desc: Dynamic Names cannot have composed dereferencing. + desc: Dotted Names are resolved entirely before dereferencing begins. data: { foo: 'fizz', bar: 'buzz', fizz: { buzz: { 'content' } } } template: '"{{>*foo.*bar}}"' partials: { content: 'Hello, world!' } -- cgit v1.2.1 From f72af176915f21d227c3c3c5c4da344842d37f6e Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Mon, 25 Jul 2022 23:56:19 +0200 Subject: Rebuilding JSON. --- specs/~dynamic-names.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index d019d4f..d1401bb 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -205,7 +205,7 @@ }, { "name": "Dynamic Names - Composed Dereferencing", - "desc": "Dynamic Names cannot have composed dereferencing.", + "desc": "Dotted Names are resolved entirely before dereferencing begins.", "data": { "foo": "fizz", "bar": "buzz", -- cgit v1.2.1 From 3f33715739cb3de3201bdccfcfc0e38cff50b820 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Tue, 26 Jul 2022 22:48:19 +0200 Subject: Adding examples to the rationale --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 135 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 6 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index d1401bb..044a41b 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced primarly to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways, or in cases where a\npartial template needs to be included by multiple parent templates; cases\nwhich would otherwise be possible to render only with solutions that are\nconvoluted, inefficient, or both.\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways, cases which would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways: the\nitems having a key named `content` should be rendered with the template\n`text.mustache` and the items having a key named `url` should be rendered\nwith the template `image.mustache`:\ntext.mustache:\n {{!image.mustache}}\n \nimage.mustache:\n {{!text.mustache}}\n {{content}}\nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\n## Using Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates should look\nlike this:\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\n## Using Lambdas\nThe idea is to inject into the data functions that will be later called from\nthe template.\nThis way the data will look like this:\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags tags in\nit.\n## Using If Blocks\nThe idea is to put some logic into the main template so it can dynamically\nload templates:\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\n## Using Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n+----------------+---------------------+-------------------------------------+\n| Approach | Pros | Cons |\n+----------------+---------------------+-------------------------------------+\n| Pre-Processing | Essential template, | Secondary templating system needed, |\n| | more control | slower rendering |\n| Lambdas | Slim template | Data tagging, logic in data |\n| If Blocks | No data overhead | Template linear growth |\n| Dynamic Names | Slim template | Data tagging |\n+----------------+---------------------+-------------------------------------+\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 7783f3f..27c4419 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -1,11 +1,136 @@ overview: | - Rationale: this special notation was introduced primarly to allow the dynamic + Rationale: this special notation was introduced primarily to allow the dynamic loading of partials. The main advantage that this notation offers is to allow dynamic loading of partials, which is particularly useful in cases where - polymorphic data needs to be rendered in different ways, or in cases where a - partial template needs to be included by multiple parent templates; cases - which would otherwise be possible to render only with solutions that are - convoluted, inefficient, or both. + polymorphic data needs to be rendered in different ways, cases which would + otherwise be possible to render only with solutions that are convoluted, + inefficient, or both. + Example. + Let's consider the following data: + items: [ + { content: 'Hello, World!' }, + { url: 'http://example.com/foo.jpg' }, + { content: 'Some text' }, + { content: 'Some other text' }, + { url: 'http://example.com/bar.jpg' }, + { url: 'http://example.com/baz.jpg' }, + { content: 'Last text here' } + ] + The goal is to render the different types of items in different ways: the + items having a key named `content` should be rendered with the template + `text.mustache` and the items having a key named `url` should be rendered + with the template `image.mustache`: + text.mustache: + {{!image.mustache}} + + image.mustache: + {{!text.mustache}} + {{content}} + There are already several ways to achieve this goal, here below are + illustrated and discussed the most significant solutions to this problem. + ## Using Pre-Processing + The idea is to use a secondary templating mechanism to dynamically generate + the template that will be rendered. + The template that our secondary templating mechanism generates should look + like this: + {{!template.mustache}} + {{items.1.content}} + + {{items.3.content}} + {{items.4.content}} + + + {{items.7.content}} + This solutions offers the advantages of having more control over the template + and minimizing the template blocks to the essential ones. + The drawbacks are the rendering speed and the complexity that the secondary + templating mechanism requires. + ## Using Lambdas + The idea is to inject into the data functions that will be later called from + the template. + This way the data will look like this: + items: [ + { + content: 'Hello, World!', + html: function() { return '{{>text}}'; } + }, + { + url: 'http://example.com/foo.jpg', + html: function() { return '{{>image}}'; } + }, + { + content: 'Some text', + html: function() { return '{{>text}}'; } + }, + { + content: 'Some other text', + html: function() { return '{{>text}}'; } + }, + { + url: 'http://example.com/bar.jpg', + html: function() { return '{{>image}}'; } + }, + { + url: 'http://example.com/baz.jpg', + html: function() { return '{{>image}}'; } + }, + { + content: 'Last text here', + html: function() { return '{{>text}}'; } + } + ] + And the template will look like this: + {{!template.mustache}} + {{#items}} + {{{html}}} + {{/items}} + The advantage this solution offers is to have a light main template. + The drawback is that the data needs to embed logic and template tags tags in + it. + ## Using If Blocks + The idea is to put some logic into the main template so it can dynamically + load templates: + {{!template.mustache}} + {{#items}} + {{#url}} + {{>image}} + {{/url}} + {{#content}} + {{>text}} + {{/content}} + {{/items}} + The main advantage of this solution is that it works without adding any + overhead fields to the data. + The drawback is that this solution isn't optimal for heterogeneous data sets + as the main template grows linearly with the number of polymorphic variants. + ## Using Dynamic Names + This is the solution proposed by this spec. + The idea is to load partials dynamically. + This way the data items have to be tagged with the corresponding partial name: + items: [ + { content: 'Hello, World!', dynamic: 'text' }, + { url: 'http://example.com/foo.jpg', dynamic: 'image' }, + { content: 'Some text', dynamic: 'text' }, + { content: 'Some other text', dynamic: 'text' }, + { url: 'http://example.com/bar.jpg', dynamic: 'image' }, + { url: 'http://example.com/baz.jpg', dynamic: 'image' }, + { content: 'Last text here', dynamic: 'text' } + ] + And the template would simple look like this: + {{!template.mustache}} + {{#items}} + {{>*dynamic}} + {{/items}} + Summary: + +----------------+---------------------+-------------------------------------+ + | Approach | Pros | Cons | + +----------------+---------------------+-------------------------------------+ + | Pre-Processing | Essential template, | Secondary templating system needed, | + | | more control | slower rendering | + | Lambdas | Slim template | Data tagging, logic in data | + | If Blocks | No data overhead | Template linear growth | + | Dynamic Names | Slim template | Data tagging | + +----------------+---------------------+-------------------------------------+ Dynamic Names are a special notation to dynamically determine a tag's content. -- cgit v1.2.1 From d17d6885f9b58b0faed4f5270b0e11e7eab34928 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Tue, 26 Jul 2022 22:51:26 +0200 Subject: Adding pro to the if blocks approach --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 044a41b..f4d0eab 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways, cases which would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways: the\nitems having a key named `content` should be rendered with the template\n`text.mustache` and the items having a key named `url` should be rendered\nwith the template `image.mustache`:\ntext.mustache:\n {{!image.mustache}}\n \nimage.mustache:\n {{!text.mustache}}\n {{content}}\nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\n## Using Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates should look\nlike this:\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\n## Using Lambdas\nThe idea is to inject into the data functions that will be later called from\nthe template.\nThis way the data will look like this:\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags tags in\nit.\n## Using If Blocks\nThe idea is to put some logic into the main template so it can dynamically\nload templates:\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\n## Using Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n+----------------+---------------------+-------------------------------------+\n| Approach | Pros | Cons |\n+----------------+---------------------+-------------------------------------+\n| Pre-Processing | Essential template, | Secondary templating system needed, |\n| | more control | slower rendering |\n| Lambdas | Slim template | Data tagging, logic in data |\n| If Blocks | No data overhead | Template linear growth |\n| Dynamic Names | Slim template | Data tagging |\n+----------------+---------------------+-------------------------------------+\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways, cases which would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways: the\nitems having a key named `content` should be rendered with the template\n`text.mustache` and the items having a key named `url` should be rendered\nwith the template `image.mustache`:\ntext.mustache:\n {{!image.mustache}}\n \nimage.mustache:\n {{!text.mustache}}\n {{content}}\nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\n## Using Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates should look\nlike this:\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\n## Using Lambdas\nThe idea is to inject into the data functions that will be later called from\nthe template.\nThis way the data will look like this:\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags tags in\nit.\n## Using If Blocks\nThe idea is to put some logic into the main template so it can dynamically\nload templates:\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\n## Using Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n+----------------+---------------------+-------------------------------------+\n| Approach | Pros | Cons |\n+----------------+---------------------+-------------------------------------+\n| Pre-Processing | Essential template, | Secondary templating system needed, |\n| | more control | slower rendering |\n| Lambdas | Slim template | Data tagging, logic in data |\n| If Blocks | No data overhead, | Template linear growth |\n| | self-documenting | |\n| Dynamic Names | Slim template | Data tagging |\n+----------------+---------------------+-------------------------------------+\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 27c4419..30edc39 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -128,7 +128,8 @@ overview: | | Pre-Processing | Essential template, | Secondary templating system needed, | | | more control | slower rendering | | Lambdas | Slim template | Data tagging, logic in data | - | If Blocks | No data overhead | Template linear growth | + | If Blocks | No data overhead, | Template linear growth | + | | self-documenting | | | Dynamic Names | Slim template | Data tagging | +----------------+---------------------+-------------------------------------+ -- cgit v1.2.1 From 7a4d919d9dc43453524477470a4f217d11b94e24 Mon Sep 17 00:00:00 2001 From: Julian Gonggrijp Date: Mon, 5 Jul 2021 15:48:29 +0200 Subject: Specify block scope resolution (#125) --- specs/~inheritance.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/specs/~inheritance.yml b/specs/~inheritance.yml index 07fa081..5a24ba8 100644 --- a/specs/~inheritance.yml +++ b/specs/~inheritance.yml @@ -224,3 +224,14 @@ tests: partials: parent: "{{$foo}}default content{{/foo}}" expected: default content + + - name: Block scope + desc: Scope of a substituted block is evaluated in the context of the parent template + data: + fruit: apples + nested: + fruit: bananas + template: "{{ Date: Mon, 5 Jul 2021 16:13:50 +0200 Subject: Update JSON accordingly --- specs/~inheritance.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/specs/~inheritance.json b/specs/~inheritance.json index fedb34b..0a6878b 100644 --- a/specs/~inheritance.json +++ b/specs/~inheritance.json @@ -230,6 +230,21 @@ "parent": "{{$foo}}default content{{/foo}}" }, "expected": "default content" + }, + { + "name": "Block scope", + "desc": "Scope of a substituted block is evaluated in the context of the parent template", + "data": { + "fruit": "apples", + "nested": { + "fruit": "bananas" + } + }, + "template": "{{ Date: Tue, 22 Mar 2022 08:36:44 +0000 Subject: Adding a test: comment content colliding with variable (squashed) (coauthored) Currently, an implementation treating comments as undefined variables successfully passes all tests, because both undefined variables and comments render to empty string. This clarifies the behavior: A comment MUST NOT render into anything, under any circumstances. This includes the case where a variable with the same name as the comment content is defined. The test data is designed in a way to trigger any possible name collision, including white spaces, a leading exclamation mark (!). Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp --- specs/comments.json | 12 ++++++++++++ specs/comments.yml | 6 ++++++ specs/~dynamic-names.yml | 49 ++++++++++++++++++++++++------------------------ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/specs/comments.json b/specs/comments.json index 60a4929..924ed46 100644 --- a/specs/comments.json +++ b/specs/comments.json @@ -89,6 +89,18 @@ }, "template": "12345 {{! Comment Block! }} 67890", "expected": "12345 67890" + }, + { + "name": "Variable Name Collision", + "desc": "Comments must never render, even if variable with same name exists.", + "data": { + "! comment": 1, + "! comment ": 2, + "!comment": 3, + "comment": 4 + }, + "template": "comments never show: >{{! comment }}<", + "expected": "comments never show: ><" } ] } diff --git a/specs/comments.yml b/specs/comments.yml index 7b14c7f..3bad09f 100644 --- a/specs/comments.yml +++ b/specs/comments.yml @@ -101,3 +101,9 @@ tests: data: { } template: '12345 {{! Comment Block! }} 67890' expected: '12345 67890' + + - name: Variable Name Collision + desc: Comments must never render, even if variable with same name exists. + data: { '! comment': 1, '! comment ': 2, '!comment': 3, 'comment': 4} + template: 'comments never show: >{{! comment }}<' + expected: 'comments never show: ><' diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 30edc39..d980717 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -2,36 +2,36 @@ overview: | Rationale: this special notation was introduced primarily to allow the dynamic loading of partials. The main advantage that this notation offers is to allow dynamic loading of partials, which is particularly useful in cases where - polymorphic data needs to be rendered in different ways, cases which would + polymorphic data needs to be rendered in different ways. Such cases would otherwise be possible to render only with solutions that are convoluted, inefficient, or both. Example. Let's consider the following data: - items: [ - { content: 'Hello, World!' }, - { url: 'http://example.com/foo.jpg' }, - { content: 'Some text' }, - { content: 'Some other text' }, - { url: 'http://example.com/bar.jpg' }, - { url: 'http://example.com/baz.jpg' }, - { content: 'Last text here' } - ] - The goal is to render the different types of items in different ways: the + items: [ + { content: 'Hello, World!' }, + { url: 'http://example.com/foo.jpg' }, + { content: 'Some text' }, + { content: 'Some other text' }, + { url: 'http://example.com/bar.jpg' }, + { url: 'http://example.com/baz.jpg' }, + { content: 'Last text here' } + ] + The goal is to render the different types of items in different ways. The items having a key named `content` should be rendered with the template `text.mustache` and the items having a key named `url` should be rendered with the template `image.mustache`: - text.mustache: - {{!image.mustache}} - - image.mustache: - {{!text.mustache}} - {{content}} + + {{!image.mustache}} + + + {{!text.mustache}} + {{content}} There are already several ways to achieve this goal, here below are illustrated and discussed the most significant solutions to this problem. ## Using Pre-Processing The idea is to use a secondary templating mechanism to dynamically generate the template that will be rendered. - The template that our secondary templating mechanism generates should look + The template that our secondary templating mechanism generates might look like this: {{!template.mustache}} {{items.1.content}} @@ -46,7 +46,7 @@ overview: | The drawbacks are the rendering speed and the complexity that the secondary templating mechanism requires. ## Using Lambdas - The idea is to inject into the data functions that will be later called from + The idea is to inject functions into the data that will be later called from the template. This way the data will look like this: items: [ @@ -85,11 +85,11 @@ overview: | {{{html}}} {{/items}} The advantage this solution offers is to have a light main template. - The drawback is that the data needs to embed logic and template tags tags in + The drawback is that the data needs to embed logic and template tags in it. - ## Using If Blocks - The idea is to put some logic into the main template so it can dynamically - load templates: + ## Using If-Else Blocks + The idea is to put some logic into the main template so it can select the + templates at rendering time: {{!template.mustache}} {{#items}} {{#url}} @@ -100,7 +100,8 @@ overview: | {{/content}} {{/items}} The main advantage of this solution is that it works without adding any - overhead fields to the data. + overhead fields to the data. It also documents which external templates are + appropriate for expansion in this position. The drawback is that this solution isn't optimal for heterogeneous data sets as the main template grows linearly with the number of polymorphic variants. ## Using Dynamic Names -- cgit v1.2.1 From 9dc776c8fb3fb092515129da86daf96989566bac Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Wed, 27 Jul 2022 20:29:25 +0200 Subject: Removing leading hashtags and fixing spacing --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index f4d0eab..8b4c8af 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways, cases which would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways: the\nitems having a key named `content` should be rendered with the template\n`text.mustache` and the items having a key named `url` should be rendered\nwith the template `image.mustache`:\ntext.mustache:\n {{!image.mustache}}\n \nimage.mustache:\n {{!text.mustache}}\n {{content}}\nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\n## Using Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates should look\nlike this:\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\n## Using Lambdas\nThe idea is to inject into the data functions that will be later called from\nthe template.\nThis way the data will look like this:\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags tags in\nit.\n## Using If Blocks\nThe idea is to put some logic into the main template so it can dynamically\nload templates:\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\n## Using Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n+----------------+---------------------+-------------------------------------+\n| Approach | Pros | Cons |\n+----------------+---------------------+-------------------------------------+\n| Pre-Processing | Essential template, | Secondary templating system needed, |\n| | more control | slower rendering |\n| Lambdas | Slim template | Data tagging, logic in data |\n| If Blocks | No data overhead, | Template linear growth |\n| | self-documenting | |\n| Dynamic Names | Slim template | Data tagging |\n+----------------+---------------------+-------------------------------------+\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways. Such cases would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways. The\nitems having a key named `content` should be rendered with the template\n`text.mustache`:\n {{!image.mustache}}\n \nAnd the items having a key named `url` should be rendered with the template\n`image.mustache`:\n {{!text.mustache}}\n {{content}}\nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\nUsing Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates might look\nlike this:\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\nUsing Lambdas\nThe idea is to inject functions into the data that will be later called from\nthe template.\nThis way the data will look like this:\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags in\nit.\nUsing If-Else Blocks\nThe idea is to put some logic into the main template so it can select the\ntemplates at rendering time:\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data. It also documents which external templates are\nappropriate for expansion in this position.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\nUsing Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n+----------------+---------------------+-------------------------------------+\n| Approach | Pros | Cons |\n+----------------+---------------------+-------------------------------------+\n| Pre-Processing | Essential template, | Secondary templating system needed, |\n| | more control | slower rendering |\n| Lambdas | Slim template | Data tagging, logic in data |\n| If Blocks | No data overhead, | Template linear growth |\n| | self-documenting | |\n| Dynamic Names | Slim template | Data tagging |\n+----------------+---------------------+-------------------------------------+\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index d980717..0823764 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -18,17 +18,16 @@ overview: | ] The goal is to render the different types of items in different ways. The items having a key named `content` should be rendered with the template - `text.mustache` and the items having a key named `url` should be rendered - with the template `image.mustache`: - + `text.mustache`: {{!image.mustache}} - + And the items having a key named `url` should be rendered with the template + `image.mustache`: {{!text.mustache}} {{content}} There are already several ways to achieve this goal, here below are illustrated and discussed the most significant solutions to this problem. - ## Using Pre-Processing + Using Pre-Processing The idea is to use a secondary templating mechanism to dynamically generate the template that will be rendered. The template that our secondary templating mechanism generates might look @@ -45,7 +44,7 @@ overview: | and minimizing the template blocks to the essential ones. The drawbacks are the rendering speed and the complexity that the secondary templating mechanism requires. - ## Using Lambdas + Using Lambdas The idea is to inject functions into the data that will be later called from the template. This way the data will look like this: @@ -87,7 +86,7 @@ overview: | The advantage this solution offers is to have a light main template. The drawback is that the data needs to embed logic and template tags in it. - ## Using If-Else Blocks + Using If-Else Blocks The idea is to put some logic into the main template so it can select the templates at rendering time: {{!template.mustache}} @@ -104,7 +103,7 @@ overview: | appropriate for expansion in this position. The drawback is that this solution isn't optimal for heterogeneous data sets as the main template grows linearly with the number of polymorphic variants. - ## Using Dynamic Names + Using Dynamic Names This is the solution proposed by this spec. The idea is to load partials dynamically. This way the data items have to be tagged with the corresponding partial name: -- cgit v1.2.1 From f3ceea64a22fcd0c3f8fe481e9b0b9c54fb262f7 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Thu, 28 Jul 2022 16:53:26 +0200 Subject: Adding blanklines and indenting rationale codes. --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 165 ++++++++++++++++++++++++---------------------- 2 files changed, 88 insertions(+), 79 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 8b4c8af..8264d21 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -1,5 +1,5 @@ { - "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways. Such cases would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways. The\nitems having a key named `content` should be rendered with the template\n`text.mustache`:\n {{!image.mustache}}\n \nAnd the items having a key named `url` should be rendered with the template\n`image.mustache`:\n {{!text.mustache}}\n {{content}}\nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\nUsing Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates might look\nlike this:\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\nUsing Lambdas\nThe idea is to inject functions into the data that will be later called from\nthe template.\nThis way the data will look like this:\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags in\nit.\nUsing If-Else Blocks\nThe idea is to put some logic into the main template so it can select the\ntemplates at rendering time:\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data. It also documents which external templates are\nappropriate for expansion in this position.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\nUsing Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n+----------------+---------------------+-------------------------------------+\n| Approach | Pros | Cons |\n+----------------+---------------------+-------------------------------------+\n| Pre-Processing | Essential template, | Secondary templating system needed, |\n| | more control | slower rendering |\n| Lambdas | Slim template | Data tagging, logic in data |\n| If Blocks | No data overhead, | Template linear growth |\n| | self-documenting | |\n| Dynamic Names | Slim template | Data tagging |\n+----------------+---------------------+-------------------------------------+\n\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", + "overview": "Rationale: this special notation was introduced primarily to allow the dynamic\nloading of partials. The main advantage that this notation offers is to allow\ndynamic loading of partials, which is particularly useful in cases where\npolymorphic data needs to be rendered in different ways. Such cases would\notherwise be possible to render only with solutions that are convoluted,\ninefficient, or both.\nExample.\nLet's consider the following data:\n\n items: [\n { content: 'Hello, World!' },\n { url: 'http://example.com/foo.jpg' },\n { content: 'Some text' },\n { content: 'Some other text' },\n { url: 'http://example.com/bar.jpg' },\n { url: 'http://example.com/baz.jpg' },\n { content: 'Last text here' }\n ]\nThe goal is to render the different types of items in different ways. The\nitems having a key named `content` should be rendered with the template\n`text.mustache`:\n\n {{!text.mustache}}\n {{content}}\nAnd the items having a key named `url` should be rendered with the template\n`image.mustache`:\n\n {{!image.mustache}}\n \nThere are already several ways to achieve this goal, here below are\nillustrated and discussed the most significant solutions to this problem.\nUsing Pre-Processing\nThe idea is to use a secondary templating mechanism to dynamically generate\nthe template that will be rendered.\nThe template that our secondary templating mechanism generates might look\nlike this:\n\n {{!template.mustache}}\n {{items.1.content}}\n \n {{items.3.content}}\n {{items.4.content}}\n \n \n {{items.7.content}}\nThis solutions offers the advantages of having more control over the template\nand minimizing the template blocks to the essential ones.\nThe drawbacks are the rendering speed and the complexity that the secondary\ntemplating mechanism requires.\nUsing Lambdas\nThe idea is to inject functions into the data that will be later called from\nthe template.\nThis way the data will look like this:\n\n items: [\n {\n content: 'Hello, World!',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/foo.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Some text',\n html: function() { return '{{>text}}'; }\n },\n {\n content: 'Some other text',\n html: function() { return '{{>text}}'; }\n },\n {\n url: 'http://example.com/bar.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n url: 'http://example.com/baz.jpg',\n html: function() { return '{{>image}}'; }\n },\n {\n content: 'Last text here',\n html: function() { return '{{>text}}'; }\n }\n ]\nAnd the template will look like this:\n\n {{!template.mustache}}\n {{#items}}\n {{{html}}}\n {{/items}}\nThe advantage this solution offers is to have a light main template.\nThe drawback is that the data needs to embed logic and template tags in\nit.\nUsing If-Else Blocks\nThe idea is to put some logic into the main template so it can select the\ntemplates at rendering time:\n\n {{!template.mustache}}\n {{#items}}\n {{#url}}\n {{>image}}\n {{/url}}\n {{#content}}\n {{>text}}\n {{/content}}\n {{/items}}\nThe main advantage of this solution is that it works without adding any\noverhead fields to the data. It also documents which external templates are\nappropriate for expansion in this position.\nThe drawback is that this solution isn't optimal for heterogeneous data sets\nas the main template grows linearly with the number of polymorphic variants.\nUsing Dynamic Names\nThis is the solution proposed by this spec.\nThe idea is to load partials dynamically.\nThis way the data items have to be tagged with the corresponding partial name:\n\n items: [\n { content: 'Hello, World!', dynamic: 'text' },\n { url: 'http://example.com/foo.jpg', dynamic: 'image' },\n { content: 'Some text', dynamic: 'text' },\n { content: 'Some other text', dynamic: 'text' },\n { url: 'http://example.com/bar.jpg', dynamic: 'image' },\n { url: 'http://example.com/baz.jpg', dynamic: 'image' },\n { content: 'Last text here', dynamic: 'text' }\n ]\nAnd the template would simple look like this:\n\n {{!template.mustache}}\n {{#items}}\n {{>*dynamic}}\n {{/items}}\nSummary:\n\n +----------------+---------------------+-----------------------------------+\n | Approach | Pros | Cons |\n +----------------+---------------------+-----------------------------------+\n | Pre-Processing | Essential template, | Secondary templating system |\n | | more control | needed, slower rendering |\n | Lambdas | Slim template | Data tagging, logic in data |\n | If Blocks | No data overhead, | Template linear growth |\n | | self-documenting | |\n | Dynamic Names | Slim template | Data tagging |\n +----------------+---------------------+-----------------------------------+\nDynamic Names are a special notation to dynamically determine a tag's content.\n\nDynamic Names MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter. A Dynamic Name consists of an asterisk,\nfollowed by a dotted name. The dotted name follows the same notation as in an\nInterpolation tag.\n\nThis tag's dotted name, which is the Dynamic Name excluding the\nleading asterisk, references a key in the context whose value will be used in\nplace of the Dynamic Name itself as content of the tag. The dotted name\nresolution produces the same value as an Interpolation tag and does not affect\nthe context for further processing.\n\nSet Delimiter tags MUST NOT affect the resolution of a Dynamic Name. The\nDynamic Names MUST be resolved against the context stack local to the tag.\nFailed resolution of the dynamic name SHOULD result in nothing being rendered.\n\nEngines that implement Dynamic Names MUST support their use in Partial tags.\nIn engines that also implement the optional inheritance spec, Dynamic Names\ninside Parent tags SHOULD be supported as well. Dynamic Names cannot be\nresolved more than once (Dynamic Names cannot be nested).\n", "tests": [ { "name": "Basic Behavior - Partial", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 0823764..0c6561d 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -7,6 +7,7 @@ overview: | inefficient, or both. Example. Let's consider the following data: + items: [ { content: 'Hello, World!' }, { url: 'http://example.com/foo.jpg' }, @@ -19,12 +20,14 @@ overview: | The goal is to render the different types of items in different ways. The items having a key named `content` should be rendered with the template `text.mustache`: - {{!image.mustache}} - - And the items having a key named `url` should be rendered with the template - `image.mustache`: + {{!text.mustache}} {{content}} + And the items having a key named `url` should be rendered with the template + `image.mustache`: + + {{!image.mustache}} + There are already several ways to achieve this goal, here below are illustrated and discussed the most significant solutions to this problem. Using Pre-Processing @@ -32,14 +35,15 @@ overview: | the template that will be rendered. The template that our secondary templating mechanism generates might look like this: - {{!template.mustache}} - {{items.1.content}} - - {{items.3.content}} - {{items.4.content}} - - - {{items.7.content}} + + {{!template.mustache}} + {{items.1.content}} + + {{items.3.content}} + {{items.4.content}} + + + {{items.7.content}} This solutions offers the advantages of having more control over the template and minimizing the template blocks to the essential ones. The drawbacks are the rendering speed and the complexity that the secondary @@ -48,56 +52,59 @@ overview: | The idea is to inject functions into the data that will be later called from the template. This way the data will look like this: - items: [ - { - content: 'Hello, World!', - html: function() { return '{{>text}}'; } - }, - { - url: 'http://example.com/foo.jpg', - html: function() { return '{{>image}}'; } - }, - { - content: 'Some text', - html: function() { return '{{>text}}'; } - }, - { - content: 'Some other text', - html: function() { return '{{>text}}'; } - }, - { - url: 'http://example.com/bar.jpg', - html: function() { return '{{>image}}'; } - }, - { - url: 'http://example.com/baz.jpg', - html: function() { return '{{>image}}'; } - }, - { - content: 'Last text here', - html: function() { return '{{>text}}'; } - } - ] + + items: [ + { + content: 'Hello, World!', + html: function() { return '{{>text}}'; } + }, + { + url: 'http://example.com/foo.jpg', + html: function() { return '{{>image}}'; } + }, + { + content: 'Some text', + html: function() { return '{{>text}}'; } + }, + { + content: 'Some other text', + html: function() { return '{{>text}}'; } + }, + { + url: 'http://example.com/bar.jpg', + html: function() { return '{{>image}}'; } + }, + { + url: 'http://example.com/baz.jpg', + html: function() { return '{{>image}}'; } + }, + { + content: 'Last text here', + html: function() { return '{{>text}}'; } + } + ] And the template will look like this: - {{!template.mustache}} - {{#items}} - {{{html}}} - {{/items}} + + {{!template.mustache}} + {{#items}} + {{{html}}} + {{/items}} The advantage this solution offers is to have a light main template. The drawback is that the data needs to embed logic and template tags in it. Using If-Else Blocks The idea is to put some logic into the main template so it can select the templates at rendering time: - {{!template.mustache}} - {{#items}} - {{#url}} - {{>image}} - {{/url}} - {{#content}} - {{>text}} - {{/content}} - {{/items}} + + {{!template.mustache}} + {{#items}} + {{#url}} + {{>image}} + {{/url}} + {{#content}} + {{>text}} + {{/content}} + {{/items}} The main advantage of this solution is that it works without adding any overhead fields to the data. It also documents which external templates are appropriate for expansion in this position. @@ -107,32 +114,34 @@ overview: | This is the solution proposed by this spec. The idea is to load partials dynamically. This way the data items have to be tagged with the corresponding partial name: - items: [ - { content: 'Hello, World!', dynamic: 'text' }, - { url: 'http://example.com/foo.jpg', dynamic: 'image' }, - { content: 'Some text', dynamic: 'text' }, - { content: 'Some other text', dynamic: 'text' }, - { url: 'http://example.com/bar.jpg', dynamic: 'image' }, - { url: 'http://example.com/baz.jpg', dynamic: 'image' }, - { content: 'Last text here', dynamic: 'text' } - ] + + items: [ + { content: 'Hello, World!', dynamic: 'text' }, + { url: 'http://example.com/foo.jpg', dynamic: 'image' }, + { content: 'Some text', dynamic: 'text' }, + { content: 'Some other text', dynamic: 'text' }, + { url: 'http://example.com/bar.jpg', dynamic: 'image' }, + { url: 'http://example.com/baz.jpg', dynamic: 'image' }, + { content: 'Last text here', dynamic: 'text' } + ] And the template would simple look like this: - {{!template.mustache}} - {{#items}} - {{>*dynamic}} - {{/items}} + + {{!template.mustache}} + {{#items}} + {{>*dynamic}} + {{/items}} Summary: - +----------------+---------------------+-------------------------------------+ - | Approach | Pros | Cons | - +----------------+---------------------+-------------------------------------+ - | Pre-Processing | Essential template, | Secondary templating system needed, | - | | more control | slower rendering | - | Lambdas | Slim template | Data tagging, logic in data | - | If Blocks | No data overhead, | Template linear growth | - | | self-documenting | | - | Dynamic Names | Slim template | Data tagging | - +----------------+---------------------+-------------------------------------+ + +----------------+---------------------+-----------------------------------+ + | Approach | Pros | Cons | + +----------------+---------------------+-----------------------------------+ + | Pre-Processing | Essential template, | Secondary templating system | + | | more control | needed, slower rendering | + | Lambdas | Slim template | Data tagging, logic in data | + | If Blocks | No data overhead, | Template linear growth | + | | self-documenting | | + | Dynamic Names | Slim template | Data tagging | + +----------------+---------------------+-----------------------------------+ Dynamic Names are a special notation to dynamically determine a tag's content. Dynamic Names MUST be a non-whitespace character sequence NOT containing -- cgit v1.2.1 From 734537ea72d9f4e12e32c9606b163c938a00a136 Mon Sep 17 00:00:00 2001 From: anomal00us <95467104+anomal00us@users.noreply.github.com> Date: Thu, 28 Jul 2022 17:21:35 +0200 Subject: Updating spec, typo fix (squashed) (coauthored) Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp Update specs/~dynamic-names.yml Co-authored-by: Julian Gonggrijp --- specs/~dynamic-names.json | 2 +- specs/~dynamic-names.yml | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/specs/~dynamic-names.json b/specs/~dynamic-names.json index 8264d21..dca3b72 100644 --- a/specs/~dynamic-names.json +++ b/specs/~dynamic-names.json @@ -191,7 +191,7 @@ "expected": "X>" }, { - "name": "Dynamic Names - Dobule Dereferencing", + "name": "Dynamic Names - Double Dereferencing", "desc": "Dynamic Names can't be dereferenced more than once.", "data": { "dynamic": "test", diff --git a/specs/~dynamic-names.yml b/specs/~dynamic-names.yml index 0c6561d..0ab9290 100644 --- a/specs/~dynamic-names.yml +++ b/specs/~dynamic-names.yml @@ -5,6 +5,7 @@ overview: | polymorphic data needs to be rendered in different ways. Such cases would otherwise be possible to render only with solutions that are convoluted, inefficient, or both. + Example. Let's consider the following data: @@ -17,20 +18,25 @@ overview: | { url: 'http://example.com/baz.jpg' }, { content: 'Last text here' } ] + The goal is to render the different types of items in different ways. The items having a key named `content` should be rendered with the template `text.mustache`: {{!text.mustache}} {{content}} + And the items having a key named `url` should be rendered with the template `image.mustache`: {{!image.mustache}} + There are already several ways to achieve this goal, here below are illustrated and discussed the most significant solutions to this problem. + Using Pre-Processing + The idea is to use a secondary templating mechanism to dynamically generate the template that will be rendered. The template that our secondary templating mechanism generates might look @@ -44,11 +50,14 @@ overview: | {{items.7.content}} + This solutions offers the advantages of having more control over the template and minimizing the template blocks to the essential ones. The drawbacks are the rendering speed and the complexity that the secondary templating mechanism requires. + Using Lambdas + The idea is to inject functions into the data that will be later called from the template. This way the data will look like this: @@ -83,16 +92,20 @@ overview: | html: function() { return '{{>text}}'; } } ] + And the template will look like this: {{!template.mustache}} {{#items}} {{{html}}} {{/items}} + The advantage this solution offers is to have a light main template. The drawback is that the data needs to embed logic and template tags in it. + Using If-Else Blocks + The idea is to put some logic into the main template so it can select the templates at rendering time: @@ -105,12 +118,15 @@ overview: | {{>text}} {{/content}} {{/items}} + The main advantage of this solution is that it works without adding any overhead fields to the data. It also documents which external templates are appropriate for expansion in this position. The drawback is that this solution isn't optimal for heterogeneous data sets as the main template grows linearly with the number of polymorphic variants. + Using Dynamic Names + This is the solution proposed by this spec. The idea is to load partials dynamically. This way the data items have to be tagged with the corresponding partial name: @@ -124,12 +140,14 @@ overview: | { url: 'http://example.com/baz.jpg', dynamic: 'image' }, { content: 'Last text here', dynamic: 'text' } ] + And the template would simple look like this: {{!template.mustache}} {{#items}} {{>*dynamic}} {{/items}} + Summary: +----------------+---------------------+-----------------------------------+ @@ -142,6 +160,7 @@ overview: | | | self-documenting | | | Dynamic Names | Slim template | Data tagging | +----------------+---------------------+-----------------------------------+ + Dynamic Names are a special notation to dynamically determine a tag's content. Dynamic Names MUST be a non-whitespace character sequence NOT containing @@ -272,7 +291,7 @@ tests: partials: { node: '{{content}}<{{#nodes}}{{>*template}}{{/nodes}}>' } expected: 'X>' - - name: Dynamic Names - Dobule Dereferencing + - name: Dynamic Names - Double Dereferencing desc: Dynamic Names can't be dereferenced more than once. data: { dynamic: 'test', 'test': 'content' } template: '"{{>**dynamic}}"' -- cgit v1.2.1