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

Support {{#.}} and Iterable values #2

Closed
michaelhixson opened this issue Jun 12, 2013 · 13 comments
Closed

Support {{#.}} and Iterable values #2

michaelhixson opened this issue Jun 12, 2013 · 13 comments

Comments

@michaelhixson
Copy link
Contributor

See the following commit for a small change that adds support for:

  1. {{#.}} as a section identifier
  2. Iterable values (instead of just List)

https://github.com/michaelhixson/mustache/commit/ce331cc921da62d6aadc3569a890120be682e580

Why? It removes the need for some extra map/list creation in code that uses this library. For instance, in this code, I want to pass a list of simple model objects to my template:

https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/dart/server.dart#L195-L198

Currently I have to insert that as the value in a single-entry map, so that it has a name the template understands (but I'd rather avoid that and use "{{#.}}"). Also, I have to add a "toList()" to turn my Iterable values into a List, or else the template doesn't know how to render it (but I'd rather just avoid doing that -- the template doesn't really need it to be a List).

@xxgreg
Copy link
Owner

xxgreg commented Jun 12, 2013

Good idea regarding support of Iterables.

Could you give me an example of the {{#.}} feature. I haven't seen that feature before.

Is it related to implicit iterators?

For example:

data:
  list: [ 'a', 'b', 'c', 'd', 'e' ]
template: '"{{#list}}({{.}}){{/list}}"'
expected: '"(a)(b)(c)(d)(e)"'

@michaelhixson
Copy link
Contributor Author

I'm not entirely sure if it's a "standard" Mustache feature. It is in this Java port though: https://github.com/spullara/mustache.java

(I could understand if you don't want to add something that isn't standard Mustache.)

It's for when the thing you're passing to template.renderString is itself a List (or Iterable). Like this:

data: [ 'a', 'b', 'c', 'd', 'e' ]
template: '"{{#.}}({{.}}){{/.}}"'
expected: '"(a)(b)(c)(d)(e)"'

@xxgreg
Copy link
Owner

xxgreg commented Jun 12, 2013

Ok - I see. That saves allocating a map in your use case. I'm not opposed to adding it, especially if it's already supported in other mustache libraries.

I suppose this should work as well then?

data: {'list': [ 'a', 'b', 'c', 'd', 'e' ]}
template: '"{{#list}}{{#.}}({{.}}){{/.}}{{/list}}"'
expected: '"(a)(b)(c)(d)(e)"'

And perhaps even this? (but that makes my head hurt)

data: {'list': [ 'a', 'b', 'c', 'd', 'e' ]}
template: '"{{#list}}{{#.}}({{.}}){{/.}} {{#.}}({{.}}){{/.}}{{/list}}"'
expected: '"(a)(b)(c)(d)(e) (a)(b)(c)(d)(e)"'

@michaelhixson
Copy link
Contributor Author

Hmm... I think the second one is wrong, and the first one is right, but not for the reason you think. The second one should be:

data: {'list': [ 'a', 'b', 'c', 'd', 'e' ]}
template: '"{{#list}}{{#.}}({{.}}){{/.}} {{#.}}({{.}}){{/.}}{{/list}}"'
expected: '"(a) (a)(b) (b)(c) (c)(d) (d)(e) (e)"'

The dot is like a 'this' keyword. In that example, it appears inside "{{#list}}", so the dot refers to the current entry in the list. Then, since the current entry is a string, "{{#.}}" is basically an if statement testing that the string is truthy (a.k.a. non-empty). If it is truthy (which they all are), then it prints 'this', which is still the string.

Another example:

data: {'list': [ [ 'a', 'b', 'c'], [1, 2, 3] ]}
template: '"{{#list}}<{{#.}}({{.}}){{/.}}>{{/list}}"'
expected:  '"<(a)(b)(c)><(1)(2)(3)>"'

I didn't actually test that with the Java Mustache library but I'm pretty sure that's how it works.

@xxgreg
Copy link
Owner

xxgreg commented Jun 12, 2013

Oops. My mistake. I wanted to make sure the feature nests correctly. Thanks
for the example.

On Wed, Jun 12, 2013 at 3:45 PM, michaelhixson [email protected]:

Hmm... I think the second one is wrong, and the first one is right, but
not for the reason you think. The second one should be:

data: {'list': [ 'a', 'b', 'c', 'd', 'e' ]}
template: '"{{#list}}{{#.}}({{.}}){{/.}} {{#.}}({{.}}){{/.}}{{/list}}"'
expected: '"(a) (a)(b) (b)(c) (c)(d) (d)(e) (e)"'

The dot is like a 'this' keyword. In that example, it appears inside
"{{#list}}", so the dot refers to the current entry in the list. Then,
since the current entry is a string, "{{#.}}" is basically an if statement
testing that the string is truthy (a.k.a. non-empty). If it is truthy
(which they all are), then it prints 'this', which is still the string.

Another example:

data: {'list': [ [ 'a', 'b', 'c'], [1, 2, 3] ]}
template: '"{{#list}}<{{#.}}({{.}}){{/.}}>{{/list}}"'
expected: '"<(a)(b)(c)><(1)(2)(3)>"'

I didn't actually test that with the Java Mustache library but I'm pretty
sure that's how it works.


Reply to this email directly or view it on GitHubhttps://github.com//issues/2#issuecomment-19305575
.

@michaelhixson
Copy link
Contributor Author

Heh, well I'm talking about what it should do. My code in the OP might be doing something else. I've been a bad/lazy contributor by not adding unit tests.

@michaelhixson
Copy link
Contributor Author

I confirmed that mustache.js has the expected behavior for the 2 examples I gave. https://github.com/janl/mustache.js

This library (with my changes) gets the second example right, but it fails on the first example.

data: {'list': [ 'a', 'b', 'c', 'd', 'e' ]}
template: '"{{#list}}{{#.}}({{.}}){{/.}} {{#.}}({{.}}){{/.}}{{/list}}"'
expected: '"(a) (a)(b) (b)(c) (c)(d) (d)(e) (e)"'

I had to add if statements to _renderSection and _renderInvSection for dealing with the "(value is String)" case.

_renderSection:

    } else if (value is String) {
      if (value.isEmpty) {
        // Do nothing.
      } else {
        _renderSectionWithValue(node, value);
      }

_renderInvSection:

    } else if (value is String) {
      if (value.isEmpty) {
        _renderSectionWithValue(node, value);
      } else {
        // Do nothing.
      }

That made it produce the expected output. Not sure if truthy strings are actually part of the Mustache spec or if I just made that up. If I just made it up, then I guess it's supposed to throw an error, and this change isn't necessary?

@xxgreg
Copy link
Owner

xxgreg commented Jun 12, 2013

Is this relevant?

The expectation is that the empty string is treated like any other value: if the host language treats it as falsey, it's
falsey, otherwise it's truthy. The behavior of coercing the empty string to false in JavaScript is as described and
as expected.

mustache/spec#28

@michaelhixson
Copy link
Contributor Author

Ah. So, truthy/falsey is left to the language. And Dart does not do coercion of non-booleans to booleans, right? In that case, the code from my last comment should not be added, and the error is correct.

@xxgreg
Copy link
Owner

xxgreg commented Jun 12, 2013

I think matching ruby's mustache library is the best bet. Taken from the bug linked above:

>> Mustache.render('This is a {{#test}}truthy{{/test}} string!', { :test => '' })
=> "This is a truthy string!"

@xxgreg
Copy link
Owner

xxgreg commented Jun 12, 2013

Yikes - but don't want to match this behaviour. Weird!

Mustache.render("{{#empty_string}}Truthy.{{/empty_string}} {{^empty_string}}And falsey, too{{/empty_string}}",     {:empty_string => ''})
=> "Truthy. And falsey, too"

@michaelhixson
Copy link
Contributor Author

I think it would be best to follow the advice of @pvande.

In the end, it's not only possible but simple to write host-language-agnostic templates with Mustache. Boolean (and Inverted) sections should be passed boolean arguments. Iterative sections should be passed list-like arguments. No more, no less. (As a convenience, if your language allows non-boolean types to be used as booleans, Mustache will do that coercion for you.)

That is, do not add any notion of string truthiness. Dart has no such thing and neither does (generic) Mustache. A section that resolves to a string value should throw an error, as it already does.

That's just my... vote. (Is this library a democracy? :)

@xxgreg
Copy link
Owner

xxgreg commented Jun 13, 2013

Yup - sounds good to me.

Btw - if you want to add yourself to the authors list in the pubspec.yaml file - go for it.

@xxgreg xxgreg closed this as completed Feb 17, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants