From a6735fc16b92a776ae4b08807631551953032b29 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Tue, 17 Oct 2017 13:15:44 +0530 Subject: [PATCH] feat(tags): add set tag set tag allows data mutation at runtime with templates --- src/Tags/SetTag.js | 89 +++++++++++++++++++++++++++ src/Tags/index.js | 3 +- test/unit/tags/set.spec.js | 120 +++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/Tags/SetTag.js create mode 100644 test/unit/tags/set.spec.js diff --git a/src/Tags/SetTag.js b/src/Tags/SetTag.js new file mode 100644 index 0000000..3b6323d --- /dev/null +++ b/src/Tags/SetTag.js @@ -0,0 +1,89 @@ +'use strict' + +/* + * edge + * + * (c) Harminder Virk + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. +*/ + +const BaseTag = require('./BaseTag') +const _ = require('lodash') + +/** + * The `set` tag allows you to mutate data + * object for a given template. + * + * @class SetTag + * @extends {BaseTag} + * @static + */ +class SetTag extends BaseTag { + /** + * Tag name to be used for registering + * the tag + * + * @method tagName + * + * @return {String} + */ + get tagName () { + return 'set' + } + + /** + * Whether or not the tag is block level + * tag. Which is no in this case. + * + * @method isBlock + * + * @return {Boolean} + */ + get isBlock () { + return false + } + + /** + * The expressions allowed inside an if tag. + * + * @method allowedExpressions + * + * @return {Array} + */ + get allowedExpressions () { + return ['SequenceExpression'] + } + + /** + * Compile the template + * + * @method compile + * + * @param {Object} compiler + * @param {Object} lexer + * @param {Object} buffer + * @param {String} options.body + * @param {Array} options.childs + * @param {Number} options.lineno + * + * @return {void} + */ + compile (compiler, lexer, buffer, { body, childs, lineno }) { + const [lhs, rhs] = this._compileStatement(lexer, body, lineno).toStatement() + buffer.writeLine(`this.context.setValue(${lhs}, ${rhs})`) + } + + /** + * Nothing needs to be done in runtime + * for an include tag + */ + run (Context) { + Context.macro('setValue', function (key, value) { + _.set(this.$presenter.$data, key, value) + }) + } +} + +module.exports = SetTag diff --git a/src/Tags/index.js b/src/Tags/index.js index 3fb9532..e237dc6 100644 --- a/src/Tags/index.js +++ b/src/Tags/index.js @@ -22,5 +22,6 @@ module.exports = { debugger: new (require('./DebuggerTag'))(), raw: new (require('./RawTag'))(), unless: new (require('./UnlessTag'))(), - mustache: new (require('./MustacheTag'))() + mustache: new (require('./MustacheTag'))(), + set: new (require('./SetTag'))() } diff --git a/test/unit/tags/set.spec.js b/test/unit/tags/set.spec.js new file mode 100644 index 0000000..0d456fb --- /dev/null +++ b/test/unit/tags/set.spec.js @@ -0,0 +1,120 @@ +'use strict' + +/* + * edge + * + * (c) Harminder Virk + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. +*/ + +const test = require('japa') +const Template = require('../../../src/Template') +const Loader = require('../../../src/Loader') +const Context = require('../../../src/Context') +const dedent = require('dedent-js') +const path = require('path') +const loader = new Loader(path.join(__dirname, '../../../test-helpers/views')) + +test.group('Tags | Set ', (group) => { + group.beforeEach(() => { + require('../../../test-helpers/transform-tags')(this, require('../../../src/Tags')) + }) + + test('parse the set block which mutates the data', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('username', 'virk') + ` + const output = template.compileString(statement) + + assert.equal(output, dedent` + return (function templateFn () { + let out = new String() + this.context.setValue('username', 'virk') + return out + }).bind(this)() + `) + }) + + test('parse the set block with value as an array of data', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('users', ['virk']) + ` + const output = template.compileString(statement) + + assert.equal(output, dedent` + return (function templateFn () { + let out = new String() + this.context.setValue('users', ['virk']) + return out + }).bind(this)() + `) + }) + + test('parse the set block with value as a reference', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('users', admin.users) + ` + const output = template.compileString(statement) + + assert.equal(output, dedent` + return (function templateFn () { + let out = new String() + this.context.setValue('users', this.context.accessChild(this.context.resolve('admin'), ['users'])) + return out + }).bind(this)() + `) + }) + + test('set literal value at runtime', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('username', 'virk') + {{ username }} + ` + + this.tags.set.run(Context) + const output = template.renderString(statement) + assert.equal(output.trim(), 'virk') + }) + + test('set identifier at runtime', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('username', admin.username) + {{ username }} + ` + + this.tags.set.run(Context) + const output = template.renderString(statement, { admin: { username: 'virk' } }) + assert.equal(output.trim(), 'virk') + }) + + test('set array at runtime', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('users', ['virk', 'nikk']) + {{ users.join(',') }} + ` + + this.tags.set.run(Context) + const output = template.renderString(statement) + assert.equal(output.trim(), 'virk,nikk') + }) + + test('set array at runtime', (assert) => { + const template = new Template(this.tags, {}, {}, loader) + const statement = dedent` + @set('users', ['virk', 'nikk']) + {{ users.join(',') }} + ` + + this.tags.set.run(Context) + const output = template.renderString(statement) + assert.equal(output.trim(), 'virk,nikk') + }) +})