This patch supersedes #8164, which itself replaced #8060 and #8078. It goes way beyond the limited scope of these, providing support for just about everything, at a weight gain 5 bytes below 1KB.
It is also extensively tested (29 assertions in 7 tests, covering all possible combinations, including nightmarish ones).
With this patch, you can:
- Use dot-notation and []-based indexing.
- Use method names as regular properties: these methods are going to be called with no argument. You can keep chaining components after a method call: it needs not be the last part of the expression.
- Provide custom replacement callbacks in the options (2nd) parameter, which are components of the expression starting with a specific string (defaults to '@', can be customized through the callbackPrefix option). These callbacks will be called with two arguments: first the object that is their current context, second the original object passed to evaluate.
- Simplify one-shot templating by using 'templateText'.evaluate(obj) instead of new Template('templateText').evaluate(obj). The factory method accepts the same optional parameter for options as the Template constructor does.
Note that...
# This patch preserves complete backward compatibility.
# You can use empty brackets to represent the empty string as a property name. Brackets are, essentially, there for weird property names (e.g. those containing brackets, which can then be escaped with \, or those with dots). There is no need for quotes inside the brackets, and they are indeed going to be used literally if you put any.
The unit tests provide examples aplenty, but here is a monster test with combined features, straight out of them:
var source = '#{name} is #{age} years old, ' +
'managed by #{manager.name}, #{manager.age}.\n' +
'Yes, #{manager.@cb2}.\n' +
'#{name} works on #{getJob}.\n' +
'Colleagues include #{colleagues[0].name} ' +
'(working on #{colleagues[0].getJob}) ' +
'and #{colleagues[1].name},\n' +
'but "#{colleagues[0].@cb2}" is false.';
var subject = { name: 'Stephan', age: 22, manager: { name: 'John', age: 29 },
colleagues: [
{ name: 'Mark', getJob: function() { return 'SOAP'; } },
{ name: 'Indy' } ],
getJob: function() { return 'Prototype stuff'; }
};
var opts = {
cb2: function(local, global) {
return local.name + ' supervises ' + global.name;
}
};
new Template(source, opts).evaluate(subject)
This will evaluate to:
Stephan is 22 years old, managed by John, 29.
Yes, John supervises Stephan.
Stephan works on Prototype stuff.
Colleagues include Mark (working on SOAP) and Indy,
but "Mark supervises Stephan" is false.
Note that you could very well use templates like:
'#{colleagues[0].getJob.toLowerCase}'
// => 'soap'
'#{colleagues[0].getJob.length}'
// => '4'
(Where you get multiple functions, or a function and a property, etc.)
You could also use square brackets for weird property names:
var subject = { '': 'empty', '.NET': 'C#' };
'#{[]}'.evaluate(subject)
// => 'empty'
'#{[.NET]}'.evaluate(subject)
// => 'C#'
Isn't it sweeeeeet?