Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Feature Request: Inline partials #63

Open
spudly opened this Issue Jan 15, 2013 · 49 comments

Comments

Projects
None yet
5 participants

spudly commented Jan 15, 2013

Partials are great, but it'd sure be nice to be able to define partials right inside the template that uses them. In most cases, the partials I create are only used by one parent template so it's a lot of extra work to create the partial the way it works right now. Also, a lot of my partials are one-liners. As a result, I often times end up just copy/pasting instead. It sure would be nice if we could define partials inline. See issue #59. This would allow us to write mini-templates that can be reused within the same template. Here's some possible syntax:

Definition:

{{*mypartial}}
    <li title='{{title}}'>{{text}}</li>
{{/}}

Usage:

<ul>
  {{#items}}
    {{**mypartial}}
  {{/items}}
</ul>

spudly commented Jan 15, 2013

The example I gave was not a use-case... more just to define a possible syntax (single asterisk for definition, double asterisk for usage). @devinrhode2 came up with the syntax in #59.

Inline partials just make it easier to define reusable code snippets. Some partials do not need to be shared between templates, so there's no reason to go through all the work to define them in their own mustache template file. That's the use case this helps with.

I'm curious where basic iterations would not take care of this. Can you think of a good scenario where inline partials are better than a basic iteration?

The syntax for outputting a partial is already {{> partial}} so essentially the question is: what's the syntax for creating a partial inline? Is {{* someNewPartial}} the best? Perhaps a block helper (if mustache gets those) could be used: {{#partial aPartial}} what do you think @groue?

spudly commented Jan 15, 2013

I think you're right about the syntax. We should continue to use {{>partial}} for using the defined partials. How about {{>>partial}} for definition?

One good scenario is where you iterate over something more than once, or iterate over two lists with similar items. In that case, you don't want to just copy/paste. You need a partial that you can reuse. Here's a snippet:

{{>>person}}
  <a href='/user/{{username}}'>{{name}}</a>
{{/person}}

<h1>Democrats</h1>
<ul>
  {{#democrats}}
    <li>{{>person}}</li>
  {{/democrats}}
</ul>

<h1>Republicans</h1>
<ul>
  {{#republicans}}
    <li>{{>person}}</li>
  {{/republicans}}
</ul>

{{>> partial}} is better for sure

Solid use case, thanks

groue commented Jan 16, 2013

Hi all.

I think the use case still lacks proper definition.

If the motivation is to avoid partials and to be DRY, then Mustache already provides a solution called lambdas:

<h1>Democrats</h1>
<ul>
  {{#democrats}}
    <li>{{link}}</li>
  {{/democrats}}
</ul>

<h1>Republicans</h1>
<ul>
  {{#republicans}}
    <li>{{link}}</li>
  {{/republicans}}
</ul>

link being defined in the caller code as returning the rendering of <a href='/user/{{username}}'>{{name}}</a> (the API is implementation-dependant, thus no sample code).

If you have another motivation, @spudly, please refine it.

spudly commented Jan 16, 2013

I reuse my mustache files with two different implementations (PHP and JavaScript). For this reason, I don't like using lambdas and have to stick strictly to the spec. Also, using lambdas for something like this would mean that I would have to write the code in one language and then port it to the other. Then if I ever discover a bug in one I have to be sure to fix both, not just one. No thanks.

Also, I precompile my templates using Hogan.js, which means I can't pass the contents of a lambda block to the lambda function (see twitter/hogan.js#75).

groue commented Jan 16, 2013

Thanks @spudly for providing context.

If I understand your points, I regret your "No thanks".

Such a simple lambda would not be difficult to maintain in two languages. Seriously. It is not.

Mustache suffers from the start from a lack of cross-language fostering. I, however, see cross-language concepts and APIs the key to help Mustache become a strong template engine that does not collapse as soon as the application leaves the trivial zone.

Without an attempt at fostering solid and versatile APIs, Mustache will never go anywhere, and this repo will get more and more filled with issues that, seen from the outside, are all tiny additions that, if ever applied (and implemented by implementors), would turn Mustache into an ugly feature-bloated "tool" unable of having serious job done. I consider this as a sad waste.

Not that I think that "inline partials" are an ugly feature per se. What I say is that quick "No thanks" dismissals do not go in the right direction.

spudly commented Jan 16, 2013

Please forgive my rudeness. I simply meant to say that I'd rather not maintain multiple copies of the same code. I do see the usefulness of lambdas; I just don't see them as a good solution to this particular problem.

You're right that creating a link is easy to implement and maintain using lambdas, however that was just a simplified example. I expect many inline partials would be muich more complex. Drawing a table row for example would be a pain to write in a typical programming language, but simple enough using a partial. In that situation I would probably have to write a lambda that then processes another template to create the row html and then returns the html... but then you've just reinvented partials.

Also, I believe there are scoping problems. Lambdas operate on a single parameter, that being the text within the lambda block. So {{mylambda}}foo {{somevar}}{{/mylambda}} becomes mylambda("foo {{somevar}}") (unrendered). There's no way to inherit the current variable scope to get the person's name and username.

groue commented Jan 17, 2013

I would probably have to write a lambda that then processes another template to create the row html and then returns the html... but then you've just reinvented partials.

Also, I believe there are scoping problems

I think you are not lucky and that you use sub-par libraries that prevent you to do your job. I don't care if they are the most-used/forked/watched JS or PHP libs. I care that they don't help you. I care that in order to have your job done, you would need to tweak them so much they would collapse, wasted under your iron will. And thus you give up, and complain about Mustache in this very repo.

What you describe are plain and simple bugs in the implementations. Not in Mustache.

One needs to be able to write a lambda that processes another template, and not to be feared by that. The API should make this simple, not scary, and linked to partials instead of opposed to them.

One needs to write lambdas that inherit the current variable scope. If your Mustache implementation does not provide it, it's a toy, not a production tool.

groue commented Jan 17, 2013

BTW, back to your topic. A user of GRMustache stores his templates in a custom home-made format, so that he can split them into named partials. He built is own inline-partial feature. Because he needed it. See for example https://github.com/tomaz/appledoc/blob/master/Templates/html/document-template.html

spudly commented Jan 17, 2013

you use sub-par libraries that prevent you to do your job

It's not the libraries themselves. I don't expect the libraries to implement anything that is not in the spec. I want the feature added to the spec, so that both will implement it. That way I don't have to write two different template files to account for differences in the implementations. That's the whole point of having a spec, right?

And thus you give up, and complain about Mustache in this very repo.

Not trying to complain. Sorry if it came out that way. I'm simply requesting a feature be added to the spec and explaining why I think it would help.

What you describe are plain and simple bugs in the implementations. Not in Mustache.

A lack of a feature is not a bug. I did describe one bug that gives me grief with lambdas in Hogan.js, but that's the only one and I don't think it's even relevant to this issue.

One needs to be able to write a lambda that processes another template, and not to be feared by that.

I'm not scared to do it. I just don't see the point because I could just use a partial in the first place. Using lambdas to process another template seems like reinventing the wheel to me.

One needs to write lambdas that inherit the current variable scope. If your Mustache implementation does not provide it, it's a toy, not a production tool.

If I'm correct, the spec doesn't say that lambdas inherit the variable scope. Since I need this to work on multiple implementations, I only want to use features that are in the spec. This is why I put the feature request on the Mustache spec and not just on an implementation's repo.

Lambdas as designed in the spec do not and can not solve my problem. Sure, there may be implementations that can do so, but that's not in the spec so anyone who reuses templates across different implementations can't use them. That's the reason why I use mustache and not handlebars or any other mustache extension. Here is an idea for a feature that could solve a problem. It's DRY and logic-free. Many other template libraries have it. I think it would be a good idea to add this to the spec. If whoever approves/writes the changes to the mustache spec doesn't agree, that's fine.

groue commented Jan 17, 2013

I just don't see the point because I could just use a partial in the first place. Using lambdas to process another template seems like reinventing the wheel to me.

That's precisely the kind of reasoning that happens because the implementations you are using are weak and force you into an uninspiring mental landscape. In these implementations, the versatility you are looking for is only accessible through partials. So you want more partials. And since those libraries provide inconvenient APIs for defining partials, you ask the language itself for more convenience - inline partials.

Sorry if I sound like I wildly try to double guess you. But the fact that you "don't see" something does not mean there is nothing - just that you don't see it. So do, arguably, the implementations you're fighting with in your quest for doing the real job.

The problem is the APIs, not the language syntax.

For example, when you say "Using lambdas to process another template seems like reinventing the wheel to me", I see the opportunity to encapsulate code chunks in clear and focused units. I see Mustache that sneaks in the territory of Liquid templates. I see Mustache able to render {{items}} just like Ruby on Rails renders <%= render @items %> (actually, @thelucid got the idea first).

All that is actually implemented in GRMustache, which is a Mustache library that allows you to write spec-compliant templates, but has been designed to help its users.

Anyway. Now that you can't use lambdas because they are unusable in the libraries you are using, and because inline templates are for sure easier to implement, and thus may qualify as a reasonable goal, how could they look like?

What about the = character for defining inline templates? It looks like a definition, doesn't it?

{{=person}}
    <a href=...
{{/}}

{{#democrats}}
    {{>person}}
{{/democrats}}

In order to help the implementors, we could say that inline templates have to be defined before their usage, in order to prevent a forced double parsing phase.

In case of multiple definitions, the last definition before usage wins:

{{=person}}
    ignored {{! 1 }}
{{/}}

{{=person}}
    <a href="..." {{! 2 }}
{{/}}

{{#democrats}}
    {{>person}} {{! renders partial 2 }}
{{/democrats}}

{{=person}}
    <a href="..." {{! 3 }}
{{/}}

{{#republicans}}
    {{>person}} {{! renders partial 3 }}
{{/republicans}}

Question: should an inline definition be superseded by a partial that is programmatically defined? Or the opposite?

In order to support Mustache implementations that can load templates and partials from a hierarchy of files and directories, allowing to define {{= shared/header }}...{{/}} could be a plus, allowing partials inside the shared folder to use {{> header}}. This would be consistent.

Some filesystem-based Mustache implementations also define absolute paths to partials ({{> a}} renders the sibling a, when {{> /a }} renders the root a). So it would be nice to allow the definition of {{= /person}} as well, in order to have {{> /person}} rendered in the same fashion in partials of arbitrary depth.

Do you see any more rules that should be written?

Have to agree with @groue on this one, definitely something that could/should be handled with a lambda. Or, in the case of Tache, you can simply return a compiled (or uncompiled) template object from your view method for rendering. I think this would be a more worthy addition to the spec as it has far more applications than just inline partials, something like:

  • An implementation MUST allow compiled or uncompiled template instances to be returned from views for rendering.

groue commented Jan 18, 2013

Or, in the case of Tache, you can simply return a compiled (or uncompiled) template object from your view method for rendering

Nice. Excellent convergence with GRMustache here :-)

id data = @{
    @"democrats": ...,
    @"republicans": ...,
    @"link": [GRMustacheTemplate templateFromString:@"<a href=\"/user/{{username}}\">{{name}}</a>" error:NULL],
};

NSString *rendering = [GRMustacheTemplate renderObject:data
                                          fromResource:@"Document"
                                                bundle:nil
                                                 error:NULL];

groue commented Jan 18, 2013

Dynamic partials discussed in #49 can be achieved with exactly the same logic, actually. I guess it's the same in Tache:

id data = @{
    @"democrats": ...,
    @"republicans": ...,
    @"link": [GRMustacheTemplate templateFromResource:@"Link" bundle:nil error:NULL],
};

NSString *rendering = [GRMustacheTemplate renderObject:data
                                          fromResource:@"Document"
                                                bundle:nil
                                                 error:NULL];

spudly commented Jan 18, 2013

Maybe I'm wrong (because I am not familiar enough with objective-c) but what you're doing in that code looks like what I'm already doing with partials... I have separate template files that I compile and pass to a render function. These are then rendered wherever I reference them in the main template and they inherit the variable scope when they do so. What I want to do is to define the partials in the main template file so I don't have to define them in a separate file and pass them in.

@spudly I see your problem. I was going to suggest creating a generic inline_partial lambda that captures the content and saves it for later, however you wouldn't be able to store the contents by name for recall later.

This touches on the larger subject of helpers which Mustache is currently lacking. I'm talking about helpers in the Handlebars sense, for example you could handle your problem with a 'capture' helper in Handlebars:

{{! The following would capture the content into a 'captured' hash for later. }}

{{#capture "my_partial_name"}}
Some stuff I want to repeat
{{/capture}}

<h1>{{captured.my_partial_name}}</h1>
<footer>
  <p>{{captured.my_partial_name}}</p>
</footer>

I think something needs to be agreed upon when it comes to helper support in Mustache. The more I play with Handlebars, the more my helper envy grows. What are your thoughts @groue?

groue commented Jan 18, 2013

@thelucid : this would require helpers/filters AND literal parsing.

@groue Good point, although literals in Handlebars are only in the case of helpers, not a global change. They just get sent as arguments to the helper functions.

I'm starting to think that this could address many of the questions that crop up in this repo.

groue commented Jan 19, 2013

@spudly

What I want

I suggest you quit this childish tone immediately.

https://github.com/mustache/spec/blob/master/specs/%7Elambdas.yml#L96 shows that Mustache 1.1.2 lambdas inherit the current context when rendering: lambdas are the answer of Mustache 1.1.2 to your problem.

Since the Mustache implementations you are using prevent you from using them, go and open issues in their repositories.

@thelucid

@groue Good point, although literals in Handlebars are only in the case of helpers, not a global change. They just get sent as arguments to the helper functions.

I'm starting to think that this could address many of the questions that crop up in this repo.

Could they? I suggest you gather link to all issues that are related to literals in a global "literal" issue. Maybe the case for them will be so strong we'll just jump on the wagon right away. Or maybe not :-)

groue commented Jan 19, 2013

(I really should improve my English and stop "suggesting" like a machine gun :-)

@groue I didn't sense a childish tone from @spudly in the slightest, he simply wants to solve a problem in his Mustache workflow which is fair enough.

I'm not suggesting that literals would solve many issues, I am suggesting that Handlebars style helpers (with basic literal support) would. You seem very quick to dismiss ideas unless they are your own.

spudly commented Jan 19, 2013

@groue I hardly think I'm being childish. If anything you have a hostile
tone. Let's have a discussion, not an argument.

Thanks for pointing out that lambdas inherit variable scope. I was not
aware of that. Will look into it.

Even so, I don't see how lambdas are a solution to my problem. The solution
to my problem is partials. I can easily enough create a partial as a string
in my view and pass it in as a partial. That's really all I would have to
do. A better solution to my problem would be inline partials, because then
I don't have template code in my view!
On Jan 19, 2013 12:54 AM, "Gwendal Roué" notifications@github.com wrote:

@spudly https://github.com/spudly

What I want

I suggest you quit this childish tone immediately.

https://github.com/mustache/spec/blob/master/specs/%7Elambdas.yml#L96shows that Mustache 1.1.2 lambdas inherit the current context when
rendering: lambdas are the answer of Mustache 1.1.2 to your problem.

Since the Mustache implementations you are using prevent you from using
them, go and open issues in their repositories.

@thelucid https://github.com/thelucid

@groue https://github.com/groue Good point, although literals in
Handlebars are only in the case of helpers, not a global change. They just
get sent as arguments to the helper functions.

I'm starting to think that this could address many of the questions that
crop up in this repo.

Could they? I suggest you gather link to all issues that are related to
literals in a global "literal" issue. Maybe the case for them will be so
strong we'll just jump on the wagon right away. Or maybe not :-)


Reply to this email directly or view it on GitHubhttps://github.com/mustache/spec/issues/63#issuecomment-12450983.

@spudly Do you see how Handlebars style helpers would allow you to achieve this?

spudly commented Jan 19, 2013

@jamie, yes I do see how helpers could solve this problem pretty easily,
although it would not be my preferred approach.
On Jan 19, 2013 7:15 AM, "Jamie Hill" notifications@github.com wrote:

@spudly https://github.com/spudly Do you see how Handlebars style
helpers would allow you to achieve this?


Reply to this email directly or view it on GitHubhttps://github.com/mustache/spec/issues/63#issuecomment-12454113.

@spudly My experience with this spec repo has been that generally new language additions aren't accepted straight off. Instead, the maintainers look for more generic ways of solving the same issues that keep the core of Mustache as lean as possible (a good example of this is my ticket on dynamic partials and partial collections. Whilst this can be frustrating, it does sometimes result in a more generic solution that benefits the whole of Mustache, not just the initial problem.

There are many issues that could benefit from a generic helper solution including your inline partial need, hence my suggestion. Given my experience on other tickets, I wouldn't hold out too much hope for getting inline partials added specifically, however if there is a more generic solution, this is more likely to be accepted (I feel your pain).

groue commented Jan 21, 2013

@thelucid got the context right. Adding a feature each time somebody uses italics on want won't happen any time soon.

Many ideas have been proposed here. None has been explored thoroughly enough.

@spudly has been stuck on his inline partials, never telling how, for example, should their conflict with external partials be resolved (see above for the first unfinished exploration of his idea).

@thelucid had a few difficulties with a "recorder" helper, and started gazing at Handlebars. I think invoking Handlebars is far too early. Handlebars is not the magic wand that solves all problems. Instead, you could focus first on a pair of lambdas, one that "records" its section, another that "replays" it. Is it possible? What is required to avoid recompilation? Any nasty corner cases?

For the record, at the end of this comment is some GRMustache code that implements this record/replay pair without recompilation. Yes, a Mustache engine can do it.

One argument for record/replay pairs is that they do not conflict with external partials. Maybe this conflict is a false problem. I wish we knew better. This requires in-depth inline partials analysis.

If the idea of record/replay pairs remains solid after investigation, then we can explore the literals. I agree with @thelucid that it looks like literals look quite useful in conjunction with filters/helpers (see for instance groue/GRMustache#37, and another idea by @spudly: "variable defaults" #64, which used literals). Still I wish we had a better landscape of use cases that involve literals.

The record/replay pair with GRMustache:

template.mustache:

{{#record}}
  <a href="{{url}}">{{username}}</a>
{{/record}}
{{#democrats}}
  {{replay}}
{{/democrats}}
{{#republicans}}
  {{replay}}
{{/republicans}}

Rendering code:

__block GRMustacheTag *recordedTag = nil;
id data = @{
    @"democrats": ...
    @"republicans": ...
    @"record": [GRMustache renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) {
        // Record: save tag for later, and don't render anything.
        recordedTag = tag;
        return @"";
    }],
    @"replay": [GRMustache renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) {
        // Replay: render recorded tag.
        return [recordedTag renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
    }],
};

NSString *rendering = [tempalte renderObject:data error:NULL];

groue commented Jan 21, 2013

Given my experience on other tickets, I wouldn't hold out too much hope for getting inline partials added specifically, however if there is a more generic solution, this is more likely to be accepted (I feel your pain).

Given my own experience, the spec maintainers @pvande, @defunkt and @janl won't show their nose and won't update the spec. Our only solution is to find a reasonably good solution for use cases, and implement them right away without spec support, hoping other implementors will follow. This is the really sad reality. A spec with maintainers who moved on.

Owner

janl commented Jan 21, 2013

Specs should describe solutions. In the field, not drive innovation.

Also, sorry for not spending even more time on open source.

@groue Yes, record/replay functionality is easy enough to implement as I mentioned (already done that), however as soon as you need more than one inline partial, it's impractical.

{{#record}}Foo{{/record}}
{{#record}}Bar{{/record}}

{{replay}} - {{replay}}

...this get's you 'Foo - Foo', not what we wanted. The only way to store by name would be with Handlebars style helpers:

{{#record "foo"}}Foo{{/record}}
{{#record "bar"}}Bar{{/record}}

{{replay "foo"}} - {{replay "bar"}}

@janl Completely understand that you are busy with other projects. Would you accept pull requests for tried and tested additions to the spec such as collection shortcuts that both Tache and GRMustache implement i.e. {{items}} renders the same as {{#items}}{{.}}{{/items}} when items is a collection. Tache maintains indentation for collections of objects that coerce to a template object (even compiled templates), meaning the following is possible:

class MyView < Tache
  def ok
    'OK'
  end

  def items
    [ OpenStruct.new(to_tache_value: Tache::Template.new("<li>First {{ok}}</li>\n"), ok: 'Yay!'),
      OpenStruct.new(to_tache_value: Tache::Template.new("<li>Second {{ok}}</li>\n")) ]
  end
end
Template:
<ul>
  {{items}}
</ul>

Result
<ul>
  <li>First Yay!</li>
  <li>Second OK</li>
</ul>

Edit: The to_tache_value method may change in future versions of Tache. It's a way to coerce an object into something that can render i.e. a string or template instance.

groue commented Jan 21, 2013

Yes, record/replay functionality is easy enough to implement as I mentioned

Aren't you too fast? It may be easy for Tache and GRMustache, but there are other users out there. Many Mustache implementations have a narrow view of lambdas, because of the spec. For them i'm not sure it's as easy as you say. If I remember well, you are familiar with mustache.js. Do you think it's easy to implement the record/play pair with it?

however as soon as you need more than one inline partial, it's impractical

Yes, of course. But one thing after the other.

Solutions for multiple record buckets, as you say, may use literals and filters (and I think Mustache issue #41 is a better framework for code injection than Handlebars envy).

Another solution which does not use literals is to pre-define as many record/replay pairs as needed, and to invoke them ({{# bucket1.record }}...{{/}}...{{# bucket2.record }}...{{/}}...{{ bucket1.replay }}...{{ bucket2.replay }}.

There may be more solutions.

groue commented Jan 21, 2013

Another difficulty with "record/replay" pairs is that the user expects the records to be cleared for each rendering. Even before considering naming those pairs, how would you implement a single pair in Tache, @thelucid? And how would you clear the record at the beginning of each rendering?

@groue With Tache it would be as simple as:

class View < Tache
  def record
    lambda do |text|
      @captured = text
    end
  end

  def replay
    render @captured
  end
end

With Mustache.js I guess you would do something like:

var view = {
  record: function(text, render) {
    this.captured = text;
  },

  replay: function() {
    Mustache.render(text, this);
  }
};

...although I'm not sure that the JavaScript example would maintain the right context as Mustache.js doesn't implement lambda's as per the spec i.e. it passes the render function as the second argument to the lambda instead of having a view object that has a render function.

And how would you clear the record at the beginning of each rendering?

I'm not sure what you mean here?

I think Mustache issue #41 is a better framework for code injection than Handlebars envy

@groue But your {{helper(one, two)}} functionality is almost identical to Handlebars's {{helper one two}} functionality, just with the extra noise from brackets and commas. I would personally say that the Handlebars syntax is clearer and easier to pass.

groue commented Jan 21, 2013

@thelucid OK - In your example, captured stores a string, doesn't it? Does Tache need to recompile the full recorded section on each replay?

And how would you clear the record at the beginning of each rendering?

I'm not sure what you mean here?

I mean that when you render the template a second time, @captured is not cleared: {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing.

groue commented Jan 21, 2013

But your {{helper(one, two)}} functionality is almost identical to Handlebars's {{helper one two}} functionality, just with the extra noise from brackets and commas.

"Extra noise"? uppercase(helper(one, two)) can be parsed. uppercase helper one two can not be parsed: it's impossible to say if it's a three-arguments helper, a composition of two or three helpers, etc.

GRMustache filters uses a syntax whose efficiency has been long-proved, and which does not limit the library users with artificial walls built by "clearer" or "easier" syntax.

With GRMustache, users work. They do not even realize the library helps them, they just do their stuff and forget it. They do not open tickets saying "please allow me to compose helpers", or "please remove extra brackets and commas".

@thelucid OK - In your example, captured stores a string, doesn't it? Does Tache need to recompile the full recorded section on each replay?

Yes, or it can compile it only the first time (Tache is quite flexible when it comes to compiled templates):

class View < Tache
  def record
    lambda do |text|
      # The 'compile' call is optional as Tache will lazily compile anyway.
      @captured = Tache::Template.new(text).compile
    end
  end

  def replay
    @captured.render(context)
  end
end

I mean that when you render the template a second time, @captured is not cleared: {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing.

That would work as expected.

@groue I would say that nested helpers are a bad idea in a template anyway as things could get messy pretty fast.

@groue If you needed nested helpers, you could accomplish with block helpers i.e. {{#upcase}}{{helper thing}}{{/upcase}} or simply with methods i.e. {{helper thing.upcase}}

groue commented Jan 21, 2013

Yes, or it can compile it only the first time

OK, that's better than nothing :-) That's still one extra compilation phase, since the record section has already been compiled when the record lambda is invoked.

That would work as expected.

For you it's expected that {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing?

I would say that nested helpers are a bad idea in a template anyway as things could get messy pretty fast.

Come on, are you like that? You a duck-typing guy? Again: GRMustache users do not open tickets saying "please allow me to compose helpers", or "please remove extra brackets and commas". Even less "please clean up the mess I've been messing up with using your messy library".

groue commented Jan 21, 2013

@groue If you needed nested helpers, you could accomplish with block helpers i.e. {{#upcase}}{{helper thing}}{{/upcase}} or simply with methods i.e. {{helper thing.upcase}}

Sure. Or use a syntax that does not suck.

OK, that's better than nothing :-) That's still one extra compilation phase, since the record section has already been compiled when the record lambda is invoked.

The lambda could receive the compiled tokens but the spec says that it should only receive text.

For you it's expected that {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing?

Yes, why would it? You are replaying one thing, capturing a new thing, then replaying that.

Come on, are you like that?

I'm not, but the thing I like about Mustache as it generally doesn't allow template authors to get in a mess. I can happily teach a designer the basics of Mustache, supply a JSON structure and he's unlikely to hang himself. With nested helpers, brackets etc. I see him trying all kinds of whacky stuff.

Sure. Or use a syntax that does not suck.

I don't see why you think {{helper thing.upcase}} sucks? Very succinct if you ask me... I've not seen anyone complain about the Handlebars helper syntax either.

@janl How do you feel about:

  • An implementation must allow compiled or uncompiled template instances to be returned from views for rendering.
  • An implementation must render {{items}} as {{#items}}{{.}}{{/items}} when items is a collection.
  • An implementation should allow objects to coerce themselves to a template instance or string for rendering.

These three rules solve Dynamic Partials (#49 and #54), Partial Collections (#54) and no doubt, many other situations. All three rules have been tried and tested in Tache and GRMustache.

Edit: Probably should go in another ticket but as we're discussing what should enter the spec here, felt like a good place to ask. I am happy to write the specs and submit a pull request.

groue commented Jan 21, 2013

OK, that's better than nothing :-) That's still one extra compilation phase, since the record section has already been compiled when the record lambda is invoked.

The lambda could receive the compiled tokens but the spec says that it should only receive text.

@thelucid Pretty limiting, hu? What about taking another point of view, and saying "my lib must allow people to render spec-compliant templates", instead of "my lib must behave exactly, no more, no less, as described by the spec"?

For you it's expected that {{replay}}{{#record}}...{{/record}}{{replay}} does not render twice the same thing?

Yes, why would it? You are replaying one thing, capturing a new thing, then replaying that.

I'm talking about two distinct renderings of the same entire template, provided with the same input data.

Come on, are you like that?

I'm not, but the thing I like about Mustache as it generally doesn't allow template authors to get in a mess. I can happily teach a designer the basics of Mustache, supply a JSON structure and he's unlikely to hang himself. With nested helpers, brackets etc. I see him trying all kinds of whacky stuff.

OK. Let's build a toy, then.

@groue wycats/handlebars.js#222

Thanks for the link. Let's look in details:

...For format tweaks...

{{ escape_javacript(uppercase(name)) }} is not about formatting. Let's look at another argument.

If you look in the issue tracker, you'll see that there are still some issues related to interactions between existing helper styles (like if and bindAttr), and every new piece of possible syntax dramatically increases the interaction surface area.

Oh. Handlebars is difficult to test. I feel sorry for them.

Perhaps some day, when the existing bindings are extremely solid and mature, we can look into adding new features. For now, if it is possible to implement something via computed properties (or other existing primitives), I'm going to lean heavily in favor of using those primitives over adding new syntax.

Oh. Handlebars is not mature enough. I feel sorry for them as well.

GRMustache system is very simple, and thus extremely well tested, and extremely solid.

Tags contain expressions. name, uppercase(name), last(people).name. Expressions have a value computed in the current context. The value is asked to render for the tag. Depending on the class of the value and the tag type, booleans will render or not mustache sections, strings will render themselves, render or ignore sections, etc. Library users can provide their own rendering code, and have access to the same APIs as the standard rendering objects (bools, strings, arrays, etc.). Very few concepts, well integrated, for maximum flexibility. Because GRMustache does not nanny its users.

@thelucid Pretty limiting, hu? What about taking another point of view, and saying "my lib must allow people to render spec-compliant templates", instead of "my lib must behave exactly, no more, no less, as described by the spec"?

I'm running automated tests against the spec so if I want it to pass, it's the only option. Either that, or get the spec updated to allow tokens (or a template instance) to be passed to the lambda.

I'm talking about two distinct renderings of the same entire template, provided with the same input data.

Oh, I see. I guess you'd just define a reset! method that cleared the instance variable.

OK. Let's build a toy, then.

Or you could make it more concise for simple cases and only require parentheses for nested calls like Ruby i.e.

{{! standard }}
{{helper value1 value2}}

{{#block_helper value1 value2}}
...
{{/block_helper}}


{{! nested }}
{{helper upcase(value1) value2}}

{{#block_helper upcase(value1) value2}}
...
{{/block_helper}}


{{! with handlebars style optionals }}

{{helper value1 value2 optional_a=value3 optional_b=upcase(value4)

groue commented Jan 21, 2013

I'm running automated tests against the spec so if I want it to pass, it's the only option. Either that, or get the spec updated to allow tokens (or a template instance) to be passed to the lambda.

Well, you have to pass the spec's tests. And your own for each of your additions. How do you think other implementors do, when they extend the language?

Oh, I see. I guess you'd just define a reset! method that cleared the instance variable.

Yes, I have exactly the same problem. This is an encapsulation issue we need to address. If we do not, the user will have to document his code with sentences like "Remember to call reset! before using the template". This would be a real mess, this time.

Or you could make it more concise for simple cases and only require parentheses for nested calls like Ruby i.e.

{{! standard }}
{{helper value1 value2}}

{{#block_helper value1 value2}}
...
{{/block_helper}}

{{! nested }}
{{helper upcase(value1) value2}}

{{#block_helper upcase(value1) value2}}
...
{{/block_helper}}

{{! with handlebars style optionals }}

{{helper value1 value2 optional_a=value3 optional_b=upcase(value4)

You're border-line dishonest this time :-) I'm sure one reason for which you don't like much the f(g(x)) syntax is the fact that the parser must be recursive, and is less easy to implement than a simple <identifier>[<white_space><identifier>]. And now parsing would become even more difficult, since your syntax for "nested helpers" introduces syntax inconsistency!

Let's have a look at your actual Ruby code in Tache... Yeah, you're like me. You always use parenthesis when methods have arguments, and never use them when methods have no argument. You don't like ambiguity. Neither do I:

{{ name }}
{{ uppercase(person.name) }}
{{# withPosition(items) }} {{position}} {{name}} {{/}}
{{# empty?(items) }} No items {{/}}

This is already implemented in GRMustache, and I don't want to switch to a poorer solution.

I haven't yet needed named arguments in my own templates, as Handlebars do - I'd take inspiration from Ruby on that topic, I guess.

Well, you have to pass the spec's tests. And your own for each of your additions. How do you think other implementors do, when they extend the language?

Adding an additional argument would break the test I believe. Switching from text to tokens would definitely break the spec.

Yes, I have exactly the same problem. This is an encapsulation issue we need to address. If we do not, the user will have to document his code with sentences like "Remember to call reset! before using the template". This would be a real mess, this time.

I think that if the user is going to litter their views with instance variable, it should be up to them to clear them. We could always provide some kind of after_render method that get's invoked if present.

You're border-line dishonest this time :-)

You are very quick with your accusations ;-) I hadn't even given the complexity of parsing a thought, just prefer the Handlebars syntax for the majority of cases.

Let's have a look at your actual Ruby code in Tache... Yeah, you're like me. You always use parenthesis when methods have arguments, and never use them when methods have no argument. You don't like ambiguity. Neither do I

You've not seen my ERB templates, that is when I do leave off parentheses as it makes for much more readable templates e.g. <%= link_to "Somewhere", somewhere_path %>. I would much prefer being able to leave off the parentheses for helpers that aren't nested... I don't mind the comma's but 90% of the time helpers won't me nested and parens just add unnecessary noise.

Named args could be handled the Ruby way too e.g. {{link_to name, path, class: css_class}}. Guess we should get the opinion of the guys over at: #41

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment