summaryrefslogtreecommitdiff
path: root/specs/~inheritance.yml
blob: 07fa08198ed10af8fc7b3a57e4d22ddfc00e1b98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
overview: |
  Like partials, Parent tags are used to expand an external template into the
  current template. Unlike partials, Parent tags may contain optional
  arguments delimited by Block tags. For this reason, Parent tags may also be
  referred to as Parametric Partials.

  The Parent tags' content MUST be a non-whitespace character sequence NOT
  containing the current closing delimiter; each Parent tag MUST be followed by
  an End Section tag with the same content within the matching Parent tag.

  This tag's content names the Parent template to inject. Set Delimiter tags
  Preceding a Parent tag MUST NOT affect the parsing of the injected external
  template. The Parent MUST be rendered against the context stack local to the
  tag. If the named Parent cannot be found, the empty string SHOULD be used
  instead, as in interpolations.

  Parent tags SHOULD be treated as standalone when appropriate. If this tag is
  used standalone, any whitespace preceding the tag should be treated as
  indentation, and prepended to each line of the Parent before rendering.

  The Block tags' content MUST be a non-whitespace character sequence NOT
  containing the current closing delimiter. Each Block tag MUST be followed by
  an End Section tag with the same content within the matching Block tag. This
  tag's content determines the parameter or argument name.

  Block tags may appear both inside and outside of Parent tags. In both cases,
  they specify a position within the template that can be overridden; it is a
  parameter of the containing template. The template text between the Block tag
  and its matching End Section tag defines the default content to render when
  the parameter is not overridden from outside.

  In addition, when used inside of a Parent tag, the template text between a
  Block tag and its matching End Section tag defines content that replaces the
  default defined in the Parent template. This content is the argument passed
  to the Parent template.

  The practice of injecting an external template using a Parent tag is referred
  to as inheritance. If the Parent tag includes a Block tag that overrides a
  parameter of the Parent template, this may also be referred to as
  substitution.

  Parent templates are taken from the same namespace as regular Partial
  templates and in fact, injecting a regular Partial is exactly equivalent to
  injecting a Parent without making any substitutions. Parameter and arguments
  names live in a namespace that is distinct from both Partials and the context.
tests:
  - name: Default
    desc: Default content should be rendered if the block isn't overridden
    data: { }
    template: |
      {{$title}}Default title{{/title}}
    expected: |
      Default title

  - name: Variable
    desc: Default content renders variables
    data: { bar: 'baz' }
    template: |
      {{$foo}}default {{bar}} content{{/foo}}
    expected: |
      default baz content

  - name: Triple Mustache
    desc: Default content renders triple mustache variables
    data: { bar: '<baz>' }
    template: |
      {{$foo}}default {{{bar}}} content{{/foo}}
    expected: |
      default <baz> content

  - name: Sections
    desc: Default content renders sections
    data: { bar: {baz: 'qux'} }
    template: |
      {{$foo}}default {{#bar}}{{baz}}{{/bar}} content{{/foo}}
    expected: |
      default qux content

  - name: Negative Sections
    desc: Default content renders negative sections
    data: { baz: 'three' }
    template: |
      {{$foo}}default {{^bar}}{{baz}}{{/bar}} content{{/foo}}
    expected: |
      default three content

  - name: Mustache Injection
    desc: Mustache injection in default content
    data: {bar: {baz: '{{qux}}'} }
    template: |
      {{$foo}}default {{#bar}}{{baz}}{{/bar}} content{{/foo}}
    expected: |
      default {{qux}} content

  - name: Inherit
    desc: Default content rendered inside inherited templates
    data: { }
    template: |
      {{<include}}{{/include}}
    partials:
      include: "{{$foo}}default content{{/foo}}"
    expected: "default content"

  - name: Overridden content
    desc: Overridden content
    data: { }
    template: "{{<super}}{{$title}}sub template title{{/title}}{{/super}}"
    partials:
      super: "...{{$title}}Default title{{/title}}..."
    expected: "...sub template title..."

  - name: Data does not override block
    desc: Context does not override argument passed into parent
    data: { var: 'var in data' }
    template: "{{<include}}{{$var}}var in template{{/var}}{{/include}}"
    partials:
      include: "{{$var}}var in include{{/var}}"
    expected: "var in template"

  - name: Data does not override block default
    desc: Context does not override default content of block
    data: { var: 'var in data' }
    template: "{{<include}}{{/include}}"
    partials:
      include: "{{$var}}var in include{{/var}}"
    expected: "var in include"

  - name: Overridden parent
    desc: Overridden parent
    data: { }
    template: "test {{<parent}}{{$stuff}}override{{/stuff}}{{/parent}}"
    partials:
      parent: "{{$stuff}}...{{/stuff}}"
    expected: "test override"

  - name: Two overridden parents
    desc: Two overridden parents with different content
    data: { }
    template: |
      test {{<parent}}{{$stuff}}override1{{/stuff}}{{/parent}} {{<parent}}{{$stuff}}override2{{/stuff}}{{/parent}}
    partials:
      parent: "|{{$stuff}}...{{/stuff}}{{$default}} default{{/default}}|"
    expected: |
      test |override1 default| |override2 default|

  - name: Override parent with newlines
    desc: Override parent with newlines
    data: { }
    template: "{{<parent}}{{$ballmer}}\npeaked\n\n:(\n{{/ballmer}}{{/parent}}"
    partials:
      parent: "{{$ballmer}}peaking{{/ballmer}}"
    expected: "peaked\n\n:(\n"

  - name: Inherit indentation
    desc: Inherit indentation when overriding a parent
    data: { }
    template: "{{<parent}}{{$nineties}}hammer time{{/nineties}}{{/parent}}"
    partials:
      parent: |
        stop:
          {{$nineties}}collaborate and listen{{/nineties}}
    expected: |
      stop:
        hammer time

  - name: Only one override
    desc: Override one parameter but not the other
    data: { }
    template: "{{<parent}}{{$stuff2}}override two{{/stuff2}}{{/parent}}"
    partials:
      parent: "{{$stuff}}new default one{{/stuff}}, {{$stuff2}}new default two{{/stuff2}}"
    expected: "new default one, override two"

  - name: Parent template
    desc: Parent templates behave identically to partials when called with no parameters
    data: { }
    template: "{{>parent}}|{{<parent}}{{/parent}}"
    partials:
      parent: "{{$foo}}default content{{/foo}}"
    expected: "default content|default content"

  - name: Recursion
    desc: Recursion in inherited templates
    data: {}
    template: "{{<parent}}{{$foo}}override{{/foo}}{{/parent}}"
    partials:
      parent: "{{$foo}}default content{{/foo}} {{$bar}}{{<parent2}}{{/parent2}}{{/bar}}"
      parent2: "{{$foo}}parent2 default content{{/foo}} {{<parent}}{{$bar}}don't recurse{{/bar}}{{/parent}}"
    expected: "override override override don't recurse"

  - name: Multi-level inheritance
    desc: Top-level substitutions take precedence in multi-level inheritance
    data: { }
    template: "{{<parent}}{{$a}}c{{/a}}{{/parent}}"
    partials:
      parent: "{{<older}}{{$a}}p{{/a}}{{/older}}"
      older: "{{<grandParent}}{{$a}}o{{/a}}{{/grandParent}}"
      grandParent: "{{$a}}g{{/a}}"
    expected: c

  - name: Multi-level inheritance, no sub child
    desc: Top-level substitutions take precedence in multi-level inheritance
    data: { }
    template: "{{<parent}}{{/parent}}"
    partials:
      parent: "{{<older}}{{$a}}p{{/a}}{{/older}}"
      older: "{{<grandParent}}{{$a}}o{{/a}}{{/grandParent}}"
      grandParent: "{{$a}}g{{/a}}"
    expected: p

  - name: Text inside parent
    desc: Ignores text inside parent templates, but does parse $ tags
    data: { }
    template: "{{<parent}} asdfasd {{$foo}}hmm{{/foo}} asdfasdfasdf {{/parent}}"
    partials:
      parent: "{{$foo}}default content{{/foo}}"
    expected:
      hmm

  - name: Text inside parent
    desc: Allows text inside a parent tag, but ignores it
    data: {}
    template: "{{<parent}} asdfasd asdfasdfasdf {{/parent}}"
    partials:
      parent: "{{$foo}}default content{{/foo}}"
    expected: default content