Skip to content
This repository has been archived by the owner on Apr 11, 2018. It is now read-only.

Template variables can now execute functions and methods #182

Merged
merged 4 commits into from
Jun 9, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 42 additions & 23 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ exports.check = check;
* Returns an escaped string (safe for evaling). If context is passed
* then returns a concatenation of context and the escaped variable name.
*/
exports.escapeVarName = function (variable, context) {
exports.escapeVarName = function (variable, context, args) {
if (variable === '') {
return '';
}
if (_.isArray(variable)) {
_.each(variable, function (val, key) {
variable[key] = exports.escapeVarName(val, context);
Expand Down Expand Up @@ -235,20 +238,15 @@ exports.escapeVarName = function (variable, context) {
prevDot = false;
});

return chain;
return '(typeof ' + chain + ' === \'function\') ? ' + chain + '(' + args + ') : ' + chain;
};

exports.wrapMethod = function (variable, filter, context) {
var output = '(function () {\n',
args;

variable = variable || '""';

if (!filter) {
return variable;
exports.wrapArgs = function (arguments) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arguments is a reserved word. You shouldn't ever use it unless referencing a functions arguments object: more info

if (!arguments) {
return { declarations: '', args: ''};
}

args = filter.args.split(',');
var declarations = '',
args = arguments.split(',');
args = _.map(args, function (value) {
var varname,
stripped = value.replace(/^\s+|\s+$/g, '');
Expand All @@ -260,20 +258,37 @@ exports.wrapMethod = function (variable, filter, context) {
}

if (exports.isValidName(stripped)) {
output += exports.setVar(varname, parser.parseVariable(stripped));
declarations += exports.setVar(varname, parser.parseVariable(stripped));
return varname;
}

return value;
});

args = (args && args.length) ? args.join(',') : '""';
return { declarations: declarations, args: (args && args.length) ? args.join(',') : '""' };
};

exports.wrapMethod = function (variable, filter, context) {
var output = '(function () {\n',
args,
wrappedArgs;

variable = variable || '""';

if (!filter) {
return variable;
}

wrappedArgs = exports.wrapArgs(filter.args, output);

output += wrappedArgs.declarations;

output += 'return ';
output += (context) ? context + '["' : '';
output += filter.name;
output += (context) ? '"]' : '';
output += '.call(this';
output += (args.length) ? ', ' + args : '';
output += '(';
output += wrappedArgs.args;
output += ');\n';

return output + '})()';
Expand All @@ -299,8 +314,8 @@ exports.wrapFilter = function (variable, filter) {
return output;
};

exports.wrapFilters = function (variable, filters, context, escape) {
var output = exports.escapeVarName(variable, context);
exports.wrapFilters = function (variable, filters, context, escape, args) {
var output = exports.escapeVarName(variable, context, args);

if (filters && filters.length > 0) {
_.each(filters, function (filter) {
Expand All @@ -327,11 +342,12 @@ exports.wrapFilters = function (variable, filters, context, escape) {
return output;
};

exports.setVar = function (varName, argument) {
exports.setVar = function (varName, argument, args) {
var out = '',
props,
output,
inArr;
inArr,
wrappedArgs;
if ((/\[/).test(argument.name)) {
props = argument.name.split(/(\[|\])/);
output = [];
Expand All @@ -357,16 +373,19 @@ exports.setVar = function (varName, argument) {
}
});
}

wrappedArgs = exports.wrapArgs(args);
out += wrappedArgs.declarations;
out += 'var ' + varName + ' = "";\n' +
'if (' + check(argument.name, '_context') + ') {\n' +
' ' + varName + ' = ' + exports.wrapFilters(argument.name, argument.filters, '_context', argument.escape) + ';\n' +
' ' + varName + ' = ' + exports.wrapFilters(argument.name, argument.filters, '_context', argument.escape, wrappedArgs.args) + ';\n' +
'} else if (' + check(argument.name) + ') {\n' +
' ' + varName + ' = ' + exports.wrapFilters(argument.name, argument.filters, null, argument.escape) + ';\n' +
' ' + varName + ' = ' + exports.wrapFilters(argument.name, argument.filters, null, argument.escape, wrappedArgs.args) + ';\n' +
'}\n';

if (argument.filters.length) {
out += ' else if (true) {\n';
out += ' ' + varName + ' = ' + exports.wrapFilters('', argument.filters, null, argument.escape) + ';\n';
out += ' ' + varName + ' = ' + exports.wrapFilters('', argument.filters, null, argument.escape, wrappedArgs.args) + ';\n';
out += '}\n';
}

Expand Down
2 changes: 1 addition & 1 deletion lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ exports.compile = function compile(indent, context, template) {
code += ' _output = (typeof _output === "undefined") ? ' + wrappedInMethod + ': _output + ' + wrappedInMethod + ';\n';
}
code += '} else {\n';
code += helpers.setVar('__' + name, token);
code += helpers.setVar('__' + name, token, args);
code += ' _output = (typeof _output === "undefined") ? __' + name + ': _output + __' + name + ';\n';
code += '}\n';
}
Expand Down
2 changes: 0 additions & 2 deletions lib/tags/for.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ module.exports = function (indent, parser) {
throw new Error('Invalid arguments (' + operand2.name + ') passed to "for" tag');
}

operand1 = helpers.escapeVarName(operand1);

loopShared = 'loop.index = __loopIndex + 1;\n' +
'loop.index0 = __loopIndex;\n' +
'loop.revindex = __loopLength - loop.index0;\n' +
Expand Down
63 changes: 63 additions & 0 deletions tests/node/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,69 @@ describe('Variables', function () {
expect(parser.parse('{{\n foobar \n}}')).to.eql(token);
});

it('can contain method calls with no argument', function () {
expect(parser.parse('{{ foobar() }}')).to.eql([{
type: parser.TOKEN_TYPES.VAR,
name: 'foobar',
filters: [],
escape: false,
args: ''
}]);
});

it('can contain method calls with one argument', function () {
expect(parser.parse('{{ foobar(123) }}')).to.eql([{
type: parser.TOKEN_TYPES.VAR,
name: 'foobar',
filters: [],
escape: false,
args: 123
}]);
});

it('can contain method calls with two arguments', function () {
expect(parser.parse('{{ foobar(123, 456) }}')).to.eql([{
type: parser.TOKEN_TYPES.VAR,
name: 'foobar',
filters: [],
escape: false,
args: '123, 456'
}]);
});

it('accepts method calls with no arguments', function () {
var FooObject = function (arg) {
this.arg = arg;
};
FooObject.prototype.test = function () {
return this.arg + 'bar';
};
expect(swig.compile('{{ foo.test() }}')({ foo: new FooObject('bar') }))
.to.eql('barbar');
});

it('accepts method calls with one arguments', function () {
var FooObject = function (arg) {
this.arg = arg;
};
FooObject.prototype.test = function (arg) {
return arg + this.arg;
};
expect(swig.compile('{{ foo.test("baz") }}')({ foo: new FooObject('bar') }))
.to.eql('bazbar');
});

it('accepts method calls with two arguments', function () {
var FooObject = function (arg) {
this.arg = arg;
};
FooObject.prototype.test = function (arg1, arg2) {
return arg1 + this.arg + arg2;
};
expect(swig.compile('{{ foo.test("oo", "aa") }}')({ foo: new FooObject('bar') }))
.to.eql('oobaraa');
});

describe('accepts varying notation', function () {
it('can use dot notation', function () {
expect(swig.compile('{{ a.b.c }}')({ a: { b: { c: 'hi' }} }))
Expand Down