-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add template inheritance spec #75
Conversation
Hey, @groue — I remember you saying GRMustache was moving to this style of inheritance as well... Do you mind running this spec against it and seeing how it works? |
Hi @bobthecow. Yes, GRMustache passes all those tests (modulo white-space conformance, that GRMustache has never focused on). Generally speaking, GRMustache passes all inheritance tests from this PR, from hogan.js, and spullara/mustache.java. I'm curious about your own implementations' conformance to GRMustache tests as well - they cover a few cases not included in this PR:
|
@jazzdan wrote:
There are four or them, actually :-) You can add groue/GRMustache. |
Your third bullet (separate namespace for inheritance vs. regular variable keys) was addressed in the PHP implementation. |
@groue This one is interesting, and I'm not sure I'm convinced it's the right way to treat it: Sell me on it :) |
I just tested the PHP implementation against your second bullet point (several conflicting inheritable sections), and that works as well. |
Okay. Your inheritable partials tests mostly pass in the PHP implementation (2 failures, 5 errors). The two failures are due to treatment of blocks inside sections (my "not convinced" comment above). The five errors are exceptions thrown because Mustache.php, like Hogan.js, throws errors for things like this: {{< parent }}
{{# section }}this will throw an exception!{{/ section }}
{{/ parent }} |
Thanks for your time @bobthecow :-)
Generally speaking, I tend to think that this behavior is required by those two arguments:
The two arguments join on the "context" word: if the layout template changes the context, inherited sections need to render in this context. Hence the test you question, and the example below:
Rendering:
|
I see. What is the intent? To prevent users to embed ignored content? If so, do you throw exception for plain text as well? If not, what is the intent of you exception?
|
@groue I'm actually leaning toward that (see comment). The reason it came up was things like this:
… which are weird. I'd be in favor of explicitly whitelisting only a few things that can live in the top level of a parent tag:
|
@bobthecow All right, I understand, and I'm not against returning errors for all ignored tags, but the ones you are listing: white space, comments, inheritable sections, and change-delimiter tags. BTW, GRMustache also has tests for such parsing errors: https://github.com/groue/GRMustache/blob/GRMustache7/src/tests/Public/v7.0/Suites/groue:GRMustache/GRMustacheSuites/tag_parsing_errors.json & https://github.com/groue/GRMustache/blob/GRMustache7/src/tests/Public/v7.0/Suites/groue:GRMustache/GRMustacheSuites/expression_parsing_errors.json Back on your exceptions: There are only six GRMustache inheritance tests which involve regular sections: Only 1 of them embeds a regular section inside a inheritable section, and has your implementation throw an exception. Let's forget about this one: The others should not raise any exception. I mean, your implementation does not have to handle them, but you may be interested in looking at them more precisely:
|
Those aren't the ones that raised exceptions, these are: [
// ...
{
"name": "The content of the last inheritable sections in partials is rendered in the inherited section",
"data": { },
"template": "{{<partial1}}{{$inheritable}}1{{/inheritable}}{{>partial2}}{{$inheritable}}4{{/inheritable}}{{/partial1}}",
"partials": {
"partial1": "{{$inheritable}}ignored{{/inheritable}}",
"partial2": "{{$inheritable}}2{{/inheritable}}{{$inheritable}}3{{/inheritable}}" },
"expected": "4"
},
{
"name": "The content of the last inheritable sections in partials is rendered in the inherited section",
"data": { },
"template": "{{<partial1}}{{$inheritable}}1{{/inheritable}}{{>partial2}}{{/partial1}}",
"partials": {
"partial1": "{{$inheritable}}ignored{{/inheritable}}",
"partial2": "{{$inheritable}}2{{/inheritable}}{{$inheritable}}3{{/inheritable}}" },
"expected": "3"
},
// ...
{
"name": "Partials in inheritable partials can override inheritable sections",
"data": { },
"template": "{{<partial2}}{{>partial1}}{{/partial2}}",
"partials": {
"partial1": "{{$inheritable}}partial1{{/inheritable}}",
"partial2": "{{$inheritable}}ignored{{/inheritable}}" },
"expected": "partial1"
},
// ...
{
"name": "Templates sections can not override inheritable sections in inheritable partial",
"data": { "section": true },
"template": "{{<partial}}{{#section}}inherited{{/section}}{{/partial}}",
"partials": { "partial": "{{$section}}success{{/section}}" },
"expected": "success"
},
// ...
] Note that they all include tags besides inheritable sections inside parent tags, which is the current criteria for throwing an exception. I did just realize that two of these revolve around allowing partials to define inheritable sections. I'm inclined to push back against this one and say only inheritable sections actually defined inside the parent tag count. |
All right, you can do as you wish. My take on that point is as stated above:
|
That's not constructive. I'm trying to have a conversation and come to a consensus about common functionality so that we can actually standardize this behavior. |
But that's currently not the case. Consider this template:
You can't extract any one line of this template into a partial, because the whole thing will break. I'm just saying that I feel like inheritable sections are a special case too. |
I'm sorry. I did not want to pollute @jazzdan's PR. I did state my reason after. Your answer:
Yes, you are right. Set delimiter tags ruin everything. I wish they were not there. I never use them. They are a hack. That's why I think my take is still valid. And this is why GRMustache tests for snippet-extraction-into-partials at several levels. So that I'm sure my users can extract whatever they want into a partial, as long as it looks sensible to them. Who am I to say it's forbidden? Not implemented, why not. But forbidden? When they have a legitimate need? When it can be implemented in a rigourous way? When it has been implemented? You may not include those tests in this spec. This is what I meant by "you can do as you wish". But I won't remove this feature from GRMustache. |
About free partials extraction, let me quote @defunkt on http://mustache.github.io/mustache.5.html
This is the spirit. |
Allowing partials inside parent tags changes what would be a parse error ("unexpected section tag inside a parent tag!") into a runtime exception, because you can't be sure that a partial won't include something that isn't allowed directly inside a parent tag. It complicates things :) |
What runtime exception are you talking about? I'm not aware of runtime errors I should raise when rendering plain Mustache templates. In GRMustache, the only runtime errors are for missing filters (a feature added by GRMustache, not in the spec: unknown filter |
If things like section tags are not allowed directly inside parent tags, this would be a parse error:
But this would be a runtime error:
|
Well, in your last example, we would have a parse error because the |
This thing you just said:
Directly contradicts that other thing you keep saying:
… and, in fact, agrees completely with what I'm trying to say. So maybe we misunderstand each other :) |
@bobthecow, I think you mix two questions:
The two topics are different. Your last example talks about the white-list. Since partial tags should raise a parsing exception, according to this white-list rule, this example would not raise any runtime exception. About the second topic: please consider that all the partial-extraction tests are rendered by GRMustache. They work. They have a meaning. They test the fact that one can extract a partial from any template snippet, as long as the template snippet can be rendered, and that the two new templates are valid. |
I was, and have been, talking about the four tests with exceptions I cited above. They throw exceptions because they include tags that are not on the whitelist. I thought you were talking about them as well, and saying that they should be valid. |
The three first tests are about choosing which overriding section wins, when several ones could be rendered. They make sure the last one wins. They do not involve the white-list. The last test could raise an exception, with the white-list. Today, it just makes sure an regular section is ignored, and does not mess with inheritance. |
All three of these raise exceptions, because they all have partials tags directly inside parent tags:
|
OMG let me check my tests |
Ha yeah, I got it :-) Those tests are a consequence of general partial extraction. Given the following template:
The user may want to extract the content into a partial:
So if I come back to the white-list, I would state the partial tags should be white-listed as well. |
The last example may look contrived. I have to imagine somebody who wants to reuse common inheritable sections for various layouts, and then extracts them into a partial. |
The specs come from the semi-official specification mustache/spec#75
The implementation follows the spec proposal: mustache/spec#75 which is supported by at least the following Mustache implementations: - hogan.js - mustache.php - mustache.java - GRMustache (Obj-C) and GRMustache.swift (Swift) - Text::Caml (Perl) - hxmustache (Haxe) - MuttonChop (Swift)
The specs come from the semi-official specification mustache/spec#75
The implementation follows the spec proposal: mustache/spec#75 which is supported by at least the following Mustache implementations: - hogan.js - mustache.php - mustache.java - GRMustache (Obj-C) and GRMustache.swift (Swift) - Text::Caml (Perl) - hxmustache (Haxe) - MuttonChop (Swift)
The specs come from the semi-official specification mustache/spec#75
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a really old PR and I'm a bit of an outsider since I only just wrote my own Mustache implementation, but I figured a review might still be useful since this PR hasn't been merged yet and I might offer a "fresh perspective" as a newcomer. For clarity, my own implementation doesn't support this spec yet, but I do want to support it.
Semantically and syntactically speaking, I'm perfectly happy with the spec as-is, and I agree that it should be merged in v2. The fact that it is already existing practice in multiple implementations also makes the case for this spec stronger in my opinion. I would however propose to make two superficial (but important) changes:
- Change the spec name from
inheritance.yml
to~inheritance.yml
to mark it as an optional module. It complicates the implementation a bit, so it might deter new implementers otherwise. Edit to add: after Add template inheritance spec #75 (comment), I would suggest~parametric-partial.yml
instead. - Make the jargon much more consistent. I found
{{$block}}
being referred to as "block", "parameter" and "substitution" and{{<parent}}
as "parent", "super", "include" and even "partial".Especially "partial" should really be avoided in my opinion, as it is needlessly confusing.I started to make change suggestions for the incorrect appearances of "partial", but I didn't go all the way through. I would suggest usingonly "parent" or "super" for"parameter block" for{{<parent}}
and{{$block}}
when used by itself or "argument block" when used inside{{<parent}}...{{/parent}}
.. Edit to add: I changed my opinion about this and I now believe that "parent", "super" and "inheritance" are actually the words that should be avoided. See Add template inheritance spec #75 (comment).
Some other remarks/observations:
- I'm in favor of specifying that interpolation and section tags inside argument blocks should be evaluated in the context of the parent template, as proposed by @gasche in Add template inheritance spec #75 (comment) after finding that this is the existing practice in mustache.php. This makes it possible to implement inheritance by inlining the parent template, making precompiled templates potentially faster. It is also more consistent with the same paths already occurring inside the parameter block default in the parent template.
- I really like the chosen sigils/indicators/whathever they are called.
<
makes sense because in a way, it is dual to the>
for partials.$
makes sense because the character is used for variable names or parameter expansions in several programming languages. - Maybe it would be an option to open a "future" branch so that changes for v2 can already be merged into the repository?
- name: Overridden partial | ||
desc: Overridden partial | ||
data: { } | ||
template: "test {{<partial}}{{$stuff}}override{{/stuff}}{{/partial}}" | ||
partials: | ||
partial: "{{$stuff}}...{{/stuff}}" | ||
expected: "test override" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the wording "partial" is rather unfortunate here, as neither the parent nor the template involves {{>partial}}
syntax. I suggest the following instead.
- name: Overridden partial | |
desc: Overridden partial | |
data: { } | |
template: "test {{<partial}}{{$stuff}}override{{/stuff}}{{/partial}}" | |
partials: | |
partial: "{{$stuff}}...{{/stuff}}" | |
expected: "test override" | |
- name: Overriden content with text in front | |
desc: Overriden content with text in front of parent | |
data: { } | |
template: "test {{<parent}}{{$stuff}}override{{/stuff}}{{/parent}}" | |
partials: | |
parent: "{{$stuff}}...{{/stuff}}" | |
expected: "test override" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I retract the above comment.
- name: Two overridden partials | ||
desc: Two overridden partials with different content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- name: Two overridden partials | |
desc: Two overridden partials with different content | |
- name: Overrides in twice inherited parent | |
desc: Overrides in twice inherited parent with different content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I retract the above comment.
expected: "peaked\n\n:(\n" | ||
|
||
- name: Inherit indentation | ||
desc: Inherit indentation when overriding a partial |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
desc: Inherit indentation when overriding a partial | |
desc: Inherit indentation when overriding a parent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I retract the above comment.
- name: Override partial with newlines | ||
desc: Override partial with newlines |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- name: Override partial with newlines | |
desc: Override partial with newlines | |
- name: Override parent with newlines | |
desc: Override parent with newlines |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I retract the above comment.
stop: | ||
hammer time | ||
|
||
- name: Only one override |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- name: Only one override | |
- name: Only one override |
Thanks for the carefully worded message! I agree that it's nice to push this further: I think that with the amount of implementations that support the feature, it's a bit silly to refer to it as a PR to the original spec.
If this is made an optional part of the spec, does it need to correspond to a major-version bump for the specification? I haven't thought hard about the versioning scheme but I would think as adding optional aspects as a backward-compatibility-preserving changes (implementations remain compliant). (One exception would be an implementation that provides a feature using the same syntax as proposed here but with different semantics, then I guess it would become non-compliant?)
I agree that the terminology could be made more consistent. I myself use Regarding the whole feature or Another remark: upon implementing the feature I found that it shows the limits of the current way whitespace is handled in Mustache (the notion of standalone tags, and the indentation of partials). Ideally the user would want:
Long story short, I went down a rabbit hole of finding better specifications for whitespace handling in Mustache, and they tended to be more complex, more natural for the user, and incompatible with existing implementations / the spec. in corner cases. I gave up and went back to something mostly-standard (my implementation does substract the indentation of the parameter block, iirc.), but it remains a (minor) sticking point with the feature and its specification. |
Good point about "partials without parameters" and "partials with parameters". I now realize they are more similar than I thought, and also that the implementation can probably be more similar than I thought.
I was thinking along these lines, too, but I didn't dare write it down. I'm glad you did. With it being an optional module, I feel it could be a minor version bump. |
Since my review and previous comment, I have radically changed my opinion about jargon consistency. I now believe that "parent", "super" and "inheritance" are the needlessly confusing words that should be avoided. The thing we're talking about here is just a parametric partial. I would suggest renaming the spec to |
Could you elaborate on this? The way I've understood standalone tags so far, it just means that a single tag that is all alone on a line shouldn't leave a blank line in the output, i.e., {{!comment}}
foo
{{#section}}
content
{{/section}} should produce
(assuming that
. Given this interpretation, I have no imagination of what it would even mean for a parameter block as a whole to count as a standalone tag. Consider the following example: foo
{{$parameter}}content{{/parameter}}
bar What do you believe should happen? Surely not that the entire line containing
If you mean that {{!partial.mustache}}
foo
bar
{{$parameter1}}baz{{/parameter1}}
{{$parameter2}}foobar{{/parameter2}}
{{!template.mustache}}
foobaz
barbaz
{{<partial}}
foofoo
{{$parameter1}}barbar{{/parameter1}}
{{$parameter2}}
bazbaz
{{/parameter2}}
{{/partial}} should output
then I think I agree with you.
Does this really need to be fully specified before merging, though? I think the existing spec is worthwhile as-is, even if it leaves some freedom to implementers with regard to whitespace handling. |
Regarding the terminology: I would be a bit careful here, to accommodate several viewpoints. I think that to some people thinking of those new partials as "functions with parameters" will be most helpful, but some other would be familiar with the "object-oriented inheritance" view. We could have a description that mentions both views, and I think we could manage to consistently/clearly use two terminologies. Note that the two notions are not completely equivalent in what semantics they suggest: in case of shadowing (two nested partial-with-parameters define parameters of the same name), the function view would suggest that the innermost definition (the callee) should override the outermost one, while the inheritance view suggests that the outermost definition (the parent class) should override the innermost one; and it is that latter view that was chosen (possibly without realizing the choice) in the original implementation of partial-with-parameters, and followed by further implementations. |
Regarding whitespace: I think that, ideally
and
should result in the same printing. However, with the current notion of "standalone" tag, then I think that a better concept of whitespace handling should be about whether templating constructs (at rendering time) emit "visible" content on the current line, or they don't. If a "standalone" construct (not
No, I think that this does not need to be fully specified before merging, and adding that requirement could be fatal to this PR who already had a long and unsatisfying life. My personal opinion is that this PR should be merged (marked optional) into the specification, to recognize the current status that it is consensual among implementations, and largely implemented. It is a de-facto (optional) standard, and everyone's life would be easier if it was recognized as such. My current understanding is that the notion of "standalone token" as currently somewhat-specified (you have to read a lot between the lines already) is not such a good fit for "we want the nice whitespace behavior a human would expect"; it is doing okay for existing constructs (although arguably |
@gasche Thank you for taking the effort to have this interesting discussion with me. Regarding terminology, point taken. I should mention that my opinion is not so strong that I believe it should block merging. I would like to see it be more consistent, but since the terminology is not going to change the syntactical or semantical content of the specs, we are going to get the same result in the end and I'll have the same work to do in my own implementation. For academic interest, I'll mention that parametric partials behave exactly like functions with default arguments. Indeed, nested function calls with defaults along the chain will give the same behavior where the outermost caller wins. On the other hand, a template that calls multiple (parametric) partials behaves more like composition than like multiple inheritance, when looking from an OOP perspective. Partials might be deeply nested within sections from the "inheriting" template, giving it complete control to wrap and organize the partials however the author wants. In OOP, inheritance generally isn't that flexible but composition is. Regarding whitespace, I think I actually prefer the whitespace handling as it is currently specified, although I agree that @Danappelxx @spullara I could prepare a continuation PR that renames the spec to start with a |
Personally I think this would be a good idea. Thanks for your active contributions to the mustache spec! |
This comment has been minimized.
This comment has been minimized.
Closing in favor of #125, which looks like it will be merged soon - further comments should be added there. Thank you to everyone for facilitating a healthy discussion. |
CHANGES: * Remove the AST without locations: now all functions build an AST with locations; in particular, parsing always provide located error messages. To ease backward-compatibility, the smart constructors still use the same interface, using dummy locations by default, with a With_locations module for users who wish to explicitly provide locations. (@gasche, rgrinberg/ocaml-mustache#65) * Support for "template inheritance" (partials with parameters) `{{<foo}} {{$param1}}...{{/param1}} {{$param2}}...{{/param2}} {{/foo}` following the widely-implemented semi-official specification mustache/spec#75 (@gasche, 58) * Partials are now supported in the `mustache` command-line tool (@gasche, rgrinberg/ocaml-mustache#57) They are interpreted as template inclusion: "{{>foo/bar}}" will include "foo/bar.mustache", relative to the current working directory. * Improve error messages (@gasche, rgrinberg/ocaml-mustache#47, rgrinberg/ocaml-mustache#51, rgrinberg/ocaml-mustache#56) Note: the exceptions raised by Mustache have changed, this breaks compatibility for users that would catch and deconstruct existing exceptions. * Add `render_buf` to render templates directly to buffers (@gasche, rgrinberg/ocaml-mustache#48) * When a lookup fails in the current context, lookup in parents contexts. This should fix errors when using "{{#foo}}" for a scalar variable 'foo' to check that the variable exists. (@gasche, rgrinberg/ocaml-mustache#49)
CHANGES: * Remove the AST without locations: now all functions build an AST with locations; in particular, parsing always provide located error messages. To ease backward-compatibility, the smart constructors still use the same interface, using dummy locations by default, with a With_locations module for users who wish to explicitly provide locations. (@gasche, rgrinberg/ocaml-mustache#65) * Support for "template inheritance" (partials with parameters) `{{<foo}} {{$param1}}...{{/param1}} {{$param2}}...{{/param2}} {{/foo}` following the widely-implemented semi-official specification mustache/spec#75 (@gasche, 58) * Partials are now supported in the `mustache` command-line tool (@gasche, rgrinberg/ocaml-mustache#57) They are interpreted as template inclusion: "{{>foo/bar}}" will include "foo/bar.mustache", relative to the current working directory. * Improve error messages (@gasche, rgrinberg/ocaml-mustache#47, rgrinberg/ocaml-mustache#51, rgrinberg/ocaml-mustache#56) Note: the exceptions raised by Mustache have changed, this breaks compatibility for users that would catch and deconstruct existing exceptions. * Add `render_buf` to render templates directly to buffers (@gasche, rgrinberg/ocaml-mustache#48) * When a lookup fails in the current context, lookup in parents contexts. This should fix errors when using "{{#foo}}" for a scalar variable 'foo' to check that the variable exists. (@gasche, rgrinberg/ocaml-mustache#49)
CHANGES: * Remove the AST without locations: now all functions build an AST with locations; in particular, parsing always provide located error messages. To ease backward-compatibility, the smart constructors still use the same interface, using dummy locations by default, with a With_locations module for users who wish to explicitly provide locations. (@gasche, rgrinberg/ocaml-mustache#65) * Support for "template inheritance" (partials with parameters) `{{<foo}} {{$param1}}...{{/param1}} {{$param2}}...{{/param2}} {{/foo}` following the widely-implemented semi-official specification mustache/spec#75 (@gasche, 58) * Partials are now supported in the `mustache` command-line tool (@gasche, rgrinberg/ocaml-mustache#57) They are interpreted as template inclusion: "{{>foo/bar}}" will include "foo/bar.mustache", relative to the current working directory. * Improve error messages (@gasche, rgrinberg/ocaml-mustache#47, rgrinberg/ocaml-mustache#51, rgrinberg/ocaml-mustache#56) Note: the exceptions raised by Mustache have changed, this breaks compatibility for users that would catch and deconstruct existing exceptions. * Add `render_buf` to render templates directly to buffers (@gasche, rgrinberg/ocaml-mustache#48) * When a lookup fails in the current context, lookup in parents contexts. This should fix errors when using "{{#foo}}" for a scalar variable 'foo' to check that the variable exists. (@gasche, rgrinberg/ocaml-mustache#49)
CHANGES: * Remove the AST without locations: now all functions build an AST with locations; in particular, parsing always provide located error messages. To ease backward-compatibility, the smart constructors still use the same interface, using dummy locations by default, with a With_locations module for users who wish to explicitly provide locations. (@gasche, rgrinberg/ocaml-mustache#65) * Support for "template inheritance" (partials with parameters) `{{<foo}} {{$param1}}...{{/param1}} {{$param2}}...{{/param2}} {{/foo}` following the widely-implemented semi-official specification mustache/spec#75 (@gasche, 58) * Partials are now supported in the `mustache` command-line tool (@gasche, rgrinberg/ocaml-mustache#57) They are interpreted as template inclusion: "{{>foo/bar}}" will include "foo/bar.mustache", relative to the current working directory. * Improve error messages (@gasche, rgrinberg/ocaml-mustache#47, rgrinberg/ocaml-mustache#51, rgrinberg/ocaml-mustache#56) Note: the exceptions raised by Mustache have changed, this breaks compatibility for users that would catch and deconstruct existing exceptions. * Add `render_buf` to render templates directly to buffers (@gasche, rgrinberg/ocaml-mustache#48) * When a lookup fails in the current context, lookup in parents contexts. This should fix errors when using "{{#foo}}" for a scalar variable 'foo' to check that the variable exists. (@gasche, rgrinberg/ocaml-mustache#49)
CHANGES: * Remove the AST without locations: now all functions build an AST with locations; in particular, parsing always provide located error messages. To ease backward-compatibility, the smart constructors still use the same interface, using dummy locations by default, with a With_locations module for users who wish to explicitly provide locations. (@gasche, rgrinberg/ocaml-mustache#65) * Support for "template inheritance" (partials with parameters) `{{<foo}} {{$param1}}...{{/param1}} {{$param2}}...{{/param2}} {{/foo}` following the widely-implemented semi-official specification mustache/spec#75 (@gasche, 58) * Partials are now supported in the `mustache` command-line tool (@gasche, rgrinberg/ocaml-mustache#57) They are interpreted as template inclusion: "{{>foo/bar}}" will include "foo/bar.mustache", relative to the current working directory. * Improve error messages (@gasche, rgrinberg/ocaml-mustache#47, rgrinberg/ocaml-mustache#51, rgrinberg/ocaml-mustache#56) Note: the exceptions raised by Mustache have changed, this breaks compatibility for users that would catch and deconstruct existing exceptions. * Add `render_buf` to render templates directly to buffers (@gasche, rgrinberg/ocaml-mustache#48) * When a lookup fails in the current context, lookup in parents contexts. This should fix errors when using "{{#foo}}" for a scalar variable 'foo' to check that the variable exists. (@gasche, rgrinberg/ocaml-mustache#49)
This adds a specification for hogan.js style template inheritance. As of time of writing, three mustache implementations are 100% compatible with this implementation:
Here's how it works:
Rendering mypage.mustache would output:
A full specification can be seen below.
At @etsy, much like @twitter (see #38), we often want to have one template where we maintain the "bones" of the page and each real page on the site merely replaces sections of that page with appropriate content. A simple example of this can be seen above, where a page can grab base, getting all of our analytics scripts and other markup that every page needs, while retaining the ability to swap in a completely different header. Alternatives, such as copying the bones to every top level template, or simulating this behavior with a multi-step mustache render on the server, are not ideal.
Etsy is in the process of rolling out the new version of mustache.php that supports template inheritance to our production cluster after testing this implementation extensively. Hogan.js and mustache.java are both being used in production at Twitter.
Many thanks to @dtb, @mdg, @bobthecow for their assistance. Special thanks to @spullara for quickly making mustache.java compatible with this spec.