Skip to content

Commit

Permalink
Added data references.
Browse files Browse the repository at this point in the history
  • Loading branch information
coreybutler committed Feb 11, 2022
1 parent d380a22 commit 96e3811
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 8 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,40 @@ _Output:_
> Notice the values from the known flags are _first_.
</details>
### Custom Handler Data References
When building JavaScript applications, it may be desirable to pass data references to the `exec` method, making them available to handlers. For example:
```javascript
const sh = new Shell({
name: 'test',
commands: [{
name: 'run',
async handler (meta) {
return meta.reference.test // reference data
}
}]
})
const ref = { test: true }
const result = await sh.exec('run', ref)
```
In the command above, the data object (line 1) is passed to the `exec()` method using a special object as the second argument. This object is made available in the handler using the `meta.reference` attribute.
If a callback needs to be defined, the second argument of the `exec` method must have an attribute called `callback`, i.e.:
```javascript
const ref = {
test: true,
callback: function() {...}
}
const result = await sh.exec('run', ref)
```
The callback method is _not_ available in the `meta.reference`, but the callback will be executed when `exec()` is executed.
## Plugins
Plugins expose functions, objects, and primitives to shell handlers.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@author.io/shell",
"version": "1.8.18",
"version": "1.9.0",
"description": "A micro-framework for creating CLI-like experiences. This supports Node.js and browsers.",
"main": "./src/index.js",
"module": "./index.js",
Expand All @@ -16,6 +16,7 @@
"test:node:sanity": "dev test -rt node tests/01-sanity.js",
"test:node:base": "dev test -rt node tests/02-base.js",
"test:node:relationships": "dev test -rt node tests/06-relationships.js",
"test:node:metadata": "dev test -rt node tests/04-metadata.js",
"test:node:regression": "dev test -rt node tests/100-regression.js",
"test:browser": "dev test -rt browser tests/*.js",
"test:browser:sanity": "dev test -rt browser tests/01-sanity.js",
Expand Down
10 changes: 5 additions & 5 deletions src/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,9 +529,9 @@ export default class Command extends Base {
return data
}

async run (input, callback) {
async run (input, callback, reference = undefined) {
const fn = (this.#fn || this.defaultHandler).bind(this)
const data = typeof input === 'string' ? this.parse(input) : input
const metadata = typeof input === 'string' ? this.parse(input) : input

arguments[0] = this.deepParse(input)
arguments[0].plugins = this.plugins
Expand All @@ -558,7 +558,7 @@ export default class Command extends Base {

// No subcommand was recognized
if (this.middleware.size > 0) {
this.middleware.run(arguments[0], async meta => await Command.reply(fn(meta, callback)))
this.middleware.run(arguments[0], async meta => await Command.reply(fn(Object.assign(meta, { reference }), callback)))

if (trailers.size > 0) {
trailers.run(arguments[0])
Expand All @@ -568,8 +568,8 @@ export default class Command extends Base {
}

// Command.reply(fn(arguments[0], callback))
data.plugins = this.plugins
const result = await Command.reply(fn(data, callback))
metadata.plugins = this.plugins
const result = await Command.reply(fn(Object.assign(metadata, { reference }), callback))

if (trailers.size > 0) {
trailers.run(arguments[0])
Expand Down
15 changes: 13 additions & 2 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ export default class Shell extends Base {
}

async exec (input, callback) {
// Optionally apply reference data. Only for advanced use
// in applications (not strict CLIs).
let reference
if (typeof callback === 'object') {
if (!callback.hasOwnProperty('reference') && !callback.hasOwnProperty('callback')) { // eslint-disable-line no-prototype-builtins
throw new Error('exec method data references require a reference and/or callback attribute - recognized: ' + Object.keys(callback).join(', '))
}
reference = callback.reference
callback = callback.callback
}

// The array check exists because people are passing process.argv.slice(2) into this
// method, often forgetting to join the values into a string.
if (Array.isArray(input)) {
Expand Down Expand Up @@ -219,10 +230,10 @@ export default class Shell extends Base {
const term = processor.getTerminalCommand(args)

if (typeof callback === 'function') {
return callback(await Command.reply(await term.command.run(term.arguments, callback))) // eslint-disable-line standard/no-callback-literal
return callback(await Command.reply(await term.command.run(term.arguments, callback, reference))) // eslint-disable-line standard/no-callback-literal
}

return await Command.reply(await term.command.run(term.arguments, callback))
return await Command.reply(await term.command.run(term.arguments, callback, reference))
}

getCommandMiddleware (cmd) {
Expand Down
27 changes: 27 additions & 0 deletions tests/04-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,30 @@ test('Ordered Named Arguments', t => {

shell.exec('account create [email protected] pwd')
})


test('Application reference data', async t => {
const data = { test: true }
const sh = new Shell({
name: 'test',
commands: [{
name: 'run',
async handler (meta) {
return meta.reference.test
}
}]
})

const result = await sh.exec('run', { reference: data })

t.expect(result, true, 'reference data detected in handler')

try {
await sh.exec('run', { demo: data })
t.fail('failure to provide reference and callback throws error')
} catch (e) {
t.pass('failure to provide reference and callback throws error')
}

t.end()
})

0 comments on commit 96e3811

Please sign in to comment.