-
Notifications
You must be signed in to change notification settings - Fork 420
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
Add "anchored dot notation" #129
Conversation
So did you end up including with or without a pragma ? |
It's in the feature branch without a pragma, but I'm leaning toward adding one before it's merged. |
@bobthecow, just curious to know, why do you think mustache was implemented so that missing data in the stack looks up data up the context stack? It sounds really ambiguous/error prone to me. |
The context stack is actually intuitively what you'd expect in most cases. Consider this data: <?php
$data = array(
'owner' => 'Justin',
'pets' => array(
array('type' => 'dog', 'name' => 'Rex'),
array('type' => 'dog', 'name' => 'Max'),
array('type' => 'cat', 'name' => 'Sam'),
),
); And this template: <li id="pets">
{{# pets }}
<li>{{ name }} is a {{ type }} that belongs to {{ owner }}</li>
{{/ pets }}
</li> In most cases, you want to be able to reference anything in the parent context (for example, <?php
$owner = 'Justin';
$pets = array(
array('type' => 'dog', 'name' => 'Rex'),
array('type' => 'dog', 'name' => 'Max'),
array('type' => 'cat', 'name' => 'Sam'),
);
foreach ($pets as $pet) use ($owner) {
echo $pet['name'] . " is a " . $pet['type'] . " that belongs to " . $owner;
} That would be fairly absurd, especially if you needed access to more than one variable. Now imagine if there was no way to do If Mustache didn't have access to the context stack, you would run into far more problems than you currently do with this one particular edge case :) |
Thanks for the quick reply. |
@rothshahar One solution to that problem that I've used in the past is to compartmentalize the widgets as subrequests, or to run them through a manager of some sort. For example, we had a lot of CMS-y content that could be included in any template. We added a service to manage those CMS blocks, added it as a helper, and gave it a magic {{{ cms.blocks.someBlockId }}} Essentially, these were handled as subrequests, because the manager needed to be able to gather all of the data needed by a specific block, then render that block template as a standalone call, then return the rendered string to be included in the calling template. Another possible solution would be to add syntax equivalent to jinja's |
One further possibility that I've been playing with is presented by the It might look something like this: <?php
class OnlyHelper
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function __isset($name)
{
return true;
}
public function __get($name)
{
if (is_object($this->value) && !$this->value instanceof Closure) {
if (method_exists($this->value, $name)) {
return $this->value->$name();
} elseif (isset($this->value->$name)) {
return $this->value->$name;
}
} elseif (is_array($this->value) && array_key_exists($name, $this->value)) {
return $this->value[$name];
}
return '';
}
public static function create($value)
{
if ($value instanceof Traversable) {
$value = array_values(iterator_to_array($value));
}
if (self::isArray($value)) {
return array_map(function($v) { return new OnlyHelper($v); }, $value);
}
return new OnlyHelper($value);
}
protected static function isArray($value)
{
if (!is_array($value)) {
return false;
}
$i = 0;
foreach ($value as $k => $v) {
if ($k !== $i++) {
return false;
}
}
return true;
}
} Then it could be added as a helper: <?php
$mustache->addHelper('only', array('OnlyHelper', 'create')); ... and used to "mask" values and prevent context stack lookups: {{ title }}
{{# items | only }}
{{ title }}{{! empty if current item does not have a title property or method }}
{{/ items | only }} {{# widgetContext | only }}
{{> widgetPartial }}
{{/ widgetContext | only }} I'm not completely sold on the idea of filtered section contexts yet, but I figured I'd throw this idea out there as well. |
@bobthecow: Filtered sections are quite useful:
|
Hi @groue, @bobthecow, I wanted to find out the decision on Filtered sections. Was it implemented or are there any plans of doing so. |
@smehtaCAS Filtered sections are implemented in both GRMustache and Mustache.php See https://github.com/bobthecow/mustache.php/wiki/FILTERS-pragma |
Thanks @bobthecow. Will there be an example I can look at. |
Actually, I was trying to do the following:
Data: $data = array( 'items' => array( 'file_ids' => '1001,1003,1005' ) ); Template: {{# item }} {{# file_ids | utils.toArray }} Is this possible? |
Yes, that will work, but this is a pragma (non-standard extension to he mustache spec) so you need to enable it at the top of your template:
That said, your example should be preparing that data in code rather than using a filter in the template. |
Don't be too hard, @bobthecow... data preparation is cumbersome. :-) |
I tried the same but it does not work. It gives me nesting error. Following is what I am trying: $m->addHelper('utils', array( 'toArray' => function($value) { return explode(',', $value); } ) ); $data = array( 'items' => array( array( 'it_name' => 'test1', 'file_ids' => '1001,1003,1005' ), array( 'it_name' => 'test2', 'file_ids' => '2001,2003,2005' ) ) ); $tem = ' {{%FILTERS}} {{# items }} {{ it_name }} {{# file_ids | utils.toArray }} test {{/ file_ids }} {{/ items }} '; echo $m->render($tem, $data); The error is Any suggestion? |
You have to put the filter in the closing tag as well. It's ugly, I know, and that requirement might be dropped in the future. But the current Mustache spec says the opening and closing tags need to match. |
Thats works! I had spent hours for this. Thanks! |
To fix this ugliness, GRMustache allows empty closing tags: |
Note that this is no longer the case. Templates like this work great: {{%FILTERS}}
{{# states | eachPair }}
{{ key | upcase }}: {{ value }}
{{/ states }} |
664f8e7
to
066bcaa
Compare
066bcaa
to
39631de
Compare
39631de
to
5ab5fc6
Compare
1. Given that {{ . }} resolves as the top of the context stack; 2. And when any falsey segment in a dotted name is encountered, the whole name yields ''; 3. A name like {{ .name }} should imply {{ [top of stack].name }}; 4. Thus, it must resolve as truthy only if a member of the top of the context stack matches name. See mustache/spec#52
5ab5fc6
to
a5ad0a8
Compare
{{ . }}
resolves as the top of the context stack;''
;{{ .name }}
should imply{{ [top of stack].name }}
;name
.There have been several syntaxes proposed (mustache/spec#10 and mustache/spec#11 as well as my mustache/spec#52). This one is my favorite because,
{{ .foo.bar }}
isn't a valid variable name in the current spec.{{ ../foo }}
style anchors do, so I feel it's more in keeping with the logic-free nature of Mustache.{{ this.foo }}
syntax, which would be a backwards compatibility break, as well as limiting perfectly valid variable names in some languages.See spec discussion at mustache/spec#52 and some impetus at #98.
This is a complete implementation, but it's currently missing tests. I'm torn on including it without a pragma, since it would technically make Mustache.php not spec compliant.
Thoughts?