i just forked mustache.js and was working on some patches when i asked
myself - why do booleans and enumerables use the same syntax? they are
fundamentally very different in both meaning and operation.
- a boolean will not change the context of execution and the tagname
has nothing to do with the data that is actually displayed - you have
to look at the block contents to determine this.
- an enumerable does change the execution context and the tagname is a
good description of what is being enumerated and rendered
frankly, i can't think of any instance when a template author wont
need to explicitly know and understand the difference between the two.
overloading {{# makes things more confusing, not easier, cause you
cant easily distinguish one from the other in the template (especially
programatically)
i was wondering what the reasoning was for this, it makes no sense to
me. i'll be committing a patch to split the two apart in my fork,
using {{? for booleans.
i asked Chris Wanstrath what he though and he said to post here. his
reply was this:
"Short answer: Perhaps their explanation[wiki] should be condensed
into one tag type, instead of two. There's no overloading - a section
does one thing: it renders a block of text one or more times."
to me, it falls short of a good explanation. anyone else see this as
something that may warrant a syntax change or at least further
discussion?
thanks,
Leon
> i just forked mustache.js and was working on some patches when i asked
> myself - why do booleans and enumerables use the same syntax? they are
> fundamentally very different in both meaning and operation.
There's really only sections. As I said, perhaps this is a failure of
the documentation. They are explained separately to help make things
more clear but I fear they've made things more confusing.
Sections are rendered one or more times based on the tag's value. So
if the tag is true, an object, or a list with multiple elements, the
section will be rendered. If it's false, nil, or an empty list then
nothing will be rendered. This is not unlike Lisp's philosophy, where
an empty list and nil are the same.
It seems like you want a tag specifically for enumerables, which
changes the context, and a tag specifically for booleans, which does
not change the context.
Well, okay. But take this example from
http://github.com/defunkt/resque/tree/mustache:
{{#worker?}}
<h1>Worker {{to_s}}</h1>
<td>{{worker_host}}</td>
<td>{{worker_pid}}</td>
<td><span class="time">{{started}}</span></td>
<td class='queues'>
{{linked_queues}}
</td>
<td>{{processed}}</td>
<td>{{failed}}></td>
{{/worker?}}
Which is this? A boolean section or an enumerable section? If it's a
boolean section, then by your definition the section does not inherit
the context (a Worker object) and every tag will fail to render. If
it's a strict "enumerable" section, I need to make the `worker?`
method return a one element list instead of just an object, right? So
it will fail there, too - the `worker?` method returns an instance of
Worker which becomes the context for the section.
The answer is this is neither a "boolean" or an "enumerable" section.
It's just a section and is a great example of where Mustache shines.
As I said, perhaps the documentation should use this (and similar)
situations which are unique to Mustache as an example instead of using
familiar but not-quite-accurate descriptions.
Chris
data:
myProducts: [
{prod_id: "123", sku: "A", price: 0.99},
{prod_id: "456", sku: "B", price: 1.99},
{prod_id: "789", sku: "C", price: 2.99}
]
product_tpl:
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
i can use this template to render one product by passing a single
clean object, but not an array of objects. now template 2:
product_list_tpl:
{{#products}}
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
{{/products}}
with this i can render multiple products or one product, BUT i have to
wrap everything so that it's keyed in a hash first: {products:
myProducts} or {products: myProducts[0]}
so...there's no SINGLE template into which you can pass a clean
enumerable or a clean object to render EITHER a list OR a single
instance because there is no implicit context understanding. this also
prevents partials within enumerables from working within the context
of the currently enumerated element:
product_list_tpl:
{{#products}}
{{>product}} (product_tpl (from above) as a partial within an
iterator)
{{/products}}
i need some sort of implicit context resolution to allow these cases
to work without hacks or needless template duplication.
Leon
{{#}}
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
{{/}}
...or maybe {{@this}}{{/this}} or {{#?}} {{/?}}
here, the context would resolve to the currently enumerated object if
the passed data is enumerable or itself if the data is an object/hash.
internally, i'd just incur the overhead and wrap all single objects/
hashes with enumerables to handle everything uniformly.
combined with the implicit iterator construct from mustache.js {.}
which prints out literals, this will add a vast amount of flexibility.
Leon
this context issue only happens at the root node when calling render()
because rather than type-testing the root context like every other
node, it assumes an implicit hash. so why not just type-test the root
node the same way. this would allow passing anything into render()
that the framework accounts for rather than just hashed objects. it
would also allow partials to render in the correct context rather than
just expanding them.
this would allow a template like this:
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
to render properly if you pass an enumerable:
[
{prod_id: "123", sku: "A", price: 0.99},
{prod_id: "456", sku: "B", price: 1.99},
{prod_id: "789", sku: "C", price: 2.99}
]
or just a hash:
{prod_id: "123", sku: "A", price: 0.99}
the root behavior will always be implicit based on the datatype (like
other nodes), but template delimiters are not needed because you're
rendering everything.
--
Leon
this context issue only happens at the root node when calling render()
because rather than type-testing the root context like every other
node, it assumes an implicit hash. so why not just type-test the root
node the same way. this would allow passing anything into render()
that the framework accounts for rather than just hashed objects. it
would also allow partials to render in the correct context rather than
just expanding them.
this would allow a template like this:
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
to render properly if you pass an enumerable:
[
{prod_id: "123", sku: "A", price: 0.99},
{prod_id: "456", sku: "B", price: 1.99},
{prod_id: "789", sku: "C", price: 2.99}
]
or just a hash:
{prod_id: "123", sku: "A", price: 0.99}
the root behavior will always be implicit based on the datatype (like
Cheers,
Chris
> --
> To unsubscribe, reply using "remove me" as the subject.
>
--
Chris Wanstrath
http://github.com/defunkt
On Mar 31, 11:11 pm, Chris Wanstrath <ch...@ozmm.org> wrote:
Using a list instead of a hash to render a Mustache template should
behave as if the entire template were a section. The template will be
rendered N times, once for each item in the list, and the context
should change each time appropriately.
This is an interesting way to think of it, and something I had not
considered before.
In the current version of Mustache, this works:
$ cat test.yml test.mustache
---
{prod_id: "123", sku: "A", price: 0.99}
---
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
$ mustache test.yml test.mustache
<tr id="123">
<td>A</td>
<td>0.99</td>
</tr>
In my implicit_context[1] branch, this works:
$ cat examples/implicit_section.yml examples/implicit_section.mustache
---
[
{prod_id: "123", sku: "A", price: 0.99},
{prod_id: "456", sku: "B", price: 1.99},
{prod_id: "789", sku: "C", price: 2.99}
]
---
<tr id="{{prod_id}}">
<td>{{sku}}</td>
<td>{{price}}</td>
</tr>
$ mustache examples/implicit_section.yml examples/implicit_section.mustache
<tr id="123">
<td>A</td>
<td>0.99</td>
</tr>
<tr id="456">
<td>B</td>
<td>1.99</td>
</tr>
<tr id="789">
<td>C</td>
<td>2.99</td>
</tr>
How's that?
Chris
[1]: http://github.com/defunkt/mustache/commit/bbceca40a73e987ebe4aed508fc8553032b01d4d
if you think about it, the {{#tag {{/tag section delimiters don't
actually imply a context switch at all. they only delimit a possibly
repeatable template subsection. the new_context is ALWAYS defined by the
datatype of current_context[tag].
the root template passed to the framework IS a section like any other,
just without delimiters because there is need to parse it from a parent,
and without a tag because there's no parent context to check. BUT - it
never has an implicit context, the context is still determined by the
datatype passed to the renderer, just like every other node. this makes
everything uniform and recursive internally, without exception.
a good way to do this is to expose a function that isnt the
private/recursive sec_render(data, tpl, partials...etc) but a public
wrapper for it called render() which will pre-wrap all incoming
enumerable data with a hash and a hardcoded unusual key before passing
it into sec_render(), which in turn will always pre-check if that key
exists and if it does, it will use it and context switch as usual.
var Mustache = function(){
// root key for render+wrapper
this.root_key = "_tmpl_root";
this.html_buffer = "";
// private recursive renderer
// (accepts only hashes for data)
var sec_render = function(data, tpl, partials...etc) {
// detect if root and switch context
if (data[this.root_key]) {
data = data[this.root_key]
}
var sec_buf = "";
// now continue with "data" as context
// ...
// ...
return sec_buf;
}
// public render wrapper
// (accepts hashes or enumerables for data)
this.render = function(data, tpl, partials...etc) {
// check if enumerable + wrap into hash
if (data.length) {
data = {this.root_key: data}
}
// not tested, might need to use .call()
return sec_render.apply(this, arguments)
}
}
that's option A. option B is nearly the same but more jQuery-like: turn
everything enumerable, i actually prefer option B - you prewrap any
incoming objects in an enumerable and iterate those with sec_render().
but that's not going to make any huge diff in the end - more personal
opinion.
thanks,
Leon