stache 0.1.7 → 0.1.8
raw patch · 17 files changed
+1084/−1078 lines, 17 filesdep ~aesondep ~stachedep ~vector
Dependency ranges changed: aeson, stache, vector
Files
- CHANGELOG.md +6/−0
- README.md +1/−1
- Text/Mustache.hs +1/−1
- mustache-spec/Spec.hs +6/−6
- specification/comments.yml +103/−0
- specification/delimiters.yml +158/−0
- specification/interpolation.yml +238/−0
- specification/inverted.yml +193/−0
- specification/partials.yml +109/−0
- specification/sections.yml +263/−0
- specs/comments.yml +0/−103
- specs/delimiters.yml +0/−158
- specs/interpolation.yml +0/−238
- specs/inverted.yml +0/−193
- specs/partials.yml +0/−109
- specs/sections.yml +0/−263
- stache.cabal +6/−6
CHANGELOG.md view
@@ -1,3 +1,9 @@+## Stache 0.1.8++* Rename `specs` directory to `specification` as the previous name somehow+ caused conflicts when deploying an application on Heroku with Stache as a+ dependency.+ ## Stache 0.1.7 * Added `mustache` quasi-quoter.
README.md view
@@ -15,7 +15,7 @@ render them. The implementation uses the Megaparsec parsing library to parse the-templates which is results in superior quality of error messages.+templates which results in superior quality of error messages. For rendering you only need to create Aeson's `Value` where you put the data to interpolate. Since the library re-uses Aeson's instances and most data
Text/Mustache.hs view
@@ -15,7 +15,7 @@ -- text) and one to render them. -- -- The implementation uses the Megaparsec parsing library to parse the--- templates which is results in superior quality of error messages.+-- templates which results in superior quality of error messages. -- -- For rendering you only need to create Aeson's 'Data.Aeson.Value' where -- you put the data to interpolate. Since the library re-uses Aeson's
mustache-spec/Spec.hs view
@@ -58,12 +58,12 @@ spec :: Spec spec = do- specData "Comments" $(embedFile "specs/comments.yml")- specData "Delimiters" $(embedFile "specs/delimiters.yml")- specData "Interpolation" $(embedFile "specs/interpolation.yml")- specData "Inverted" $(embedFile "specs/inverted.yml")- specData "Partials" $(embedFile "specs/partials.yml")- specData "Sections" $(embedFile "specs/sections.yml")+ specData "Comments" $(embedFile "specification/comments.yml")+ specData "Delimiters" $(embedFile "specification/delimiters.yml")+ specData "Interpolation" $(embedFile "specification/interpolation.yml")+ specData "Inverted" $(embedFile "specification/inverted.yml")+ specData "Partials" $(embedFile "specification/partials.yml")+ specData "Sections" $(embedFile "specification/sections.yml") specData :: String -> ByteString -> Spec specData aspect bytes = describe aspect $ do
+ specification/comments.yml view
@@ -0,0 +1,103 @@+overview: |+ Comment tags represent content that should never appear in the resulting+ output.++ The tag's content may contain any substring (including newlines) EXCEPT the+ closing delimiter.++ Comment tags SHOULD be treated as standalone when appropriate.+tests:+ - name: Inline+ desc: Comment blocks should be removed from the template.+ data: { }+ template: '12345{{! Comment Block! }}67890'+ expected: '1234567890'++ - name: Multiline+ desc: Multiline comments should be permitted.+ data: { }+ template: |+ 12345{{!+ This is a+ multi-line comment...+ }}67890+ expected: |+ 1234567890++ - name: Standalone+ desc: All standalone comment lines should be removed.+ data: { }+ template: |+ Begin.+ {{! Comment Block! }}+ End.+ expected: |+ Begin.+ End.++ - name: Indented Standalone+ desc: All standalone comment lines should be removed.+ data: { }+ template: |+ Begin.+ {{! Indented Comment Block! }}+ End.+ expected: |+ Begin.+ End.++ - name: Standalone Line Endings+ desc: '"\r\n" should be considered a newline for standalone tags.'+ data: { }+ template: "|\r\n{{! Standalone Comment }}\r\n|"+ expected: "|\r\n|"++ - name: Standalone Without Previous Line+ desc: Standalone tags should not require a newline to precede them.+ data: { }+ template: " {{! I'm Still Standalone }}\n!"+ expected: "!"++ - name: Standalone Without Newline+ desc: Standalone tags should not require a newline to follow them.+ data: { }+ template: "!\n {{! I'm Still Standalone }}"+ expected: "!\n"++ - name: Multiline Standalone+ desc: All standalone comment lines should be removed.+ data: { }+ template: |+ Begin.+ {{!+ Something's going on here...+ }}+ End.+ expected: |+ Begin.+ End.++ - name: Indented Multiline Standalone+ desc: All standalone comment lines should be removed.+ data: { }+ template: |+ Begin.+ {{!+ Something's going on here...+ }}+ End.+ expected: |+ Begin.+ End.++ - name: Indented Inline+ desc: Inline comments should not strip whitespace+ data: { }+ template: " 12 {{! 34 }}\n"+ expected: " 12 \n"++ - name: Surrounding Whitespace+ desc: Comment removal should preserve surrounding whitespace.+ data: { }+ template: '12345 {{! Comment Block! }} 67890'+ expected: '12345 67890'
+ specification/delimiters.yml view
@@ -0,0 +1,158 @@+overview: |+ Set Delimiter tags are used to change the tag delimiters for all content+ following the tag in the current compilation unit.++ The tag's content MUST be any two non-whitespace sequences (separated by+ whitespace) EXCEPT an equals sign ('=') followed by the current closing+ delimiter.++ Set Delimiter tags SHOULD be treated as standalone when appropriate.+tests:+ - name: Pair Behavior+ desc: The equals sign (used on both sides) should permit delimiter changes.+ data: { text: 'Hey!' }+ template: '{{=<% %>=}}(<%text%>)'+ expected: '(Hey!)'++ - name: Special Characters+ desc: Characters with special meaning regexen should be valid delimiters.+ data: { text: 'It worked!' }+ template: '({{=[ ]=}}[text])'+ expected: '(It worked!)'++ - name: Sections+ desc: Delimiters set outside sections should persist.+ data: { section: true, data: 'I got interpolated.' }+ template: |+ [+ {{#section}}+ {{data}}+ |data|+ {{/section}}++ {{= | | =}}+ |#section|+ {{data}}+ |data|+ |/section|+ ]+ expected: |+ [+ I got interpolated.+ |data|++ {{data}}+ I got interpolated.+ ]++ - name: Inverted Sections+ desc: Delimiters set outside inverted sections should persist.+ data: { section: false, data: 'I got interpolated.' }+ template: |+ [+ {{^section}}+ {{data}}+ |data|+ {{/section}}++ {{= | | =}}+ |^section|+ {{data}}+ |data|+ |/section|+ ]+ expected: |+ [+ I got interpolated.+ |data|++ {{data}}+ I got interpolated.+ ]++ - name: Partial Inheritence+ desc: Delimiters set in a parent template should not affect a partial.+ data: { value: 'yes' }+ partials:+ include: '.{{value}}.'+ template: |+ [ {{>include}} ]+ {{= | | =}}+ [ |>include| ]+ expected: |+ [ .yes. ]+ [ .yes. ]++ - name: Post-Partial Behavior+ desc: Delimiters set in a partial should not affect the parent template.+ data: { value: 'yes' }+ partials:+ include: '.{{value}}. {{= | | =}} .|value|.'+ template: |+ [ {{>include}} ]+ [ .{{value}}. .|value|. ]+ expected: |+ [ .yes. .yes. ]+ [ .yes. .|value|. ]++ # Whitespace Sensitivity++ - name: Surrounding Whitespace+ desc: Surrounding whitespace should be left untouched.+ data: { }+ template: '| {{=@ @=}} |'+ expected: '| |'++ - name: Outlying Whitespace (Inline)+ desc: Whitespace should be left untouched.+ data: { }+ template: " | {{=@ @=}}\n"+ expected: " | \n"++ - name: Standalone Tag+ desc: Standalone lines should be removed from the template.+ data: { }+ template: |+ Begin.+ {{=@ @=}}+ End.+ expected: |+ Begin.+ End.++ - name: Indented Standalone Tag+ desc: Indented standalone lines should be removed from the template.+ data: { }+ template: |+ Begin.+ {{=@ @=}}+ End.+ expected: |+ Begin.+ End.++ - name: Standalone Line Endings+ desc: '"\r\n" should be considered a newline for standalone tags.'+ data: { }+ template: "|\r\n{{= @ @ =}}\r\n|"+ expected: "|\r\n|"++ - name: Standalone Without Previous Line+ desc: Standalone tags should not require a newline to precede them.+ data: { }+ template: " {{=@ @=}}\n="+ expected: "="++ - name: Standalone Without Newline+ desc: Standalone tags should not require a newline to follow them.+ data: { }+ template: "=\n {{=@ @=}}"+ expected: "=\n"++ # Whitespace Insensitivity++ - name: Pair with Padding+ desc: Superfluous in-tag whitespace should be ignored.+ data: { }+ template: '|{{= @ @ =}}|'+ expected: '||'
+ specification/interpolation.yml view
@@ -0,0 +1,238 @@+overview: |+ Interpolation tags are used to integrate dynamic content into the template.++ The tag's content MUST be a non-whitespace character sequence NOT containing+ the current closing delimiter.++ This tag's content names the data to replace the tag. A single period (`.`)+ indicates that the item currently sitting atop the context stack should be+ used; otherwise, name resolution is as follows:+ 1) Split the name on periods; the first part is the name to resolve, any+ remaining parts should be retained.+ 2) Walk the context stack from top to bottom, finding the first context+ that is a) a hash containing the name as a key OR b) an object responding+ to a method with the given name.+ 3) If the context is a hash, the data is the value associated with the+ name.+ 4) If the context is an object, 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.+ Data should be coerced into a string (and escaped, if appropriate) before+ interpolation.++ The Interpolation tags MUST NOT be treated as standalone.+tests:+ - name: No Interpolation+ desc: Mustache-free templates should render as-is.+ data: { }+ template: |+ Hello from {Mustache}!+ expected: |+ Hello from {Mustache}!++ - name: Basic Interpolation+ desc: Unadorned tags should interpolate content into the template.+ data: { subject: "world" }+ template: |+ Hello, {{subject}}!+ expected: |+ Hello, world!++ - name: HTML Escaping+ desc: Basic interpolation should be HTML escaped.+ data: { forbidden: '& " < >' }+ template: |+ These characters should be HTML escaped: {{forbidden}}+ expected: |+ These characters should be HTML escaped: & " < >++ - name: Triple Mustache+ desc: Triple mustaches should interpolate without HTML escaping.+ data: { forbidden: '& " < >' }+ template: |+ These characters should not be HTML escaped: {{{forbidden}}}+ expected: |+ These characters should not be HTML escaped: & " < >++ - name: Ampersand+ desc: Ampersand should interpolate without HTML escaping.+ data: { forbidden: '& " < >' }+ template: |+ These characters should not be HTML escaped: {{&forbidden}}+ expected: |+ These characters should not be HTML escaped: & " < >++ - name: Basic Integer Interpolation+ desc: Integers should interpolate seamlessly.+ data: { mph: 85 }+ template: '"{{mph}} miles an hour!"'+ expected: '"85 miles an hour!"'++ - name: Triple Mustache Integer Interpolation+ desc: Integers should interpolate seamlessly.+ data: { mph: 85 }+ template: '"{{{mph}}} miles an hour!"'+ expected: '"85 miles an hour!"'++ - name: Ampersand Integer Interpolation+ desc: Integers should interpolate seamlessly.+ data: { mph: 85 }+ template: '"{{&mph}} miles an hour!"'+ expected: '"85 miles an hour!"'++ - name: Basic Decimal Interpolation+ desc: Decimals should interpolate seamlessly with proper significance.+ data: { power: 1.210 }+ template: '"{{power}} jiggawatts!"'+ expected: '"1.21 jiggawatts!"'++ - name: Triple Mustache Decimal Interpolation+ desc: Decimals should interpolate seamlessly with proper significance.+ data: { power: 1.210 }+ template: '"{{{power}}} jiggawatts!"'+ expected: '"1.21 jiggawatts!"'++ - name: Ampersand Decimal Interpolation+ desc: Decimals should interpolate seamlessly with proper significance.+ data: { power: 1.210 }+ template: '"{{&power}} jiggawatts!"'+ expected: '"1.21 jiggawatts!"'++ # Context Misses++ - name: Basic Context Miss Interpolation+ desc: Failed context lookups should default to empty strings.+ data: { }+ template: "I ({{cannot}}) be seen!"+ expected: "I () be seen!"++ - name: Triple Mustache Context Miss Interpolation+ desc: Failed context lookups should default to empty strings.+ data: { }+ template: "I ({{{cannot}}}) be seen!"+ expected: "I () be seen!"++ - name: Ampersand Context Miss Interpolation+ desc: Failed context lookups should default to empty strings.+ data: { }+ template: "I ({{&cannot}}) be seen!"+ expected: "I () be seen!"++ # Dotted Names++ - name: Dotted Names - Basic Interpolation+ desc: Dotted names should be considered a form of shorthand for sections.+ data: { person: { name: 'Joe' } }+ template: '"{{person.name}}" == "{{#person}}{{name}}{{/person}}"'+ expected: '"Joe" == "Joe"'++ - name: Dotted Names - Triple Mustache Interpolation+ desc: Dotted names should be considered a form of shorthand for sections.+ data: { person: { name: 'Joe' } }+ template: '"{{{person.name}}}" == "{{#person}}{{{name}}}{{/person}}"'+ expected: '"Joe" == "Joe"'++ - name: Dotted Names - Ampersand Interpolation+ desc: Dotted names should be considered a form of shorthand for sections.+ data: { person: { name: 'Joe' } }+ template: '"{{&person.name}}" == "{{#person}}{{&name}}{{/person}}"'+ expected: '"Joe" == "Joe"'++ - name: Dotted Names - Arbitrary Depth+ desc: Dotted names should be functional to any level of nesting.+ data:+ a: { b: { c: { d: { e: { name: 'Phil' } } } } }+ template: '"{{a.b.c.d.e.name}}" == "Phil"'+ expected: '"Phil" == "Phil"'++ - name: Dotted Names - Broken Chains+ desc: Any falsey value prior to the last part of the name should yield ''.+ data:+ a: { }+ template: '"{{a.b.c}}" == ""'+ expected: '"" == ""'++ - name: Dotted Names - Broken Chain Resolution+ desc: Each part of a dotted name should resolve only against its parent.+ data:+ a: { b: { } }+ c: { name: 'Jim' }+ template: '"{{a.b.c.name}}" == ""'+ expected: '"" == ""'++ - name: Dotted Names - Initial Resolution+ desc: The first part of a dotted name should resolve as any other name.+ data:+ a: { b: { c: { d: { e: { name: 'Phil' } } } } }+ b: { c: { d: { e: { name: 'Wrong' } } } }+ template: '"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"'+ expected: '"Phil" == "Phil"'++ - name: Dotted Names - Context Precedence+ desc: Dotted names should be resolved against former resolutions.+ data:+ a: { b: { } }+ b: { c: 'ERROR' }+ template: '{{#a}}{{b.c}}{{/a}}'+ expected: ''++ # Whitespace Sensitivity++ - name: Interpolation - Surrounding Whitespace+ desc: Interpolation should not alter surrounding whitespace.+ data: { string: '---' }+ template: '| {{string}} |'+ expected: '| --- |'++ - name: Triple Mustache - Surrounding Whitespace+ desc: Interpolation should not alter surrounding whitespace.+ data: { string: '---' }+ template: '| {{{string}}} |'+ expected: '| --- |'++ - name: Ampersand - Surrounding Whitespace+ desc: Interpolation should not alter surrounding whitespace.+ data: { string: '---' }+ template: '| {{&string}} |'+ expected: '| --- |'++ - name: Interpolation - Standalone+ desc: Standalone interpolation should not alter surrounding whitespace.+ data: { string: '---' }+ template: " {{string}}\n"+ expected: " ---\n"++ - name: Triple Mustache - Standalone+ desc: Standalone interpolation should not alter surrounding whitespace.+ data: { string: '---' }+ template: " {{{string}}}\n"+ expected: " ---\n"++ - name: Ampersand - Standalone+ desc: Standalone interpolation should not alter surrounding whitespace.+ data: { string: '---' }+ template: " {{&string}}\n"+ expected: " ---\n"++ # Whitespace Insensitivity++ - name: Interpolation With Padding+ desc: Superfluous in-tag whitespace should be ignored.+ data: { string: "---" }+ template: '|{{ string }}|'+ expected: '|---|'++ - name: Triple Mustache With Padding+ desc: Superfluous in-tag whitespace should be ignored.+ data: { string: "---" }+ template: '|{{{ string }}}|'+ expected: '|---|'++ - name: Ampersand With Padding+ desc: Superfluous in-tag whitespace should be ignored.+ data: { string: "---" }+ template: '|{{& string }}|'+ expected: '|---|'
+ specification/inverted.yml view
@@ -0,0 +1,193 @@+overview: |+ Inverted Section tags and End Section tags are used in combination to wrap a+ section of the template.++ These tags' content MUST be a non-whitespace character sequence NOT+ containing the current closing delimiter; each Inverted Section tag MUST be+ followed by an End Section tag with the same content within the same+ section.++ This tag's content names the data to replace the tag. Name resolution is as+ follows:+ 1) Split the name on periods; the first part is the name to resolve, any+ remaining parts should be retained.+ 2) Walk the context stack from top to bottom, finding the first context+ that is a) a hash containing the name as a key OR b) an object responding+ to a method with the given name.+ 3) If the context is a hash, the data is the value associated with the+ name.+ 4) If the context is an object and the method with the given name has an+ arity of 1, the method SHOULD be called with a String containing the+ unprocessed contents of the sections; the data is the value returned.+ 5) Otherwise, the data is the value returned by calling the method with+ the given name.+ 6) If any name parts were retained in step 1, each should be resolved+ against a context stack containing only the result from the former+ resolution. If any part fails resolution, the result should be considered+ falsey, and should interpolate as the empty string.+ If the data is not of a list type, it is coerced into a list as follows: if+ the data is truthy (e.g. `!!data == true`), use a single-element list+ containing the data, otherwise use an empty list.++ This section MUST NOT be rendered unless the data list is empty.++ Inverted Section and End Section tags SHOULD be treated as standalone when+ appropriate.+tests:+ - name: Falsey+ desc: Falsey sections should have their contents rendered.+ data: { boolean: false }+ template: '"{{^boolean}}This should be rendered.{{/boolean}}"'+ expected: '"This should be rendered."'++ - name: Truthy+ desc: Truthy sections should have their contents omitted.+ data: { boolean: true }+ template: '"{{^boolean}}This should not be rendered.{{/boolean}}"'+ expected: '""'++ - name: Context+ desc: Objects and hashes should behave like truthy values.+ data: { context: { name: 'Joe' } }+ template: '"{{^context}}Hi {{name}}.{{/context}}"'+ expected: '""'++ - name: List+ desc: Lists should behave like truthy values.+ data: { list: [ { n: 1 }, { n: 2 }, { n: 3 } ] }+ template: '"{{^list}}{{n}}{{/list}}"'+ expected: '""'++ - name: Empty List+ desc: Empty lists should behave like falsey values.+ data: { list: [ ] }+ template: '"{{^list}}Yay lists!{{/list}}"'+ expected: '"Yay lists!"'++ - name: Doubled+ desc: Multiple inverted sections per template should be permitted.+ data: { bool: false, two: 'second' }+ template: |+ {{^bool}}+ * first+ {{/bool}}+ * {{two}}+ {{^bool}}+ * third+ {{/bool}}+ expected: |+ * first+ * second+ * third++ - name: Nested (Falsey)+ desc: Nested falsey sections should have their contents rendered.+ data: { bool: false }+ template: "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |"+ expected: "| A B C D E |"++ - name: Nested (Truthy)+ desc: Nested truthy sections should be omitted.+ data: { bool: true }+ template: "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |"+ expected: "| A E |"++ - name: Context Misses+ desc: Failed context lookups should be considered falsey.+ data: { }+ template: "[{{^missing}}Cannot find key 'missing'!{{/missing}}]"+ expected: "[Cannot find key 'missing'!]"++ # Dotted Names++ - name: Dotted Names - Truthy+ desc: Dotted names should be valid for Inverted Section tags.+ data: { a: { b: { c: true } } }+ template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == ""'+ expected: '"" == ""'++ - name: Dotted Names - Falsey+ desc: Dotted names should be valid for Inverted Section tags.+ data: { a: { b: { c: false } } }+ template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here"'+ expected: '"Not Here" == "Not Here"'++ - name: Dotted Names - Broken Chains+ desc: Dotted names that cannot be resolved should be considered falsey.+ data: { a: { } }+ template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here"'+ expected: '"Not Here" == "Not Here"'++ # Whitespace Sensitivity++ - name: Surrounding Whitespace+ desc: Inverted sections should not alter surrounding whitespace.+ data: { boolean: false }+ template: " | {{^boolean}}\t|\t{{/boolean}} | \n"+ expected: " | \t|\t | \n"++ - name: Internal Whitespace+ desc: Inverted should not alter internal whitespace.+ data: { boolean: false }+ template: " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"+ expected: " | \n | \n"++ - name: Indented Inline Sections+ desc: Single-line sections should not alter surrounding whitespace.+ data: { boolean: false }+ template: " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n"+ expected: " NO\n WAY\n"++ - name: Standalone Lines+ desc: Standalone lines should be removed from the template.+ data: { boolean: false }+ template: |+ | This Is+ {{^boolean}}+ |+ {{/boolean}}+ | A Line+ expected: |+ | This Is+ |+ | A Line++ - name: Standalone Indented Lines+ desc: Standalone indented lines should be removed from the template.+ data: { boolean: false }+ template: |+ | This Is+ {{^boolean}}+ |+ {{/boolean}}+ | A Line+ expected: |+ | This Is+ |+ | A Line++ - name: Standalone Line Endings+ desc: '"\r\n" should be considered a newline for standalone tags.'+ data: { boolean: false }+ template: "|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|"+ expected: "|\r\n|"++ - name: Standalone Without Previous Line+ desc: Standalone tags should not require a newline to precede them.+ data: { boolean: false }+ template: " {{^boolean}}\n^{{/boolean}}\n/"+ expected: "^\n/"++ - name: Standalone Without Newline+ desc: Standalone tags should not require a newline to follow them.+ data: { boolean: false }+ template: "^{{^boolean}}\n/\n {{/boolean}}"+ expected: "^\n/\n"++ # Whitespace Insensitivity++ - name: Padding+ desc: Superfluous in-tag whitespace should be ignored.+ data: { boolean: false }+ template: '|{{^ boolean }}={{/ boolean }}|'+ expected: '|=|'
+ specification/partials.yml view
@@ -0,0 +1,109 @@+overview: |+ Partial tags are used to 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 the partial to inject. Set Delimiter tags MUST NOT+ affect the parsing of a partial. The partial MUST be rendered against the+ context stack local to the tag. If the named partial cannot be found, the+ empty string SHOULD be used instead, as in interpolations.++ 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 greater-than operator should expand to the named partial.+ data: { }+ template: '"{{>text}}"'+ partials: { text: 'from partial' }+ expected: '"from partial"'++ - name: Failed Lookup+ desc: The empty string should be used when the named partial is not found.+ data: { }+ template: '"{{>text}}"'+ partials: { }+ expected: '""'++ - name: Context+ desc: The greater-than operator should operate within the current context.+ data: { text: 'content' }+ template: '"{{>partial}}"'+ partials: { partial: '*{{text}}*' }+ expected: '"*content*"'++ - name: Recursion+ desc: The greater-than operator should properly recurse.+ data: { content: "X", nodes: [ { content: "Y", nodes: [] } ] }+ template: '{{>node}}'+ partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' }+ expected: 'X<Y<>>'++ # Whitespace Sensitivity++ - name: Surrounding Whitespace+ desc: The greater-than operator should not alter surrounding whitespace.+ data: { }+ template: '| {{>partial}} |'+ partials: { partial: "\t|\t" }+ expected: "| \t|\t |"++ - name: Inline Indentation+ desc: Whitespace should be left untouched.+ data: { 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: { }+ template: "|\r\n{{>partial}}\r\n|"+ partials: { partial: ">" }+ expected: "|\r\n>|"++ - name: Standalone Without Previous Line+ desc: Standalone tags should not require a newline to precede them.+ data: { }+ template: " {{>partial}}\n>"+ partials: { partial: ">\n>"}+ expected: " >\n >>"++ - name: Standalone Without Newline+ desc: Standalone tags should not require a newline to follow them.+ data: { }+ template: ">\n {{>partial}}"+ partials: { partial: ">\n>" }+ expected: ">\n >\n >"++ - name: Standalone Indentation+ desc: Each line of the partial should be indented before rendering.+ data: { content: "<\n->" }+ template: |+ \+ {{>partial}}+ /+ partials:+ partial: |+ |+ {{{content}}}+ |+ expected: |+ \+ |+ <+ ->+ |+ /++ # Whitespace Insensitivity++ - name: Padding Whitespace+ desc: Superfluous in-tag whitespace should be ignored.+ data: { boolean: true }+ template: "|{{> partial }}|"+ partials: { partial: "[]" }+ expected: '|[]|'
+ specification/sections.yml view
@@ -0,0 +1,263 @@+overview: |+ Section tags and End Section tags are used in combination to wrap a section+ of the template for iteration++ These tags' content MUST be a non-whitespace character sequence NOT+ containing the current closing delimiter; each Section tag MUST be followed+ by an End Section tag with the same content within the same section.++ This tag's content names the data to replace the tag. Name resolution is as+ follows:+ 1) Split the name on periods; the first part is the name to resolve, any+ remaining parts should be retained.+ 2) Walk the context stack from top to bottom, finding the first context+ that is a) a hash containing the name as a key OR b) an object responding+ to a method with the given name.+ 3) If the context is a hash, the data is the value associated with the+ name.+ 4) If the context is an object and the method with the given name has an+ arity of 1, the method SHOULD be called with a String containing the+ unprocessed contents of the sections; the data is the value returned.+ 5) Otherwise, the data is the value returned by calling the method with+ the given name.+ 6) If any name parts were retained in step 1, each should be resolved+ against a context stack containing only the result from the former+ resolution. If any part fails resolution, the result should be considered+ falsey, and should interpolate as the empty string.+ If the data is not of a list type, it is coerced into a list as follows: if+ the data is truthy (e.g. `!!data == true`), use a single-element list+ containing the data, otherwise use an empty list.++ For each element in the data list, the element MUST be pushed onto the+ context stack, the section MUST be rendered, and the element MUST be popped+ off the context stack.++ Section and End Section tags SHOULD be treated as standalone when+ appropriate.+tests:+ - name: Truthy+ desc: Truthy sections should have their contents rendered.+ data: { boolean: true }+ template: '"{{#boolean}}This should be rendered.{{/boolean}}"'+ expected: '"This should be rendered."'++ - name: Falsey+ desc: Falsey sections should have their contents omitted.+ data: { boolean: false }+ template: '"{{#boolean}}This should not be rendered.{{/boolean}}"'+ expected: '""'++ - name: Context+ desc: Objects and hashes should be pushed onto the context stack.+ data: { context: { name: 'Joe' } }+ template: '"{{#context}}Hi {{name}}.{{/context}}"'+ expected: '"Hi Joe."'++ - name: Deeply Nested Contexts+ desc: All elements on the context stack should be accessible.+ data:+ a: { one: 1 }+ b: { two: 2 }+ c: { three: 3 }+ d: { four: 4 }+ e: { five: 5 }+ template: |+ {{#a}}+ {{one}}+ {{#b}}+ {{one}}{{two}}{{one}}+ {{#c}}+ {{one}}{{two}}{{three}}{{two}}{{one}}+ {{#d}}+ {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}+ {{#e}}+ {{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}+ {{/e}}+ {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}+ {{/d}}+ {{one}}{{two}}{{three}}{{two}}{{one}}+ {{/c}}+ {{one}}{{two}}{{one}}+ {{/b}}+ {{one}}+ {{/a}}+ expected: |+ 1+ 121+ 12321+ 1234321+ 123454321+ 1234321+ 12321+ 121+ 1++ - name: List+ desc: Lists should be iterated; list items should visit the context stack.+ data: { list: [ { item: 1 }, { item: 2 }, { item: 3 } ] }+ template: '"{{#list}}{{item}}{{/list}}"'+ expected: '"123"'++ - name: Empty List+ desc: Empty lists should behave like falsey values.+ data: { list: [ ] }+ template: '"{{#list}}Yay lists!{{/list}}"'+ expected: '""'++ - name: Doubled+ desc: Multiple sections per template should be permitted.+ data: { bool: true, two: 'second' }+ template: |+ {{#bool}}+ * first+ {{/bool}}+ * {{two}}+ {{#bool}}+ * third+ {{/bool}}+ expected: |+ * first+ * second+ * third++ - name: Nested (Truthy)+ desc: Nested truthy sections should have their contents rendered.+ data: { bool: true }+ template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"+ expected: "| A B C D E |"++ - name: Nested (Falsey)+ desc: Nested falsey sections should be omitted.+ data: { bool: false }+ template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"+ expected: "| A E |"++ - name: Context Misses+ desc: Failed context lookups should be considered falsey.+ data: { }+ template: "[{{#missing}}Found key 'missing'!{{/missing}}]"+ expected: "[]"++ # Implicit Iterators++ - name: Implicit Iterator - String+ desc: Implicit iterators should directly interpolate strings.+ data:+ list: [ 'a', 'b', 'c', 'd', 'e' ]+ template: '"{{#list}}({{.}}){{/list}}"'+ expected: '"(a)(b)(c)(d)(e)"'++ - name: Implicit Iterator - Integer+ desc: Implicit iterators should cast integers to strings and interpolate.+ data:+ list: [ 1, 2, 3, 4, 5 ]+ template: '"{{#list}}({{.}}){{/list}}"'+ expected: '"(1)(2)(3)(4)(5)"'++ - name: Implicit Iterator - Decimal+ desc: Implicit iterators should cast decimals to strings and interpolate.+ data:+ list: [ 1.10, 2.20, 3.30, 4.40, 5.50 ]+ template: '"{{#list}}({{.}}){{/list}}"'+ expected: '"(1.1)(2.2)(3.3)(4.4)(5.5)"'++ - name: Implicit Iterator - Array+ desc: Implicit iterators should allow iterating over nested arrays.+ data:+ list: [ [1, 2, 3], ['a', 'b', 'c'] ]+ template: '"{{#list}}({{#.}}{{.}}{{/.}}){{/list}}"'+ expected: '"(123)(abc)"'++ # Dotted Names++ - name: Dotted Names - Truthy+ desc: Dotted names should be valid for Section tags.+ data: { a: { b: { c: true } } }+ template: '"{{#a.b.c}}Here{{/a.b.c}}" == "Here"'+ expected: '"Here" == "Here"'++ - name: Dotted Names - Falsey+ desc: Dotted names should be valid for Section tags.+ data: { a: { b: { c: false } } }+ template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""'+ expected: '"" == ""'++ - name: Dotted Names - Broken Chains+ desc: Dotted names that cannot be resolved should be considered falsey.+ data: { a: { } }+ template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""'+ expected: '"" == ""'++ # Whitespace Sensitivity++ - name: Surrounding Whitespace+ desc: Sections should not alter surrounding whitespace.+ data: { boolean: true }+ template: " | {{#boolean}}\t|\t{{/boolean}} | \n"+ expected: " | \t|\t | \n"++ - name: Internal Whitespace+ desc: Sections should not alter internal whitespace.+ data: { boolean: true }+ template: " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"+ expected: " | \n | \n"++ - name: Indented Inline Sections+ desc: Single-line sections should not alter surrounding whitespace.+ data: { boolean: true }+ template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n"+ expected: " YES\n GOOD\n"++ - name: Standalone Lines+ desc: Standalone lines should be removed from the template.+ data: { boolean: true }+ template: |+ | This Is+ {{#boolean}}+ |+ {{/boolean}}+ | A Line+ expected: |+ | This Is+ |+ | A Line++ - name: Indented Standalone Lines+ desc: Indented standalone lines should be removed from the template.+ data: { boolean: true }+ template: |+ | This Is+ {{#boolean}}+ |+ {{/boolean}}+ | A Line+ expected: |+ | This Is+ |+ | A Line++ - name: Standalone Line Endings+ desc: '"\r\n" should be considered a newline for standalone tags.'+ data: { boolean: true }+ template: "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|"+ expected: "|\r\n|"++ - name: Standalone Without Previous Line+ desc: Standalone tags should not require a newline to precede them.+ data: { boolean: true }+ template: " {{#boolean}}\n#{{/boolean}}\n/"+ expected: "#\n/"++ - name: Standalone Without Newline+ desc: Standalone tags should not require a newline to follow them.+ data: { boolean: true }+ template: "#{{#boolean}}\n/\n {{/boolean}}"+ expected: "#\n/\n"++ # Whitespace Insensitivity++ - name: Padding+ desc: Superfluous in-tag whitespace should be ignored.+ data: { boolean: true }+ template: '|{{# boolean }}={{/ boolean }}|'+ expected: '|=|'
− specs/comments.yml
@@ -1,103 +0,0 @@-overview: |- Comment tags represent content that should never appear in the resulting- output.-- The tag's content may contain any substring (including newlines) EXCEPT the- closing delimiter.-- Comment tags SHOULD be treated as standalone when appropriate.-tests:- - name: Inline- desc: Comment blocks should be removed from the template.- data: { }- template: '12345{{! Comment Block! }}67890'- expected: '1234567890'-- - name: Multiline- desc: Multiline comments should be permitted.- data: { }- template: |- 12345{{!- This is a- multi-line comment...- }}67890- expected: |- 1234567890-- - name: Standalone- desc: All standalone comment lines should be removed.- data: { }- template: |- Begin.- {{! Comment Block! }}- End.- expected: |- Begin.- End.-- - name: Indented Standalone- desc: All standalone comment lines should be removed.- data: { }- template: |- Begin.- {{! Indented Comment Block! }}- End.- expected: |- Begin.- End.-- - name: Standalone Line Endings- desc: '"\r\n" should be considered a newline for standalone tags.'- data: { }- template: "|\r\n{{! Standalone Comment }}\r\n|"- expected: "|\r\n|"-- - name: Standalone Without Previous Line- desc: Standalone tags should not require a newline to precede them.- data: { }- template: " {{! I'm Still Standalone }}\n!"- expected: "!"-- - name: Standalone Without Newline- desc: Standalone tags should not require a newline to follow them.- data: { }- template: "!\n {{! I'm Still Standalone }}"- expected: "!\n"-- - name: Multiline Standalone- desc: All standalone comment lines should be removed.- data: { }- template: |- Begin.- {{!- Something's going on here...- }}- End.- expected: |- Begin.- End.-- - name: Indented Multiline Standalone- desc: All standalone comment lines should be removed.- data: { }- template: |- Begin.- {{!- Something's going on here...- }}- End.- expected: |- Begin.- End.-- - name: Indented Inline- desc: Inline comments should not strip whitespace- data: { }- template: " 12 {{! 34 }}\n"- expected: " 12 \n"-- - name: Surrounding Whitespace- desc: Comment removal should preserve surrounding whitespace.- data: { }- template: '12345 {{! Comment Block! }} 67890'- expected: '12345 67890'
− specs/delimiters.yml
@@ -1,158 +0,0 @@-overview: |- Set Delimiter tags are used to change the tag delimiters for all content- following the tag in the current compilation unit.-- The tag's content MUST be any two non-whitespace sequences (separated by- whitespace) EXCEPT an equals sign ('=') followed by the current closing- delimiter.-- Set Delimiter tags SHOULD be treated as standalone when appropriate.-tests:- - name: Pair Behavior- desc: The equals sign (used on both sides) should permit delimiter changes.- data: { text: 'Hey!' }- template: '{{=<% %>=}}(<%text%>)'- expected: '(Hey!)'-- - name: Special Characters- desc: Characters with special meaning regexen should be valid delimiters.- data: { text: 'It worked!' }- template: '({{=[ ]=}}[text])'- expected: '(It worked!)'-- - name: Sections- desc: Delimiters set outside sections should persist.- data: { section: true, data: 'I got interpolated.' }- template: |- [- {{#section}}- {{data}}- |data|- {{/section}}-- {{= | | =}}- |#section|- {{data}}- |data|- |/section|- ]- expected: |- [- I got interpolated.- |data|-- {{data}}- I got interpolated.- ]-- - name: Inverted Sections- desc: Delimiters set outside inverted sections should persist.- data: { section: false, data: 'I got interpolated.' }- template: |- [- {{^section}}- {{data}}- |data|- {{/section}}-- {{= | | =}}- |^section|- {{data}}- |data|- |/section|- ]- expected: |- [- I got interpolated.- |data|-- {{data}}- I got interpolated.- ]-- - name: Partial Inheritence- desc: Delimiters set in a parent template should not affect a partial.- data: { value: 'yes' }- partials:- include: '.{{value}}.'- template: |- [ {{>include}} ]- {{= | | =}}- [ |>include| ]- expected: |- [ .yes. ]- [ .yes. ]-- - name: Post-Partial Behavior- desc: Delimiters set in a partial should not affect the parent template.- data: { value: 'yes' }- partials:- include: '.{{value}}. {{= | | =}} .|value|.'- template: |- [ {{>include}} ]- [ .{{value}}. .|value|. ]- expected: |- [ .yes. .yes. ]- [ .yes. .|value|. ]-- # Whitespace Sensitivity-- - name: Surrounding Whitespace- desc: Surrounding whitespace should be left untouched.- data: { }- template: '| {{=@ @=}} |'- expected: '| |'-- - name: Outlying Whitespace (Inline)- desc: Whitespace should be left untouched.- data: { }- template: " | {{=@ @=}}\n"- expected: " | \n"-- - name: Standalone Tag- desc: Standalone lines should be removed from the template.- data: { }- template: |- Begin.- {{=@ @=}}- End.- expected: |- Begin.- End.-- - name: Indented Standalone Tag- desc: Indented standalone lines should be removed from the template.- data: { }- template: |- Begin.- {{=@ @=}}- End.- expected: |- Begin.- End.-- - name: Standalone Line Endings- desc: '"\r\n" should be considered a newline for standalone tags.'- data: { }- template: "|\r\n{{= @ @ =}}\r\n|"- expected: "|\r\n|"-- - name: Standalone Without Previous Line- desc: Standalone tags should not require a newline to precede them.- data: { }- template: " {{=@ @=}}\n="- expected: "="-- - name: Standalone Without Newline- desc: Standalone tags should not require a newline to follow them.- data: { }- template: "=\n {{=@ @=}}"- expected: "=\n"-- # Whitespace Insensitivity-- - name: Pair with Padding- desc: Superfluous in-tag whitespace should be ignored.- data: { }- template: '|{{= @ @ =}}|'- expected: '||'
− specs/interpolation.yml
@@ -1,238 +0,0 @@-overview: |- Interpolation tags are used to integrate dynamic content into the template.-- The tag's content MUST be a non-whitespace character sequence NOT containing- the current closing delimiter.-- This tag's content names the data to replace the tag. A single period (`.`)- indicates that the item currently sitting atop the context stack should be- used; otherwise, name resolution is as follows:- 1) Split the name on periods; the first part is the name to resolve, any- remaining parts should be retained.- 2) Walk the context stack from top to bottom, finding the first context- that is a) a hash containing the name as a key OR b) an object responding- to a method with the given name.- 3) If the context is a hash, the data is the value associated with the- name.- 4) If the context is an object, 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.- Data should be coerced into a string (and escaped, if appropriate) before- interpolation.-- The Interpolation tags MUST NOT be treated as standalone.-tests:- - name: No Interpolation- desc: Mustache-free templates should render as-is.- data: { }- template: |- Hello from {Mustache}!- expected: |- Hello from {Mustache}!-- - name: Basic Interpolation- desc: Unadorned tags should interpolate content into the template.- data: { subject: "world" }- template: |- Hello, {{subject}}!- expected: |- Hello, world!-- - name: HTML Escaping- desc: Basic interpolation should be HTML escaped.- data: { forbidden: '& " < >' }- template: |- These characters should be HTML escaped: {{forbidden}}- expected: |- These characters should be HTML escaped: & " < >-- - name: Triple Mustache- desc: Triple mustaches should interpolate without HTML escaping.- data: { forbidden: '& " < >' }- template: |- These characters should not be HTML escaped: {{{forbidden}}}- expected: |- These characters should not be HTML escaped: & " < >-- - name: Ampersand- desc: Ampersand should interpolate without HTML escaping.- data: { forbidden: '& " < >' }- template: |- These characters should not be HTML escaped: {{&forbidden}}- expected: |- These characters should not be HTML escaped: & " < >-- - name: Basic Integer Interpolation- desc: Integers should interpolate seamlessly.- data: { mph: 85 }- template: '"{{mph}} miles an hour!"'- expected: '"85 miles an hour!"'-- - name: Triple Mustache Integer Interpolation- desc: Integers should interpolate seamlessly.- data: { mph: 85 }- template: '"{{{mph}}} miles an hour!"'- expected: '"85 miles an hour!"'-- - name: Ampersand Integer Interpolation- desc: Integers should interpolate seamlessly.- data: { mph: 85 }- template: '"{{&mph}} miles an hour!"'- expected: '"85 miles an hour!"'-- - name: Basic Decimal Interpolation- desc: Decimals should interpolate seamlessly with proper significance.- data: { power: 1.210 }- template: '"{{power}} jiggawatts!"'- expected: '"1.21 jiggawatts!"'-- - name: Triple Mustache Decimal Interpolation- desc: Decimals should interpolate seamlessly with proper significance.- data: { power: 1.210 }- template: '"{{{power}}} jiggawatts!"'- expected: '"1.21 jiggawatts!"'-- - name: Ampersand Decimal Interpolation- desc: Decimals should interpolate seamlessly with proper significance.- data: { power: 1.210 }- template: '"{{&power}} jiggawatts!"'- expected: '"1.21 jiggawatts!"'-- # Context Misses-- - name: Basic Context Miss Interpolation- desc: Failed context lookups should default to empty strings.- data: { }- template: "I ({{cannot}}) be seen!"- expected: "I () be seen!"-- - name: Triple Mustache Context Miss Interpolation- desc: Failed context lookups should default to empty strings.- data: { }- template: "I ({{{cannot}}}) be seen!"- expected: "I () be seen!"-- - name: Ampersand Context Miss Interpolation- desc: Failed context lookups should default to empty strings.- data: { }- template: "I ({{&cannot}}) be seen!"- expected: "I () be seen!"-- # Dotted Names-- - name: Dotted Names - Basic Interpolation- desc: Dotted names should be considered a form of shorthand for sections.- data: { person: { name: 'Joe' } }- template: '"{{person.name}}" == "{{#person}}{{name}}{{/person}}"'- expected: '"Joe" == "Joe"'-- - name: Dotted Names - Triple Mustache Interpolation- desc: Dotted names should be considered a form of shorthand for sections.- data: { person: { name: 'Joe' } }- template: '"{{{person.name}}}" == "{{#person}}{{{name}}}{{/person}}"'- expected: '"Joe" == "Joe"'-- - name: Dotted Names - Ampersand Interpolation- desc: Dotted names should be considered a form of shorthand for sections.- data: { person: { name: 'Joe' } }- template: '"{{&person.name}}" == "{{#person}}{{&name}}{{/person}}"'- expected: '"Joe" == "Joe"'-- - name: Dotted Names - Arbitrary Depth- desc: Dotted names should be functional to any level of nesting.- data:- a: { b: { c: { d: { e: { name: 'Phil' } } } } }- template: '"{{a.b.c.d.e.name}}" == "Phil"'- expected: '"Phil" == "Phil"'-- - name: Dotted Names - Broken Chains- desc: Any falsey value prior to the last part of the name should yield ''.- data:- a: { }- template: '"{{a.b.c}}" == ""'- expected: '"" == ""'-- - name: Dotted Names - Broken Chain Resolution- desc: Each part of a dotted name should resolve only against its parent.- data:- a: { b: { } }- c: { name: 'Jim' }- template: '"{{a.b.c.name}}" == ""'- expected: '"" == ""'-- - name: Dotted Names - Initial Resolution- desc: The first part of a dotted name should resolve as any other name.- data:- a: { b: { c: { d: { e: { name: 'Phil' } } } } }- b: { c: { d: { e: { name: 'Wrong' } } } }- template: '"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"'- expected: '"Phil" == "Phil"'-- - name: Dotted Names - Context Precedence- desc: Dotted names should be resolved against former resolutions.- data:- a: { b: { } }- b: { c: 'ERROR' }- template: '{{#a}}{{b.c}}{{/a}}'- expected: ''-- # Whitespace Sensitivity-- - name: Interpolation - Surrounding Whitespace- desc: Interpolation should not alter surrounding whitespace.- data: { string: '---' }- template: '| {{string}} |'- expected: '| --- |'-- - name: Triple Mustache - Surrounding Whitespace- desc: Interpolation should not alter surrounding whitespace.- data: { string: '---' }- template: '| {{{string}}} |'- expected: '| --- |'-- - name: Ampersand - Surrounding Whitespace- desc: Interpolation should not alter surrounding whitespace.- data: { string: '---' }- template: '| {{&string}} |'- expected: '| --- |'-- - name: Interpolation - Standalone- desc: Standalone interpolation should not alter surrounding whitespace.- data: { string: '---' }- template: " {{string}}\n"- expected: " ---\n"-- - name: Triple Mustache - Standalone- desc: Standalone interpolation should not alter surrounding whitespace.- data: { string: '---' }- template: " {{{string}}}\n"- expected: " ---\n"-- - name: Ampersand - Standalone- desc: Standalone interpolation should not alter surrounding whitespace.- data: { string: '---' }- template: " {{&string}}\n"- expected: " ---\n"-- # Whitespace Insensitivity-- - name: Interpolation With Padding- desc: Superfluous in-tag whitespace should be ignored.- data: { string: "---" }- template: '|{{ string }}|'- expected: '|---|'-- - name: Triple Mustache With Padding- desc: Superfluous in-tag whitespace should be ignored.- data: { string: "---" }- template: '|{{{ string }}}|'- expected: '|---|'-- - name: Ampersand With Padding- desc: Superfluous in-tag whitespace should be ignored.- data: { string: "---" }- template: '|{{& string }}|'- expected: '|---|'
− specs/inverted.yml
@@ -1,193 +0,0 @@-overview: |- Inverted Section tags and End Section tags are used in combination to wrap a- section of the template.-- These tags' content MUST be a non-whitespace character sequence NOT- containing the current closing delimiter; each Inverted Section tag MUST be- followed by an End Section tag with the same content within the same- section.-- This tag's content names the data to replace the tag. Name resolution is as- follows:- 1) Split the name on periods; the first part is the name to resolve, any- remaining parts should be retained.- 2) Walk the context stack from top to bottom, finding the first context- that is a) a hash containing the name as a key OR b) an object responding- to a method with the given name.- 3) If the context is a hash, the data is the value associated with the- name.- 4) If the context is an object and the method with the given name has an- arity of 1, the method SHOULD be called with a String containing the- unprocessed contents of the sections; the data is the value returned.- 5) Otherwise, the data is the value returned by calling the method with- the given name.- 6) If any name parts were retained in step 1, each should be resolved- against a context stack containing only the result from the former- resolution. If any part fails resolution, the result should be considered- falsey, and should interpolate as the empty string.- If the data is not of a list type, it is coerced into a list as follows: if- the data is truthy (e.g. `!!data == true`), use a single-element list- containing the data, otherwise use an empty list.-- This section MUST NOT be rendered unless the data list is empty.-- Inverted Section and End Section tags SHOULD be treated as standalone when- appropriate.-tests:- - name: Falsey- desc: Falsey sections should have their contents rendered.- data: { boolean: false }- template: '"{{^boolean}}This should be rendered.{{/boolean}}"'- expected: '"This should be rendered."'-- - name: Truthy- desc: Truthy sections should have their contents omitted.- data: { boolean: true }- template: '"{{^boolean}}This should not be rendered.{{/boolean}}"'- expected: '""'-- - name: Context- desc: Objects and hashes should behave like truthy values.- data: { context: { name: 'Joe' } }- template: '"{{^context}}Hi {{name}}.{{/context}}"'- expected: '""'-- - name: List- desc: Lists should behave like truthy values.- data: { list: [ { n: 1 }, { n: 2 }, { n: 3 } ] }- template: '"{{^list}}{{n}}{{/list}}"'- expected: '""'-- - name: Empty List- desc: Empty lists should behave like falsey values.- data: { list: [ ] }- template: '"{{^list}}Yay lists!{{/list}}"'- expected: '"Yay lists!"'-- - name: Doubled- desc: Multiple inverted sections per template should be permitted.- data: { bool: false, two: 'second' }- template: |- {{^bool}}- * first- {{/bool}}- * {{two}}- {{^bool}}- * third- {{/bool}}- expected: |- * first- * second- * third-- - name: Nested (Falsey)- desc: Nested falsey sections should have their contents rendered.- data: { bool: false }- template: "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |"- expected: "| A B C D E |"-- - name: Nested (Truthy)- desc: Nested truthy sections should be omitted.- data: { bool: true }- template: "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |"- expected: "| A E |"-- - name: Context Misses- desc: Failed context lookups should be considered falsey.- data: { }- template: "[{{^missing}}Cannot find key 'missing'!{{/missing}}]"- expected: "[Cannot find key 'missing'!]"-- # Dotted Names-- - name: Dotted Names - Truthy- desc: Dotted names should be valid for Inverted Section tags.- data: { a: { b: { c: true } } }- template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == ""'- expected: '"" == ""'-- - name: Dotted Names - Falsey- desc: Dotted names should be valid for Inverted Section tags.- data: { a: { b: { c: false } } }- template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here"'- expected: '"Not Here" == "Not Here"'-- - name: Dotted Names - Broken Chains- desc: Dotted names that cannot be resolved should be considered falsey.- data: { a: { } }- template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here"'- expected: '"Not Here" == "Not Here"'-- # Whitespace Sensitivity-- - name: Surrounding Whitespace- desc: Inverted sections should not alter surrounding whitespace.- data: { boolean: false }- template: " | {{^boolean}}\t|\t{{/boolean}} | \n"- expected: " | \t|\t | \n"-- - name: Internal Whitespace- desc: Inverted should not alter internal whitespace.- data: { boolean: false }- template: " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"- expected: " | \n | \n"-- - name: Indented Inline Sections- desc: Single-line sections should not alter surrounding whitespace.- data: { boolean: false }- template: " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n"- expected: " NO\n WAY\n"-- - name: Standalone Lines- desc: Standalone lines should be removed from the template.- data: { boolean: false }- template: |- | This Is- {{^boolean}}- |- {{/boolean}}- | A Line- expected: |- | This Is- |- | A Line-- - name: Standalone Indented Lines- desc: Standalone indented lines should be removed from the template.- data: { boolean: false }- template: |- | This Is- {{^boolean}}- |- {{/boolean}}- | A Line- expected: |- | This Is- |- | A Line-- - name: Standalone Line Endings- desc: '"\r\n" should be considered a newline for standalone tags.'- data: { boolean: false }- template: "|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|"- expected: "|\r\n|"-- - name: Standalone Without Previous Line- desc: Standalone tags should not require a newline to precede them.- data: { boolean: false }- template: " {{^boolean}}\n^{{/boolean}}\n/"- expected: "^\n/"-- - name: Standalone Without Newline- desc: Standalone tags should not require a newline to follow them.- data: { boolean: false }- template: "^{{^boolean}}\n/\n {{/boolean}}"- expected: "^\n/\n"-- # Whitespace Insensitivity-- - name: Padding- desc: Superfluous in-tag whitespace should be ignored.- data: { boolean: false }- template: '|{{^ boolean }}={{/ boolean }}|'- expected: '|=|'
− specs/partials.yml
@@ -1,109 +0,0 @@-overview: |- Partial tags are used to 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 the partial to inject. Set Delimiter tags MUST NOT- affect the parsing of a partial. The partial MUST be rendered against the- context stack local to the tag. If the named partial cannot be found, the- empty string SHOULD be used instead, as in interpolations.-- 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 greater-than operator should expand to the named partial.- data: { }- template: '"{{>text}}"'- partials: { text: 'from partial' }- expected: '"from partial"'-- - name: Failed Lookup- desc: The empty string should be used when the named partial is not found.- data: { }- template: '"{{>text}}"'- partials: { }- expected: '""'-- - name: Context- desc: The greater-than operator should operate within the current context.- data: { text: 'content' }- template: '"{{>partial}}"'- partials: { partial: '*{{text}}*' }- expected: '"*content*"'-- - name: Recursion- desc: The greater-than operator should properly recurse.- data: { content: "X", nodes: [ { content: "Y", nodes: [] } ] }- template: '{{>node}}'- partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' }- expected: 'X<Y<>>'-- # Whitespace Sensitivity-- - name: Surrounding Whitespace- desc: The greater-than operator should not alter surrounding whitespace.- data: { }- template: '| {{>partial}} |'- partials: { partial: "\t|\t" }- expected: "| \t|\t |"-- - name: Inline Indentation- desc: Whitespace should be left untouched.- data: { 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: { }- template: "|\r\n{{>partial}}\r\n|"- partials: { partial: ">" }- expected: "|\r\n>|"-- - name: Standalone Without Previous Line- desc: Standalone tags should not require a newline to precede them.- data: { }- template: " {{>partial}}\n>"- partials: { partial: ">\n>"}- expected: " >\n >>"-- - name: Standalone Without Newline- desc: Standalone tags should not require a newline to follow them.- data: { }- template: ">\n {{>partial}}"- partials: { partial: ">\n>" }- expected: ">\n >\n >"-- - name: Standalone Indentation- desc: Each line of the partial should be indented before rendering.- data: { content: "<\n->" }- template: |- \- {{>partial}}- /- partials:- partial: |- |- {{{content}}}- |- expected: |- \- |- <- ->- |- /-- # Whitespace Insensitivity-- - name: Padding Whitespace- desc: Superfluous in-tag whitespace should be ignored.- data: { boolean: true }- template: "|{{> partial }}|"- partials: { partial: "[]" }- expected: '|[]|'
− specs/sections.yml
@@ -1,263 +0,0 @@-overview: |- Section tags and End Section tags are used in combination to wrap a section- of the template for iteration-- These tags' content MUST be a non-whitespace character sequence NOT- containing the current closing delimiter; each Section tag MUST be followed- by an End Section tag with the same content within the same section.-- This tag's content names the data to replace the tag. Name resolution is as- follows:- 1) Split the name on periods; the first part is the name to resolve, any- remaining parts should be retained.- 2) Walk the context stack from top to bottom, finding the first context- that is a) a hash containing the name as a key OR b) an object responding- to a method with the given name.- 3) If the context is a hash, the data is the value associated with the- name.- 4) If the context is an object and the method with the given name has an- arity of 1, the method SHOULD be called with a String containing the- unprocessed contents of the sections; the data is the value returned.- 5) Otherwise, the data is the value returned by calling the method with- the given name.- 6) If any name parts were retained in step 1, each should be resolved- against a context stack containing only the result from the former- resolution. If any part fails resolution, the result should be considered- falsey, and should interpolate as the empty string.- If the data is not of a list type, it is coerced into a list as follows: if- the data is truthy (e.g. `!!data == true`), use a single-element list- containing the data, otherwise use an empty list.-- For each element in the data list, the element MUST be pushed onto the- context stack, the section MUST be rendered, and the element MUST be popped- off the context stack.-- Section and End Section tags SHOULD be treated as standalone when- appropriate.-tests:- - name: Truthy- desc: Truthy sections should have their contents rendered.- data: { boolean: true }- template: '"{{#boolean}}This should be rendered.{{/boolean}}"'- expected: '"This should be rendered."'-- - name: Falsey- desc: Falsey sections should have their contents omitted.- data: { boolean: false }- template: '"{{#boolean}}This should not be rendered.{{/boolean}}"'- expected: '""'-- - name: Context- desc: Objects and hashes should be pushed onto the context stack.- data: { context: { name: 'Joe' } }- template: '"{{#context}}Hi {{name}}.{{/context}}"'- expected: '"Hi Joe."'-- - name: Deeply Nested Contexts- desc: All elements on the context stack should be accessible.- data:- a: { one: 1 }- b: { two: 2 }- c: { three: 3 }- d: { four: 4 }- e: { five: 5 }- template: |- {{#a}}- {{one}}- {{#b}}- {{one}}{{two}}{{one}}- {{#c}}- {{one}}{{two}}{{three}}{{two}}{{one}}- {{#d}}- {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}- {{#e}}- {{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}- {{/e}}- {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}- {{/d}}- {{one}}{{two}}{{three}}{{two}}{{one}}- {{/c}}- {{one}}{{two}}{{one}}- {{/b}}- {{one}}- {{/a}}- expected: |- 1- 121- 12321- 1234321- 123454321- 1234321- 12321- 121- 1-- - name: List- desc: Lists should be iterated; list items should visit the context stack.- data: { list: [ { item: 1 }, { item: 2 }, { item: 3 } ] }- template: '"{{#list}}{{item}}{{/list}}"'- expected: '"123"'-- - name: Empty List- desc: Empty lists should behave like falsey values.- data: { list: [ ] }- template: '"{{#list}}Yay lists!{{/list}}"'- expected: '""'-- - name: Doubled- desc: Multiple sections per template should be permitted.- data: { bool: true, two: 'second' }- template: |- {{#bool}}- * first- {{/bool}}- * {{two}}- {{#bool}}- * third- {{/bool}}- expected: |- * first- * second- * third-- - name: Nested (Truthy)- desc: Nested truthy sections should have their contents rendered.- data: { bool: true }- template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"- expected: "| A B C D E |"-- - name: Nested (Falsey)- desc: Nested falsey sections should be omitted.- data: { bool: false }- template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"- expected: "| A E |"-- - name: Context Misses- desc: Failed context lookups should be considered falsey.- data: { }- template: "[{{#missing}}Found key 'missing'!{{/missing}}]"- expected: "[]"-- # Implicit Iterators-- - name: Implicit Iterator - String- desc: Implicit iterators should directly interpolate strings.- data:- list: [ 'a', 'b', 'c', 'd', 'e' ]- template: '"{{#list}}({{.}}){{/list}}"'- expected: '"(a)(b)(c)(d)(e)"'-- - name: Implicit Iterator - Integer- desc: Implicit iterators should cast integers to strings and interpolate.- data:- list: [ 1, 2, 3, 4, 5 ]- template: '"{{#list}}({{.}}){{/list}}"'- expected: '"(1)(2)(3)(4)(5)"'-- - name: Implicit Iterator - Decimal- desc: Implicit iterators should cast decimals to strings and interpolate.- data:- list: [ 1.10, 2.20, 3.30, 4.40, 5.50 ]- template: '"{{#list}}({{.}}){{/list}}"'- expected: '"(1.1)(2.2)(3.3)(4.4)(5.5)"'-- - name: Implicit Iterator - Array- desc: Implicit iterators should allow iterating over nested arrays.- data:- list: [ [1, 2, 3], ['a', 'b', 'c'] ]- template: '"{{#list}}({{#.}}{{.}}{{/.}}){{/list}}"'- expected: '"(123)(abc)"'-- # Dotted Names-- - name: Dotted Names - Truthy- desc: Dotted names should be valid for Section tags.- data: { a: { b: { c: true } } }- template: '"{{#a.b.c}}Here{{/a.b.c}}" == "Here"'- expected: '"Here" == "Here"'-- - name: Dotted Names - Falsey- desc: Dotted names should be valid for Section tags.- data: { a: { b: { c: false } } }- template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""'- expected: '"" == ""'-- - name: Dotted Names - Broken Chains- desc: Dotted names that cannot be resolved should be considered falsey.- data: { a: { } }- template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""'- expected: '"" == ""'-- # Whitespace Sensitivity-- - name: Surrounding Whitespace- desc: Sections should not alter surrounding whitespace.- data: { boolean: true }- template: " | {{#boolean}}\t|\t{{/boolean}} | \n"- expected: " | \t|\t | \n"-- - name: Internal Whitespace- desc: Sections should not alter internal whitespace.- data: { boolean: true }- template: " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"- expected: " | \n | \n"-- - name: Indented Inline Sections- desc: Single-line sections should not alter surrounding whitespace.- data: { boolean: true }- template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n"- expected: " YES\n GOOD\n"-- - name: Standalone Lines- desc: Standalone lines should be removed from the template.- data: { boolean: true }- template: |- | This Is- {{#boolean}}- |- {{/boolean}}- | A Line- expected: |- | This Is- |- | A Line-- - name: Indented Standalone Lines- desc: Indented standalone lines should be removed from the template.- data: { boolean: true }- template: |- | This Is- {{#boolean}}- |- {{/boolean}}- | A Line- expected: |- | This Is- |- | A Line-- - name: Standalone Line Endings- desc: '"\r\n" should be considered a newline for standalone tags.'- data: { boolean: true }- template: "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|"- expected: "|\r\n|"-- - name: Standalone Without Previous Line- desc: Standalone tags should not require a newline to precede them.- data: { boolean: true }- template: " {{#boolean}}\n#{{/boolean}}\n/"- expected: "#\n/"-- - name: Standalone Without Newline- desc: Standalone tags should not require a newline to follow them.- data: { boolean: true }- template: "#{{#boolean}}\n/\n {{/boolean}}"- expected: "#\n/\n"-- # Whitespace Insensitivity-- - name: Padding- desc: Superfluous in-tag whitespace should be ignored.- data: { boolean: true }- template: '|{{# boolean }}={{/ boolean }}|'- expected: '|=|'
stache.cabal view
@@ -31,7 +31,7 @@ -- POSSIBILITY OF SUCH DAMAGE. name: stache-version: 0.1.7+version: 0.1.8 cabal-version: >= 1.10 license: BSD3 license-file: LICENSE.md@@ -46,7 +46,7 @@ extra-doc-files: CHANGELOG.md , README.md data-files: bench-data/*.mustache- , specs/*.yml+ , specification/*.yml , templates/*.mustache source-repository head@@ -97,7 +97,7 @@ , hspec >= 2.0 && < 3.0 , hspec-megaparsec >= 0.2 && < 0.4 , megaparsec >= 5.0 && < 6.0- , stache >= 0.1.7+ , stache >= 0.1.8 , text >= 1.2 && < 1.3 other-modules: Text.Mustache.Compile.THSpec , Text.Mustache.ParserSpec@@ -122,7 +122,7 @@ , file-embed , hspec >= 2.0 && < 3.0 , megaparsec >= 5.0 && < 6.0- , stache >= 0.1.7+ , stache >= 0.1.8 , text >= 1.2 && < 1.3 , yaml >= 0.8 && < 0.9 if flag(dev)@@ -140,10 +140,10 @@ , criterion >= 0.6.2.1 && < 1.2 , deepseq >= 1.4 && < 1.5 , megaparsec >= 5.0 && < 6.0- , stache >= 0.1.7+ , stache >= 0.1.8 , text >= 1.2 && < 1.3 if flag(dev)- ghc-options: -Wall -Werror+ ghc-options: -O2 -Wall -Werror else ghc-options: -O2 -Wall default-language: Haskell2010