Skip to content

Commit

Permalink
feat(tags): add set tag
Browse files Browse the repository at this point in the history
set tag allows data mutation at runtime with templates
  • Loading branch information
thetutlage committed Oct 17, 2017
1 parent 2ff8b36 commit a6735fc
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 1 deletion.
89 changes: 89 additions & 0 deletions src/Tags/SetTag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict'

/*
* edge
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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
3 changes: 2 additions & 1 deletion src/Tags/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'))()
}
120 changes: 120 additions & 0 deletions test/unit/tags/set.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
'use strict'

/*
* edge
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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')
})
})

0 comments on commit a6735fc

Please sign in to comment.