About halfway through converting all tags to async

This commit is contained in:
Tim Hardy
2016-09-18 01:23:29 -05:00
parent ba390df7b6
commit dc85c80afe
8 changed files with 61 additions and 13 deletions
+2 -2
View File
@@ -38,12 +38,12 @@ var render = {
renderTag: function(template, scope, register) {
if (template.name === 'continue') {
scope.set('forloop.skip', true);
return;
return Promise.resolve('');
}
if (template.name === 'break') {
scope.set('forloop.stop', true);
scope.set('forloop.skip', true);
return;
return Promise.resolve('');
}
return template.render(scope, register);
},
+2 -1
View File
@@ -1,4 +1,5 @@
const lexical = require('./lexical.js');
const Promise = require('any-promise');
const Exp = require('./expression.js');
const TokenizationError = require('./error.js').TokenizationError;
@@ -21,7 +22,7 @@ module.exports = function() {
var reg = register[this.name];
if(!reg) reg = register[this.name] = {};
var obj = hash(this.token.args, scope);
return this.tagImpl.render && this.tagImpl.render(scope, obj, reg) || '';
return this.tagImpl.render && this.tagImpl.render(scope, obj, reg) || Promise.resolve('');
},
parse: function(token, tokens){
this.type = 'tag';
+2
View File
@@ -1,4 +1,5 @@
var Liquid = require('..');
var Promise = require('any-promise');
var lexical = Liquid.lexical;
var re = new RegExp(`(${lexical.identifier.source})\\s*=(.*)`);
@@ -13,6 +14,7 @@ module.exports = function(liquid) {
},
render: function(scope, hash) {
scope.set(this.key, liquid.evalOutput(this.value, scope));
return Promise.resolve('');
}
});
+2 -1
View File
@@ -1,4 +1,5 @@
var Liquid = require('..');
var Promise = require('any-promise');
var lexical = Liquid.lexical;
var groupRE = new RegExp(`^(?:(${lexical.value.source})\\s*:\\s*)?(.*)$`);
var candidatesRE = new RegExp(lexical.value.source, 'g');
@@ -37,7 +38,7 @@ module.exports = function(liquid) {
idx = (idx + 1) % this.candidates.length;
register[fingerprint] = idx;
return Liquid.evalValue(candidate, scope);
return Promise.resolve(Liquid.evalValue(candidate, scope));
}
});
};
+42 -5
View File
@@ -1,4 +1,6 @@
var Liquid = require('..');
var Promise = require('any-promise');
var _ = require('lodash');
var lexical = Liquid.lexical;
var re = new RegExp(`^(${lexical.identifier.source})\\s+in\\s+` +
`(${lexical.value.source})` +
@@ -44,6 +46,8 @@ module.exports = function(liquid) {
collection = collection.slice(offset, offset + limit);
if(this.reversed) collection.reverse();
var scopes = [];
collection.some((item, i) => {
ctx[this.variable] = item;
ctx.forloop = {
@@ -57,14 +61,47 @@ module.exports = function(liquid) {
stop: false,
skip: false
};
// todo: verify scope management is good here. Make sure we don't consume too many resources here with the clone.
// Is there a simpler solution?
scope.push(ctx);
html += liquid.renderer.renderTemplates(this.templates, scope);
var breakloop = scope.get('forloop.stop');
// We are just putting together an array of the arguments we will be passing to our sequential promises
scopes.push(_.clone(scope));
scope.pop(ctx);
if (breakloop) return true;
});
return html;
// This is some pretty tricksy javascript, at least to me. Bluebird would have made this a lot easier, but
// we are trying to not use anything that can't be done in native node Promises.
// This basically just processes an array of promises sequentially for every argument in the array - http://webcache.googleusercontent.com/search?q=cache:rNbMUn9TPtkJ:joost.vunderink.net/blog/2014/12/15/processing-an-array-of-promises-sequentially-in-node-js/+&cd=5&hl=en&ct=clnk&gl=us
var lastPromise = scopes.reduce((promise, scope) => {
return promise.then(function(partial) {
var breakloop = scope.get('forloop.stop');
if (breakloop)
throw new Error('forloop.stop'); // this will stop the sequential promise chain
html += partial;
return liquid.renderer.renderTemplates(this.templates, scope);
})
.catch((error) => {
if (error === 'forloop.stop') {
// the error is a controlled, purposeful stop. so just return the html that we have up to this point
return html;
} else {
// rethrow actual error
throw new Error(error);
}
});
}, Promise.resolve()); // start the reduce chain with a resolved Promise. After first run, the "promise" argument
// in our reduce callback will be the returned promise from our "then" above. In this
// case, the promise returned from liquid.renderer.renderTemplates.
lastPromise
.then(() => {
return Promise.resolve(html);
})
.catch((error) => {
throw new Error(error);
});
}
});
};
+3 -1
View File
@@ -1,4 +1,5 @@
var Liquid = require('..');
var Promise = require('any-promise');
var lexical = Liquid.lexical;
var re = new RegExp(`(${lexical.identifier.source})`);
@@ -20,7 +21,8 @@ module.exports = function(liquid) {
stream.start();
},
render: function(scope, hash) {
return this.tokens.map(token => token.raw).join('');
var tokens = this.tokens.map(token => token.raw).join('');
return Promise.resolve(tokens);
}
});
+1
View File
@@ -51,6 +51,7 @@ module.exports = function(liquid) {
ctx[this.variable] = item;
scope.push(ctx);
html += `<td class="col${col}">`;
// todo: replace with sequential promises, see for.js
html += liquid.renderer.renderTemplates(this.templates, scope);
html += '</td>';
scope.pop(ctx);
+7 -3
View File
@@ -11,7 +11,11 @@ var liquid = Liquid({
ctx, src, dst;
function test(src, dst) {
expect(liquid.parseAndRender(src, ctx)).to.equal(dst);
liquid.parseAndRender(src, ctx)
.then((result) => {
expect(result.to.equal(dst));
});
//expect(liquid.parseAndRender(src, ctx)).to.equal(dst);
}
function testThrow(src, pattern) {
@@ -203,7 +207,7 @@ describe('tags', function() {
test('{% decrement one %}{{one}}', '0');
});
it('should support tablerow', function() {
it.only('should support tablerow', function() {
src = '{% tablerow i in alpha cols:2 %}{{ i }}{% endtablerow %}';
dst = '<table>' +
'<tr class="row1"><td class="col1">a</td><td class="col2">b</td></tr>' +
@@ -279,7 +283,7 @@ describe('tags', function() {
expect(liquid.renderFile(filepath, ctx)).to.equal(dst);
});
it.only('should support nested includes', function() {
it('should support nested includes', function() {
//expect(liquid.renderFile('personInfo.html', ctx)).to.equal('This is a person <p>Joe Shmoe<br/>City: Dallas</p>');
return liquid.renderFile('personInfo.html', ctx).should.eventually.equal('This is a person <p>Joe Shmoe<br/>City: Dallas</p>')
});