mustache / spec

The Mustache spec.
MIT License
364 stars 71 forks source link

Dynamic Partials and Partial Collections through Coercion #54

Closed ghost closed 2 years ago

ghost commented 12 years ago

I've just created a pull request over at https://github.com/janl/mustache.js/pull/242

I am using this solution in a couple of projects and it addresses a number of concerns I often hear with regards dynamic partials in Mustache, please see ticket (https://github.com/janl/mustache.js/pull/242) and documentation here:

Really, I urge everyone to have a play with it as it has cleaned up my templates no end.

Kind regards,

Jamie

devinrhode2 commented 12 years ago

The proposed syntax:

base.mustache
{{#items}}
<p>{{>.}}</p>
{{^items}}

and

base.mustache
{{@items}}

Assume there is a key named 'partial' and then looks up that partial. The idea of dynamic partials is great, but I don't know about assuming a certain key to be present is the most developer friendly approach and the easiest to understand quickly. It could instead by {{>.partialName}} which, when the lookup for the literal key '.partialName' fails, it resolves .partialName to 'text' and looks that up.

It's clearly valuable, but I don't think it fits in the spec for 2.0

ghost commented 12 years ago

See my response here: https://github.com/janl/mustache.js/pull/242#issuecomment-8645571

As for it not fitting the 2.0 spec, there is currently no way to render partials dynamically which I see as a significant drawback. This is fine for smaller projects as you just add a boolean to each object in a collection such as is_text or is_image and have conditions in the template:

View

{
  items: [
    { type: 'image', url: 'Some URL', is_image: true },
    { type: 'text', content: 'Some text', is_text: true }
  ]
}
{{#items}}
  {{#is_text}}
    <p>{{content}}</p>
  {{/is_text}}
  {{#is_image}}
    <p><img src="{{url}}"/></p>
  {{/is_image?}}
{{/items}}

But... as soon as this goes above a couple of types, things get hard to manage. The whole premise of Mustache is to keep logic out of the template and this reads to me as a bunch of if statements.

Wouldn't it be nicer to be able to do:

base.mustache
{{@items}}

text.mustache
<p>{{content}}</p>

image.mustache
<p><img src="{{url}}"/></p>

So much cleaner, don't you think?

Sorry to labour my point, but I'm using this in production and it's changed my life :)

groue commented 12 years ago

@thelucid As usual, when one's code contains too many if, it means that you need a to inject a little object-oriented design. Let's replace your is_text, is_image "queriable" attributes by some "performing" attributes, and use Mustache lambdas:

{
  items: [
    { url:     'Some URL',  html: function() { return '<p><img src="{{url}}"/></p>'; } },
    { content: 'Some text', html: function() { return '<p>{{content}}</p>'; } }
  ]
}

base.mustache:
{{#items}}
  <p>{{{html}}}<p>
{{/items}}

And, if you absolutely want to use partials:

{
  items: [
    { url: 'Some URL',      html: function() { return '{{>image}}'; } },
    { content: 'Some text', html: function() { return '{{>text}}'; } },
  ]
}

base.mustache
{{#items}}
  {{{html}}}
{{/items}}

text.mustache
<p>{{content}}</p>

image.mustache
<p><img src="{{url}}"/></p>

And voilà, no need for a spec update!

ghost commented 12 years ago

@groue Ew, I'm sorry but this is exactly why the functionality is needed. Returning chunks of html from the view is in no way a good solution to the problem... I would hope I'm not alone on this.

This is in fact a perfect illustration as to why we need a clean solution to this problem, if hacks like @groue is suggesting are being suggested then something is fundamentally wrong.

groue commented 12 years ago

@thelucid You call it a "hack" because you see data as a model model object, not as a view model. I can understand you: Mustache has always been ambiguous on that subject: advocating simplicity, saying "just give me your model objects, I'll render them", while defending itself against missing features with arguments like "just add the missing keys to your view model, you shouldn't have provided a model in the first place anyway".

Mustache spec issues history is full on this kind of situations, if you read them carefully.

So, in a way, I gave an answer that is typical of the "Mustache 1" spirit. Be free to suggest improvements for Mustache 2. But dont' call "hacks" ways to use Mustache that you just don't happen to like.

ghost commented 12 years ago

@groue I'm sorry, I just don't see that returning a chunk of html from a view object (be it a view model or a data model) is a maintainable solution. Where do you draw the line, you may as well bypass Mustache completely and just interpolate strings.

I would much rather do:

base.mustache
{{@items}}

text.mustache
<p>{{content}}</p>

image.mustache
<p><img src="{{url}}"/></p>
groue commented 12 years ago

@thelucid Have you read my message? Check after the "And, if you absolutely want to use partials:" sentence.

ghost commented 12 years ago

@groue Yes, I have read the message and there are a few problems with the suggested partial solution:

  1. It's ugly but that's just my opinion.
  2. It's not DRY, you have two near-identical functions.
  3. It's unmaintainable, i.e you will have to add a function for every new partial.
  4. It's open to injection attacks since you are using the {{{html}}} approach.

Injection example:

{
  items: [
    { url: 'Some URL',      html: function() { return '{{>image}}'; } },
    { content: 'Some text', html: function() { return '{{>text}}'; } },
    { html: '<script>alert("I am hijacking your page")</script>' }
  ]
}
groue commented 12 years ago

It's ugly but that's just my opinion.

As much as the is_text and type attributes, and more OO.

It's not DRY

Single interface + Liskov substitution principle is no DRY for you? Gosh, I can't see how more DRY it could be.

It's unmaintainable, i.e you will have to add a function for every new partial.

Your solution must keep synced the type value and the partial name, which is just the same chore.

It's open to injection attacks since you are using the {{{html}}} approach.

Is it? open a new issue, and ask for a removal of the triple mustache.

Listen: I won't defend my proposal more than necessary. Actually, I'm quite sure you didn't know about the lambda before I introduced them to you. Think more about the tool, make sure you know every tiny details: Mustache is more rich that it looks. Maybe you'll find a solution that conforms to your standards with the spec as it is.

ghost commented 12 years ago

I'm at a loss as to why you're against a clean solution to dynamic partials.

As much as the is_text and type attributes, and more OO.

That is exactly why I am suggesting such a feature, it is dry and concise.

Single interface + Liskov substitution principle is no DRY for you? Gosh, I can't see how more DRY it could be.

You are still duplicating a function which is not DRY.

Your solution must keep synced the type value and the partial name, which is just the same chore.

Yes, but you are just specifying the partial to render, not a function that actually renders a chunk of html.

Is it? open a new issue, and ask for a removal of the triple mustache.

The triple mustache doesn't escape the value by design, that's what it's for and how it works. So yes, your example is open to injection attacks... try it if you don't believe me:

View

{
  html: '<script>alert("See, I have control");</script>'
}

Template

{{{html}}}

I did know about lambdas before you mentioned them (that's a little patronising, especially given your lack of understanding when it comes to the triple mustache), I just don't see them as a good fit for this particular problem.

We'll just have to agree to disagree on this one.

Maybe you'll find a solution that conforms to your standards with the spec as it is.

There is currently no way to render dynamic partials other than the suggestions we have already exhausted here, hence this ticket.

groue commented 12 years ago

I'm not against your solution. I just wanted to make sure everybody knows that there are already existing solutions. Usually, people ask for a feature because they don't know their tool enough. Sorry if I have patronized.

About the triple mustache: they allow injection in all situations, not only in the one we are talking about here. What I meant was that the topic of injection should hence not pollute this topic, and that you can ask for the removal of the triple mustache in some other issue.

devinrhode2 commented 12 years ago

I think @groue has a great point. This is already possible, therefore it's best not to add a feature for it.

I vote no.

ghost commented 12 years ago

It's possible to drive a nail into a block of wood with a mallet but it's not necessarily the best tool for the job.

devinrhode2 commented 12 years ago

I think letting{{>string}}first lookup the partial named 'string' - and then fallback to looking up the value of the string would certainly be acceptable. This would work perfectly without changing anything, only at a point of failure does it kick in.

And with @bobthecow's suggestion of moar dot notation hotness, you could do{{>.string}}

ghost commented 12 years ago

Yes, that's not too bad, what about the {{@collection}} shorthand?

ghost commented 12 years ago

The only potential problem with that solution is that it's a little ambiguous i.e. the same syntax does two different things. Having a separate syntax makes it easier to see what is going on.

bobthecow commented 12 years ago

That should be explicit, if that's what you want. Otherwise you're opening yourself up for exploits. Maybe use a "variable dereferencing" type token? {{> *string }}

ghost commented 12 years ago

What about using the @ symbol for everything related to dynamic partials e.g.

{{#items}}
{{>@partial}}
{{/items}}

or shorthand that assumes a partial key:

{{@items}}

I would personally prefer it being implicit, going with a convention whereby a partial key is always assumed when using dynamic partials i.e.

{{#items}}
{{@}}
{{/items}}

and shorthand:

{{@items}}

The @ symbol then means "render an object or collection of objects using their partial keys". Of course, the key doesn't necessarily have to be partial, perhaps > would be a better fit to denote a partial? ...hmm, maybe not, partial does what it says on the tin.

Has anyone tried a few examples using my patch to mustache.js? The {{@collection}} shorthand really is a joy to use, maybe that's all that's needed and we abandon the longhand.

devinrhode2 commented 12 years ago

Not a fan of using@ :(

@bobthecow Could you give an example of being exploited?

We could also use another interesting syntax:

{{> {{string}} }}

It seems the only way we dodge ambiguity is by having a special char or marker that means lookup the value of this string not the name 'string'

ghost commented 12 years ago

%? Looks a bit like repeating partials if you tilt your head to the left :) {{%items}}.

In response to the dereferenceing thing, & is commonly used for this in programming languages e.g. {{> &thing}}.

devinrhode2 commented 12 years ago

{{> {{string}} }}is kinda interesting because it presents the idea that anything can be dynamic lookup. *could be just as universal.

devinrhode2 commented 12 years ago

Essentially we're talking about the same thing - some symbol to 'dereference' the variable. I really like @bobthecow's* because that's exactly what it means in any non-garbage collected language. (C, C++, etc..)

ghost commented 12 years ago

Not keen on the taches in taches thing, wouldn't that choke the parser?

I'm currently leaning toward:

{{#items}}
{{> &partial}}
{{/items}}

with a shortcut of:

{{%items}}

or

{{@items}}
devinrhode2 commented 12 years ago

The{{> {{string}} }}syntax would require a more intelligent parser... compiling it would also be a bit more complicated too, I'm not very big on it.

I wonder how a lambda/filter/helper could assist you with a shorthand version...

What does everyone think of having a deference operator, like*?

ghost commented 12 years ago

I'm not against using * although prefer & as it's more Rubyish and reminds me of things.map(&:name) which does exactly what we want.

With regards the shorthand version, in practice this is more useful than the longhand. In fact, I'm using this setup in a couple of projects and have not actually had a need for the longhand version over the shorthand. The resulting templates are super clean and concice.

Let's say you have a page with main content and sub content, and each contains a collection of objects, this becomes (using @ here but could use % instead):

<html>
<body>

<div class="main">
{{@main}}
</div>

<div class="sub">
{{@sub}}
</div>

</body>
</html>

I would be more interested in the shorthand making it in over the longhand, be it {{%items}} or {{@items}}. It's a super simple patch, only requiring a few lines of code in return for powerful dynamic partial functionality.

groue commented 12 years ago

@thelucid : are you proposing to reserve a special key partial for your shorthand notation?

ghost commented 12 years ago

Not to necessarily reserve it but the convention would be that when rendering a collection of partials, use the partial key. Have a play with my fork of mustache.js, you'll see how nice it is to use, it just works.

I do quite like the {{> *partial}} or ideally {{> &partial}} notation too, however in practice the shorthand gets far more use.

groue commented 12 years ago

@thelucid : Don't play on words: you are indeed "blessing" the partial key it for some special purpose of your own convenience.

Of course, you are feeling it is a bad idea. And you are right: it's a big threat on compatibility. Each time a special key is added to the spec, one has to bump the Mustache version by a major increment (Mustache 2.0, 3.0, etc.) since it could break existng code.

You can give up this idea right away - it will never enter the spec.

ghost commented 12 years ago

I'm not playing on words, this functionality is backwards compatible, if you took the time to think about it or try out the patch before mouthing off you would see that. I'm just throwing some ideas out there that I have found to be hugely beneficial in my own projects and that I thought others could benefit from.

This ticket has thrown up some interesting discussion around the {{> *partial}}/{{> &partial}} syntax so I believe it to have been worthwhile. Far more worthwhile than your suggestion of returning a chunk of HTML.

Your negativity is getting on my nerves and don't tell me what or what not to do. Go find another ticket to troll.

ghost commented 12 years ago

p.s. what makes you think that I think it's a bad idea?

groue commented 12 years ago

It is not backward compatible, since a key has become blessed. Code written before the change may use this key. If the user updates the Mustache engine, the rendering will be different.

You will tell me: "this will not happen, since the user has to explicitely opt-in for the shorthand notation. Hence the change is backward compatible, and you are a bad troll".

Sure. Rigorously, you are right. But sooner or later we'll have many smart guys that will bless more punctuation signs and more keys, for their own convenience, and compatiblity will blow up.

Second reason why key blessing is bad idea: user's data is user's data. Don't mess with it, for your own convenience.

ghost commented 12 years ago

What's with all this "key blessing" nonsense. Existing templates will work just fine, you can use the partial key as much as your heart desires and the shorthand just introduces a convention whereby a partial key is used to specify the partial for each item (how often have you actually had a partial key in a view object? ...I would guess never). It's not for my own convenience specifically, it's to allow Mustache users to render a collection of items using specified partials, a pretty common scenario.

Rails uses conventions all the time and that's what makes it such a powerful framework, are you saying that Rails controllers shouldn't assume a show or index method just incase someone wants to use it for something else?

If you are so against convention, how about something like:

View

{
  items: [
    { partial: 'image', url: 'Some URL' },
    { partial: 'text', content: 'Some text' }
  ]
}

Template

{{items >*partial}}

or

{{items >&partial}}

Not too bad, but also not too pretty, that's what happens when you don't have conventions.

This does mean that you could also render a collection with an explicit partial too which is quite interesting:

This:

{{#items}}
{{>my_partial}}
{{/items}}

Could be declared as:

{{items >my_partial}}
groue commented 12 years ago

how often have you actually had a partial key in a view object? ...I would guess never

This is typically the behavior I try to avoid for myself : thinking for other people, and believing that my case is the case of everybody. Should a single people, a single user (we are doing user interface here) use a blessed key without knowing it is blessed, his rendering will fail in a confusing and non trivial way, leading to a painful debugging session. This would be bad for that user, and bad for Mustache. Again, for your own convenience - I'll repeat as often as needed that Mustache 1 already provides the feature everybody here pretend is missing.

Your comparison with Rails is interesting, but I have nothing against conventions. I'm a very happy Rails user myself. I would have used the arguments however, should you have been DHH in person. Namely: don't mess with user data because you don't own it, and don't bless keys for backward compatibility's sake.

groue commented 12 years ago

A new proposal:

What about changing the regular partial syntax from {{> foo }} to {{> "foo" }}, so that {{> foo }} now means "render the partial whose name is stored at the key foo", just like {{ foo }} means "render the value whose name is stored at the key foo" ?

It would be much more consistant than Mustache 1, and make useless the need for a dereferencing syntax. What do you all you think?

ghost commented 12 years ago

Did you read my alternate suggestion that addresses all your concerns? Not as clean but that's what happens when you don't have conventions.

I'm not trying to think for other people, just suggesting a clean solution to dynamic partials, that's all... not some big conspiracy.

I'll repeat as often as needed that Mustache 1 already provides the feature everybody here pretend is missing.

I'll repeat as often as needed that returning a chunk of HTML is not a proper solution to dynamic partials.

What about changing the regular partial syntax from {{> foo }} to {{> "foo" }}

From someone who's worried about breaking compatability, now this does break comparability.

Does anyone else have a view on the discussion so far as I'm losing the will to live?

groue commented 12 years ago

@thelucid : you did not understand my concern about compatibility. Here we are talking about Mustache 2, so we can break stuff if we want. Not ideal, but we can. However, the introduction of a blessed key would provide a hazardous pattern: each enw feature that would "need" a new blessed key would have to bump Mustache major version. Even tiny features that would only deserve a minor bump. That is the only concern with blessed keys. And if there is something in these sentences that you do not agree with, I'll be happy explaining until you understand that blessed keys are plain wrong, however nice they look at first sight. Nothing personal here : keep you will to live.

devinrhode2 commented 12 years ago

@groue in regards to{{> "foo" }}, if we said that{{> foo }}, upon failure, would fallback to looking up the partial by the name of this key ('foo') instead of searching the it's value as a string this would work. Interestingly, it's the flip of a dereference operator.

I have to agree with groue on the shorthand/special key, it's just not getting into the spec.

groue commented 12 years ago

Did you read my alternate suggestion that addresses all your concerns? Not as clean but that's what happens when you don't have conventions.

You are focusing on the rendering of dynamic partials for collections. You are trying here to solve two problems in the same time. What about focusing on dynamic partials first, and then see how it fits with collections?

ghost commented 12 years ago

@groue I understand what you are saying but they are only "blessed" (as you call it) when you explicitly use a feature that makes clear it's intent in the documentation.

What about the {{>static_partial}}, {{>*dynamic_partial}} and {{items >static_partial}} and {{items >*dynamic_partial}} notations? Doesn't this address your concerns?

I personally don't see the need to be quite so explicit but it addresses your concerns.

groue commented 12 years ago

in regards to{{> "foo" }}, if we said that{{> foo }}, upon failure, would fallback to looking up the partial by the name of this key ('foo') instead of searching the it's value as a string this would work

@devinrhode2 the problem would be that your feature would be backed on a failure, that is to say a condition that may be unexpected by the programmer. Expect hours of painful debugging, here, again.

On the opposite, {{> "foo" }}, {{> foo }} and {{ foo }} all say a single thing at one time, and are very consistent. One could say that partial tags used to contain a "template name". Now they contain an "expression" just like variable and section tags: {{> partial }}, {{> item.partial }}, {{> first(items).partial }} (using parentheses-based filters).

groue commented 12 years ago

Of course, a consequence of {{> "foo" }} vs the dereferencing {{> foo }} would be that {{ "foo" }} would have to render "foo". This is the introduction of literals in Mustache. I expect some people will not like that. Still, this is an elegant solution to the dynamic partial "problem" (admitting there is one in the first place).

ghost commented 12 years ago

@devinrhode2 Fair enough on the shorthand, at least we're in agreement that there needs to be a better way to render dynamic partial than returning a chunk or html.

I'm not keen on one syntax having two possible outcomes i.e. falling back to a key if a partial is not matched. In which case, I vote for:

Dynamic partials:

{{#items}}
{{>*partial}}
{{/items}}

I guess you're not taken by the alternative to {{%items}} or {{@items}}?

{{items >*partial}}

...I was just trying to come up with a way of not always having to write the open and closing tags as it's quite a common pattern.

groue commented 12 years ago

What about the {{>static_partial}}, {{>*dynamic_partial}} and {{items >static_partial}} and {{items >*dynamic_partial}} notations? Doesn't this address your concerns?

Yes. However, as an implementor, I would not be happy implementing the shortcut notation. It's there only to fulfill a very specific use case, and I would feel I spend time for a very little user benefit. As soon as you want to render a collection with some decoration, the short syntax becomes useless, leaving everybody using the "long" syntax. Also, the "long" syntax is {{#items}}{{> ...}}{{/items}}... Is it really so long, frankly?

@thelucid, as a Mustache implementor, what I've seen is that users are amazingly various, surprising, and rich. They always have needs I would have never imagined. And it's a joy when you see that all you have to do is to guide the users in your library, so that they can use it in order to accomplish their goal. I'm really against feature bloat. Because it never stops. Some guy will want a shortcut for this. This guy will want a shortcut for that. What they need is a library that can take many shapes, that can host many patterns, that can fit the user's needs, a library that does not need an update when some guy has some crazy idea. That's why I sound so conservative.

ghost commented 12 years ago

@groue Fair enough, I guess I've been in the Rails community for too long where creating shorter, more concise and convenient ways of doing things is ingrained (I created "sexy validations" in ActiveModel core for example).

Over time, the partial syntax in Rails went from:

<%= render :partial => "item", :locals => { :item => @item } %>

to...

<%= render :partial => "item", :item => @item %>

to...

<%= render :partial => @item %>

to...

<%= render @item %>

And collections of partials from:

<%= render :partial => "item", :collection => @items %>

to...

<%= render :partial => @items %>

to...

<%= render @items %>

If we had an inflector in Mustache we could do:

{{>items}}

...and it would automatically look for an item partial but I guess an inflector is going too far and still wouldn't account for dynamic partials.

groue commented 12 years ago

@thelucid I'm a happy Rails user, and also an Objective-C developer. Both environments focus on expressivity, with a concise variant on the Rails side, and a verbose variant on the Obj-C side. Now this can explain our "culture clash" :)

If I sum up, we have two subjects:

A: The syntax for static and dynamic partials, with two options so far:

  1. {{> static }} and {{> &dynamic }} or {{> *dynamic }}
  2. {{> "static" }} and {{> dynamic }}

The first introduces a special syntax for "dereferencing". The second has no need for "dereferencing" concept, is more consistent with variable and section, but introduces literals.

B: the questionable need for a collection shortcut, and whether it would be a good idea to "bless" some keys in order to help shortcuts.

My take on the subject is already known. Special keys are a big potential threat on backward compatibility, for a debatable benefit: the "long" version works, for Christ's sake. BTW, what are the special methods that Rails read in order to implement <%= render @item %> and <%= render @items %> ?

ghost commented 12 years ago

@groue Rails uses the class name which we obviously don't have and makes use of an inflector to get the singular which we also don't have.

the "long" version works, for Christ's sake

True, I'm just not a fan of the "If it ain't broke don't fix it" mentality, and instead prefer progress. As I said, you can drive a nail into a block of wood with a mallet but a hammer would probably be your best bet.

My vote on subject "A" is most definitely option 1 as I really don't like the idea of introducing literals.

We're obviously not going to agree on a shorthand ("B") which is a shame as I'm using (and will continue to use a variant of) {{@items}} in a couple of projects as it cleans up templates no end.

I guess the real way forward, after we have agreed on the dynamic partial syntax is to introduce a way for developers to register app specific functionality, so that crazy people like me and those trying out new ideas can do:

Mustache.Renderer.register('@', function(name, context, options) {
  // My custom functionality
  return 'the output';
});

This could also cleanup the internals.

See: https://github.com/janl/mustache.js/pull/242#issuecomment-7613180

groue commented 12 years ago

Rails uses the class name which we obviously don't have and makes use of an inflector to get the singular which we also don't have.

Yes indeed. No magic special blessed key is needed. And that's why it's OK.

The only "blessed" name I can think of right now in Rails is the to_param method. Convenient, but not quite elegant since suddenly models have their word in the URLs. Anyway, it's convenient, let's face it. And in this case, yes, it's quite unlikely that a user would define his own to_param method for some other purpose. And should he, anyway, he would be wrong, since he committed into Rails: by using Rails in the first place, a framework, he has a lot of documentation to read, in order to design classes that fit in the framework. As a side effect, all classes designed for Rails are locked in, now. I don't quite remember this time, but porting from Rails from/to Merb wasn't very easy, I guess.

But Mustache is not a framework. It's a dumb template engine. A mere tool.

My vote on subject "A" is most definitely option 1 as I really don't like the idea of introducing literals.

I usually try to tell you why I don't like some ideas. It would be nice of you to do the same.

Mustache.Renderer.register('@...

It would sure be nice to send out all this painful Mustache to the user. Not an easy task, though, especially when you want to keep intact the hosting language agnosticism of Mustache. Of course, you may want as many Mustaches as users, and ruin the cohesion of today's Mustache landscape.

You know, my GRMustache is quite an old library now, it may be two years old. Its API has continuously evolved, so did its feature set. But no features were added. No. instead I have increased the expressiveness of the library. So that users can extend, with their user-land code, the basic Mustache engine. Want to render 0-based indexes? 1-based indexes? Want to render something every odd elements? Want to format numbers, dates? Need a default values for missing ones? Need to debug your templates? Need to localize portions of your templates? Localization with parameters, so that the template renders "Arthur and Barbara are friends" or "Arthur et Barbara sont amis" ? Check. Done. All of this is based on a genuine Mustache 1 core, and a few carefully designed hooks so that the user injects the code Mustache 1 is missing.

I have quite an experience on this Mustache.Renderer.register('@... topic. I'd be happy discussing it.

ghost commented 11 years ago

The only "blessed" name I can think of right now in Rails is the to_param method.

It's quite common and even reccomended to override the to_param method in Rails for example to return a slug to be used in urls.

I actually like the idea of a to_partial and to_string convention. The to_string would be useful in cases like:

View

{ price: { currency: '£', value: '20.00', to_string: '£20.00' } }

Template

Currency: {{price.currency}} <- '£'
Value:    {{price.value}}    <- '20.00'
Price:    {{price}}          <- '£20.00' (to_string)

or

{{#price}}
Currency: {{currency}} <- '£'
Value:    {{value}}    <- '20.00'
Price:    {{.}}        <- '£20.00' (to_string)
{{/price}}

or partials...

View

{
  people: [
    { first_name: 'John', last_name: 'Lennon', to_partial: 'dead' },
    { first_name: 'Paul', last_name: 'McCartney', to_partial: 'alive' },
  ]
}

Template

{{#people}}
{{>.}}
{{/people}}

or

{{@people}}

The to_partial and to_string keys could even be > and . respectively.

I usually try to tell you why I don't like some ideas. It would be nice of you to do the same.

Liquid templates use literals and the point of literals are to be able to insert data into the template. As the premise Mustache is to be logicless and to keep the data in a view object, the introduction of literals doesn't seem a good fit. Next people will want to perform operations on those literals like in Liquid which could end badly.

I have quite an experience on this Mustache.Renderer.register('@... topic. I'd be happy discussing it.

I'm definitely up for suggestions on how to hook into the language as I think this would be a good move for Mustache and provide a nice way to showcase new functionality as well as providing a way for developers to implement their own project specific functionality.

groue commented 11 years ago

to_partial

You know my take on why special keys are bad (see my comments above)

Liquid templates use literals and the point of literals are to be able to insert data into the template. As the premise Mustache is to be logicless and to keep the data in a view object, the introduction of literals doesn't seem a good fit. Next people will want to perform operations on those literals like in Liquid which could end badly.

Go read https://github.com/mustache/spec/wiki/%5BDiscussion%5D-Logic-Free-vs.-Non-Evaled, including my answers, please. Add you own answer to the wiki if you want. And then come back with more explanations of your opinion, if it still stands.

I'm definitely up for suggestions on how to hook into the language [...]

Sure, here some more reading for you! Those suggestions are all written down in GRMustache documentation. Since you like shortcuts, go check the sample code links. Also don't miss the lambda API, which fixes a few flaws of genuine Mustache lambdas, the filters API, and the mother of all hooks, the template delegate API - now GRMustache recommends using filters, but the delegate was the way to go before their introduction.

groue commented 11 years ago

All, I've got a fresh new proposal for you.

It's inspired by the fact that the expressiveness of GRMustache comes from its API, not from the Mustache language itself.

So here it is: let's have {{items}} render the concatenation of dynamic partials, by updating the spec with those four sentences:

  1. An implementation MUST render of {{items}} the same way as {{#items}}{{.}}{{/items}} whenever the value attached to items is an enumeration.
  2. When rendering {{value}}, an implementation must fetch the value attached to the key value. If this value is a lambda, or can be automatically converted to a lambda, the rendering MUST be the result of the lambda call.
  3. An implementation SHOULD provide an API for letting the library user specify the automatic conversion to lambda described at point 2.
  4. An implementation SHOULD provide an API for letting the library user specify the automatic conversion to lambda described at point 2 via a partial name.

Now let's see how it fits your needs.

All examples below will use the same templates set:

base.mustache
{{items}}

text.mustache
<p>{{content}}</p>

image.mustache
<p><img src="{{url}}"/></p>

Let's first use a Mustache implementor that only implement MUST rules of the spec:

// User has to provide lambdas in the first place

var text_item = function() { return "{{>text}}"}
text_item.content = 'Some text'

var image_item = function() { return "{{>image}}"}
image_item.url = 'Some URL'

var data = { items: [text_item, image_item] }

Let's now use a Mustache implementation that implements rule 3, but not rule 4. Its documentation says: "If an object has a to_lambda function property, this function is used for automatic lambda conversion (see Mustache doc)."

// User complies with his Mustache implementation documentation by setting
// the to_lambda property:

var data = {
    items: [
        { content: 'Some text', to_lambda: function() { return '{{> text}}' }},
        { url:     'Some URL',  to_lambda: function() { return '{{> image}}' }}
    ]
}

Let's now use a Mustache implementation that implements rule 3 and 4. Its documentation says: "If an object has a to_partial string property, this value is used for automatic lambda conversion (see Mustache doc)."

// User complies with his Mustache implementation documentation by setting
// the to_partial property:

var data = {
    items: [
        { content: 'Some text', to_partial: 'text' },
        { url:     'Some URL',  to_partial: 'image' },
    ]
}

Now, @thelucid, see how this approach is powerful:

A Javascript implementation may state in its documentation: "optional features such as automatic partial and lambda conversions are not supported. You have to provide lambdas objects".

A Javascript implementation may state in its documentation: "in order to get automatic partial conversion, provide the to_partial key".

A Ruby implementation may state in its documentation: "automatic partial conversion is automatic and based on the class of your object"

A Java implementation may state in its documentation: "automatic partial conversion is based on the interface AutomaticMustachePartia which defines the getMustachePartialName method".

An Objective-C implementation may state in its documentation: "automatic partial conversion is based on the conformance to the MustacheMustachePartial protocol, which defines the mustachePartialName method".

See? Now if a library wants to mess with the user data, by defining special keys, it can. This is now its own problem. Some other implementations may be able to provide a much cleaner way to implement the dynamic partial feature.