From 21af8c4d7a151f1330b2d9d1f6b59f8172e8524e Mon Sep 17 00:00:00 2001 From: harttle Date: Sat, 26 Aug 2017 17:57:16 +0800 Subject: [PATCH] fix read/write variable in parent's scope, fix #39 --- src/scope.js | 13 ++++++++++++- tags/assign.js | 40 +++++++++++++++++++--------------------- tags/for.js | 6 +++--- test/scope.js | 13 ++++++++++--- test/tags/for.js | 13 +++++++++++++ 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/scope.js b/src/scope.js index a8ef4b8f5..ce4c1fcbb 100644 --- a/src/scope.js +++ b/src/scope.js @@ -20,7 +20,8 @@ var Scope = { } }, set: function (k, v) { - setPropertyByPath(this.scopes[this.scopes.length - 1], k, v) + var scope = this.findScopeFor(k) + setPropertyByPath(scope, k, v) return this }, push: function (ctx) { @@ -30,6 +31,16 @@ var Scope = { pop: function () { return this.scopes.pop() }, + findScopeFor: function (key) { + var i = this.scopes.length - 1 + while (i >= 0 && !(key in this.scopes[i])) { + i-- + } + if (i < 0) { + i = this.scopes.length - 1 + } + return this.scopes[i] + }, unshift: function (ctx) { assert(ctx, `trying to push ${ctx} into scopes`) return this.scopes.unshift(ctx) diff --git a/tags/assign.js b/tags/assign.js index a5e8b9557..74d6c4457 100644 --- a/tags/assign.js +++ b/tags/assign.js @@ -1,22 +1,20 @@ -const Liquid = require('..'); -const lexical = Liquid.lexical; -const Promise = require('any-promise'); -const re = new RegExp(`(${lexical.identifier.source})\\s*=(.*)`); -const assert = require('../src/util/assert.js'); +const Liquid = require('..') +const lexical = Liquid.lexical +const Promise = require('any-promise') +const re = new RegExp(`(${lexical.identifier.source})\\s*=(.*)`) +const assert = require('../src/util/assert.js') -module.exports = function(liquid) { - - liquid.registerTag('assign', { - parse: function(token){ - var match = token.args.match(re); - assert(match, `illegal token ${token.raw}`); - this.key = match[1]; - this.value = match[2]; - }, - render: function(scope) { - scope.set(this.key, liquid.evalOutput(this.value, scope)); - return Promise.resolve(''); - } - }); - -}; +module.exports = function (liquid) { + liquid.registerTag('assign', { + parse: function (token) { + var match = token.args.match(re) + assert(match, `illegal token ${token.raw}`) + this.key = match[1] + this.value = match[2] + }, + render: function (scope) { + scope.set(this.key, liquid.evalOutput(this.value, scope)) + return Promise.resolve('') + } + }) +} diff --git a/tags/for.js b/tags/for.js index 8aa4bb6d5..b1fb1ccc4 100644 --- a/tags/for.js +++ b/tags/for.js @@ -74,9 +74,9 @@ module.exports = function (liquid) { var html = '' return mapSeries(contexts, (context) => { - scope.push(context) - return liquid.renderer - .renderTemplates(this.templates, scope) + return Promise.resolve() + .then(() => scope.push(context)) + .then(() => liquid.renderer.renderTemplates(this.templates, scope)) .then(partial => (html += partial)) .catch(e => { if (e instanceof RenderBreakError) { diff --git a/test/scope.js b/test/scope.js index 81f54f006..b0067348a 100644 --- a/test/scope.js +++ b/test/scope.js @@ -116,9 +116,16 @@ describe('scope', function () { expect(scope.get('posts[category.diary[0]].name'), 'A Nice Day') }) - it('should create parents if needed', function () { - scope.set('foo.bar.coo', 'COO') - expect(scope.get('foo.bar.coo'), 'COO') + it('should create in parent scope if needed', function () { + scope.push({}) + scope.set('bar.coo', 'COO') + expect(scope.get('bar.coo')).to.equal('COO') + }) + it("should set parents' corresponding value", function () { + scope.push({}) + scope.set('foo', 'bar') + scope.pop() + expect(scope.get('foo')).to.equal('bar') }) }) describe('strict_variables', function () { diff --git a/test/tags/for.js b/test/tags/for.js index 32d4911a4..265f12b1e 100644 --- a/test/tags/for.js +++ b/test/tags/for.js @@ -33,6 +33,19 @@ describe('tags/for', function () { .to.eventually.equal('foo-coo-') }) + describe('scope', function () { + it('should read super scope', function () { + var src = '{%for a in (1..2)%}{{num}}{%endfor%}' + return expect(liquid.parseAndRender(src, {num: 1})) + .to.eventually.equal('11') + }) + it('should write super scope', function () { + var src = '{%for a in (1..2)%}{{num}}{%assign num = 2%}{%endfor%}' + return expect(liquid.parseAndRender(src, {num: 1})) + .to.eventually.equal('12') + }) + }) + describe('illegal', function () { it('should reject when for not closed', function () { var src = '{%for c in alpha%}{{c}}'