Skip to content
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

Implement template inheritance via {{% BLOCKS }} pragma. #130

Closed
wants to merge 1 commit into from

Conversation

bobthecow
Copy link
Owner

This is a proof-of-concept implementation of template inheritance via blocks.

Say we have a template named layout.mustache. This would be our generic site layout template, consisting of all the sorts of things you'd expect in a layout template. In addition, it would have a number of blocks defined. The blocks look like Mustache sections, but they're denoted with {{$ block }} tags instead of #.

<!-- layout.mustache -->
{{% BLOCKS }}
<!DOCTYPE html>
<html>
  <head>
    <title>{{$ title }}My Alsome Site{{/ title }}</title>
    {{$ stylesheets }}
      <link rel="stylesheet" href="/assets/css/default.css">
    {{/ stylesheets }}
  </head>
  <body>
    <header>
      {{$ header }}
        <h1>Welcome to My Alsome Site</h1>
      {{/ header }}
    </header>
    <section id="content">
      {{$ content }}
        <p>Hello, World!</p>
      {{/ content }}
    </section>
    {{$ scripts }}
      <script src="/assets/js/default.js"></script>
    {{/ scripts }}
  </body>
</html>

The blocks defined here are {{$ title }}, {{$ stylesheets }}, {{$ header }}, {{$ content }} and {{$ scripts }}.

By default, blocks merely delineate sections of the template which can be extended (replaced) in subtemplates. If the parent template itself is rendered, the block tags are simply rendered as if they are truthy sections.

This layout template can then be extended by other sub-templates, which declare their intention by adding a parent=layout to the {{% BLOCKS }} pragma tag:

<!-- page.mustache -->
{{% BLOCKS parent=layout}}

{{$ title }}My Alsome Page{{/ title }}

{{$ stylesheets }}
  <link rel="stylesheet" href="/assets/css/page.css">
{{/ stylesheets }}

{{$ header }}
  <h1>My page has some items!</h1>
{{/ header }}

{{$ content }}
  <ul>
    {{# items }}
      <li><a href="{{ link }}" title="{{ name }}">{{ name }}</a></li>
    {{/ items }}
  </ul>
{{/ content }}

Upon rendering this page.mustache, the subtemplate blocks will be substituted for the parent blocks, resulting in (approximately) this:

<!DOCTYPE html>
<html>
  <head>
    <title>My Alsome Page</title>
    <link rel="stylesheet" href="/assets/css/page.css">
  </head>
  <body>
    <header>
      <h1>My page has some items!</h1>
    </header>
    <section id="content">
      <ul>
        {{# items }}
          <li><a href="{{ link }}" title="{{ name }}">{{ name }}</a></li>
        {{/ items }}
      </ul>
    </section>
    <script src="/assets/js/default.js"></script>
  </body>
</html>

In our example, we didn't override every block: the {{$ scripts }} block retained its default value. We could also create a new subtemplate which extends page.mustache, say custom-page.mustache. That subtemplate might only replace the {{$ content }} block of page.mustache, and inherit the rest.

Note that page.mustache could even define new blocks. Maybe add a {{$ main }} and {{$ complimentary }} section inside {{$ content }}:

{{$ content }}
  <div id="main" role="main">
    {{$ main }}{{/ main }}
  </div>
  <div id="complimentary" role="complimentary">
    {{$ complimentary }}{{/ complimentary }}
  </div>
{{/ content }}

Then sub-subtemplates could choose to override just the {{$ main }} block, while leaving {{$ content }} and {{$ complimentary }} intact.

@bobthecow
Copy link
Owner Author

See also mustache/spec#38 for an ongoing discussion (with several recommendations which resemble this one).

@rafi
Copy link

rafi commented Jan 19, 2013

Awesome.. Can't wait. Is this planned for 2.2 or 2.3 ?

@bobthecow
Copy link
Owner Author

Probably 2.2.

@nicolas-t
Copy link

It seems that this feature has been implemented in the current version (2.3) of mustache.php, but I can't find the documentation for it.
Should I use the OP's syntax ?
Thanks

@rafi
Copy link

rafi commented Apr 21, 2013

It doesn't look like feature/template-inheritance branch has been merged into 2.3

@bobthecow
Copy link
Owner Author

@rafi is right. This feature has not been merged yet. Feel free to use the feature branch in the interim though.

@nicolas-t
Copy link

oops sorry, didn't notice there is a branch per feature.
Why isn't the template-inheritance not merged yet ? can I help ?

@bobthecow
Copy link
Owner Author

@nicolas-t It hasn't been merged because there's not really a consensus on how template inheritance should work in Mustache yet. See mustache/spec#38 for discussion.

Specifically:

  • This implementation only allows single inheritance, while the Twitter implementation allows multiple inheritance.
  • GRMustache and this implementation use {{$ foo }} to denote inheritance blocks, while the Twitter implementation uses {{< foo }}.
  • This implementation and the Twitter implementation override parent blocks, while GRMustache concatenates parent blocks.

@rafi
Copy link

rafi commented Aug 9, 2013

Any news on this awesome feature?
Can it be merged safely with 2.4.0 ?

@bobthecow
Copy link
Owner Author

I just rebased the feature branch onto dev, so it has all the latest and greatest v2.4.0 stuffs.

@bobthecow
Copy link
Owner Author

@calmdev I got a notification (and see a comment on your profile) but nothing shows up here. I take it you got it working?

@calmdev
Copy link

calmdev commented Sep 9, 2013

@bobthecow actually no, I was not able to get it working. I tried with the latest dev and feature/template-inheritance branch. It's odd I spent all weekend trying to figure out why it would not work. I removed the comment here, because I didn't want to confuse anyone until I was absolutely sure it was not working....

I downloaded a copy of the master back in January for a project I was working on before this PR was opened and remember manually merging the changes into the master that I was working with when I saw this PR. For some reason that copy works perfectly fine, but is obviously well behind now.

I'd really love it if you could verify that the examples posted here are working for you with the lastest dev and feature/template-inheritance branches to assure I'm not just going crazy. I'm working on a implementation for use with a custom content management system on www.fit.edu, but it might be a show stopper or create some resistance with the managers if I am not able to get the latest versions working.

I'm itchin' to get us moved over to Mustache.php

The issue I was running into seemed specifically related to the parser throwing an exception when blocks were encountered.

@WinWinHost
Copy link

Hey Guys,
I tried to run some of templates via the PHP parser, and I got errors around where template inheritance happens.
Not sure what the issue is.

We have a JAR to do the same thing, and I wanted to implement the PHP version, being a LAMP guy.
The main thing is this jar has handlebars in the name. I thought they do the same thing.
Maybe I am confused, handlebars vs mustache. And not an issue of PHP vs Java.

@WinWinHost
Copy link

Yeah, so I think there is a difference between JAVA and PHP versions.
https://gist.github.com/spullara/1854699

So how does the open source community stay on the same page?
I see someone else is doing the JAVA version, and there is already a big difference between features.
Kind of a killed my entire LAMP stack implementation of mustache.

@bobthecow
Copy link
Owner Author

@WinWinHost they're different because there's no consensus on how it should work between the different implementations, and it's not in the spec. There are almost as many ways to implement template inheritance as there are mustache libraries with template inheritance. Unless and until it actually gets standardized, there will be differences.

@WinWinHost
Copy link

What do you think the LOE would be on rewriting that functionality over into the PHP version?
And if some one were to do it, do you think you would merge it in?
It would be great if I could have a PHP equal to stand up to the Java boys.

@rafi
Copy link

rafi commented Dec 21, 2013

What's keeping this branch out of master?

@bobthecow
Copy link
Owner Author

@rafi This hasn't been released because there's no consensus on template inheritance between the different mustache implementations (see my comment above)

@dtb
Copy link

dtb commented Feb 3, 2014

Hey @bobthecow, @jazzdan and I were having trouble getting this branch to work. We created this test, which fails with Mustache_Exception_SyntaxException: Unexpected closing tag: /title (stack trace).

Next we found that Mustache_Parser seems to refer to a pragmas variable, which doesn't exist. We applied this patch to see what would happen, and then the parser error went away, but the output seemed to only include the content from the child template, and not from the layout/parent template (output here).

@bobthecow
Copy link
Owner Author

@dtb Unfortunately this branch is effectively abandoned at this point. It hasn't kept up with development of the main library, and probably needs to be reimplemented on a fork from the current dev branch. See above for why I haven't taken that on yet :)

@bobthecow
Copy link
Owner Author

This will be deprecated by #198 when it's merged, so I'm preemptively closing this pull request.

@bobthecow bobthecow closed this Apr 6, 2014
@bobthecow bobthecow deleted the feature/template-inheritance branch April 1, 2015 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants