Hi! This is a rather weird request but is there anyway to return the list of of input variables as an array of string or as an object? For a code generation function in my code, I need to map a series of inputs into
a mustache-templated string. The input variables are named exactly the same as the templating variables and will shadow/map to them perfectly and because this needs to be done automatically, hence the strange request. Here's an example of what I mean:
Assuming a function Mustache.getTemplateVariablesListAsObject
var pycode = <see below>
Blockly.JavaScript['math_foo'] = function(block) {
var value_name = Blockly.JavaScript.valueToCode(block, 'NAME', Blockly.JavaScript.ORDER_ATOMIC);
// result would be something like: {value_name: value_name}
var inputList = Mustache.getTemplateVariablesListAsObject(pycode)
var code = Mustache.render(pycode, inputList)
return code;
};
def hello(foo):
print foo
hello({{value_name}})
I have been thinking over this for more than an hour and I still can't find a better way to do this. Would be really grateful if you can offer some alternative methods etc.
If I'm understanding you correctly you want something like this
var myTemplate = "{{foo}} is {{bar}}";
var variableNames = Mustache.VariableNames(myTemplate) // ['foo', 'bar']
That's assuming VariableNames
went and returned all the variable names from the template. If that's what you want then you could probably hack an implementation using the exposed parse
function on the mustache writer.
Here's some code to get you started:
var results = Mustache.parse('{{foo}} is {{bar}}')
.filter(function(v) { return v[0] === 'name' })
.map(function(v) { return v[1]; });
console.log(results) // ["foo", "bar"]
Note that while it's possible to implement a naive version of this, there is no way to unambiguously extract all tag names, because things like this:
{{# foo }}
* {{ bar }}
{{/ foo }}
鈥β燾ould mean either {foo: {bar: 'baz'}}
_or_ {foo: true, bar: 'baz'}}
.
@bobthecow is entirely right in this circumstance. The example i gave would only pull out all the identifier nodes, it would also remove all structure effectively flattening the tree.
@Romanx @bobthecow thanks for your help!
No problem. Good luck :)
I had a similar request, but needed to traverse the tree to find all variable names. Thought I'd share the solution I used if anyone needs a reference.
var parseTree = Mustache.parse('{{#foo}}{{bar}}{{/foo}} {{baz}}');
var variableList = parseTree.reduce(function flattenVariablesFromParseTree(acc, v){
if(v[0] === 'name'){
return acc.concat([v]);
} else if (v[0] === '#') {
return acc.concat(v[4].reduce(flattenVariablesFromParseTree, []));
} else {
return acc;
}
}, [])
.map(function(v){ return v[1]; });
//variableList: ["bar", "baz"]
@nicluo does this also suffer from the ambiguity problem?
Yep. It's inherent in the language spec.
The ambiguity problem is interesting, the docs mention that it would try to find the value in this context and then search the parent's context if no values are found. A bit of investigation got me into this:
{{bar}}
{{#foo}}
{{bar}}
{{#foo}}
{{bar}}
{{#baz}}
{{no}}
{{yes}}
{{/baz}}
{{/foo}}
{{/foo}}
var renderString = '{{bar}} {{#foo}}{{bar}} {{#foo}}{{bar}} {{#baz}}{{no}} {{yes}}{{/baz}}{{/foo}}{{/foo}}';
var renderContext = new Mustache.Context({
bar: 'bar',
baz: {
no: 'no'
},
foo: {
bar: 'y',
foo: {
bar: 'z',
yes: 'yes'
}
}});
var parseTree = Mustache.parse(renderString);
var variableRefList = [];
var variableNameList = parseTree.reduce(function flattenVariablesFromParseTree(acc, v){
// Skip non-name or non-# tags
if(v[0] !== 'name' && v[0] !== '#'){
return acc;
}
var paths = [v[1]].concat(this.parents.slice(0).map(function(e){
return [e, v[1]].join('.');
}));
// Pops available context until a value is found
var path;
while(path = paths.pop()){
if(renderContext.lookup(path)){
//push to advanced list
variableRefList.push(path);
contextFound = true;
break;
}
}
if(v[0] === 'name'){
return acc.concat([v]);
} else if (v[0] === '#') {
if(typeof renderContext.lookup(path) === 'object'){
this.parents = this.parents.concat([path]);
}
return acc.concat(v[4].reduce(
flattenVariablesFromParseTree.bind({
parents: this.parents
}), []));
}
}.bind({parents: []}), [])
.map(function(v){ return v[1]; });
//variableNameList: ["bar", "bar", "bar", "no", "yes"]
//variableRefList: ["bar", "foo", "foo.bar", "foo.foo", "foo.foo.bar", "baz", "baz.no", "foo.foo.yes"]
//Mustache.render(renderString, renderContext): bar y z no yes
The example is very contrived, and there are many tricks used to stay concise, but it should show how difficult I find it is to reinvent the wheel. Cheers
@Immortalin Can you elaborate / provide a better definition of the problem? What happens with properties nested in objects? Can you provide a more complete input and output?
@dasilvacontin the project that requires this feature is currently on hiatus so I am going to close this for the time being
Simple solution to get just the top level:
Mustache.parse(template).filter(function(v) { return v[0] === 'name' || v[0] === '#' || v[0] === '&' }).map(function(v) { return v[1]; });
Most helpful comment
Simple solution to get just the top level:
Mustache.parse(template).filter(function(v) { return v[0] === 'name' || v[0] === '#' || v[0] === '&' }).map(function(v) { return v[1]; });