Implement Yargs Command plugin

This commit is contained in:
Juan Cruz Viotti 2014-11-20 11:50:58 -04:00
parent f1a76db5f5
commit 7610fe87cc
3 changed files with 164 additions and 2 deletions

View File

@ -0,0 +1,48 @@
yargs = require('yargs')
_ = require('lodash')
commandIndexBySignature = (command) ->
query = { signature: command.signature }
return _.findIndex(@command._commands, query)
isArgVariable = (word) ->
return /^(<.*>|\[.*\])$/.test(word)
splitSignature = (signature) ->
return signature.split(' ')
commandApplies = (command, args) ->
args._ ?= {}
splittedCommandSignature = splitSignature(command.signature)
if splittedCommandSignature.length isnt args._.length
return false
for word in splittedCommandSignature
index = splittedCommandSignature.indexOf(word)
if not isArgVariable(word) and word isnt args._[index]
return false
return true
run = ->
signature = splitSignature(@command._matchedCommand.signature)
parameters = _.difference(@argv._, signature)
@command._matchedCommand.action.apply(this, parameters)
module.exports = (signature, action) ->
command = { signature, action }
@command._commands ?= []
@command.run ?= _.bind(run, this)
commandIndex = commandIndexBySignature.call(this, command)
if commandIndex is -1
@command._commands.push(command)
else
@command._commands.splice(commandIndex, 1, command)
if commandApplies(command, @argv)
@command._matchedCommand = command
return this

View File

@ -0,0 +1,110 @@
chai = require('chai')
sinon = require('sinon')
chai.use(require('chai-things'))
chai.use(require('sinon-chai'))
expect = chai.expect
_ = require('lodash')
yargs = require('yargs')
yargs.command = require('./yargs-command')
COMMANDS =
appList:
signature: 'app list <id>'
action: (id) -> "App List: #{id}"
action2: (id) -> "App List: #{id}!"
deviceList:
signature: 'device list <id>'
action: (id) -> "Device: #{id}"
ARGS =
appList:
_: [ 'app', 'list', '7' ]
deviceList:
_: [ 'device', 'list', '7' ]
noMatch:
_: [ 'foo', 'bar', 'baz' ]
# Hacky way to check that both functions are equal based
# on what they returns, as mocha crashes when trying equal
# or deep equal directly on the functions for some reason
areFunctionsEqual = (fn1, fn2) ->
return fn1.toString() is fn2.toString()
describe 'Yargs Command:', ->
cleanUpYargs = ->
yargs.command._commands = [] if yargs.command._commands
yargs.argv = {}
delete yargs.command._matchedCommand
beforeEach ->
cleanUpYargs()
afterEach ->
cleanUpYargs()
it 'should expose a public command function', ->
expect(yargs.command).to.be.an.instanceof(Function)
it 'should return yargs to enable chaining', ->
result = yargs.command(COMMANDS.appList.signature, COMMANDS.appList.action)
expect(result).to.equal(yargs)
it 'should contain an empty _commands array', ->
expect(yargs.command._commands).to.deep.equal([])
it 'should push a command to _commands', ->
yargs.command(COMMANDS.appList.signature, COMMANDS.appList.action)
expect(yargs.command._commands).to.contain.something.that.deep.equals
signature: COMMANDS.appList.signature
action: COMMANDS.appList.action
describe 'if adding the same command signature twice', ->
beforeEach ->
yargs.command(COMMANDS.appList.signature, COMMANDS.appList.action)
yargs.command(COMMANDS.appList.signature, COMMANDS.appList.action2)
it 'should contain only one command', ->
expect(yargs.command._commands).to.have.length(1)
it 'should make the last action override the first one', ->
firstCommandAction = yargs.command._commands[0].action
expect(areFunctionsEqual(firstCommandAction, COMMANDS.appList.action2)).to.be.true
describe 'given various registered commands', ->
registerCommands = ->
yargs.command(COMMANDS.appList.signature, COMMANDS.appList.action)
yargs.command(COMMANDS.deviceList.signature, COMMANDS.deviceList.action)
testCommand = (name) ->
yargs.argv = ARGS[name]
registerCommands()
matchedCommand = yargs.command._matchedCommand
expect(matchedCommand.signature).to.equal(COMMANDS[name].signature)
it 'should choose the right command', ->
for command in [
'appList'
'deviceList'
]
testCommand(command)
it 'should be undefined if no match', ->
yargs.argv = ARGS.noMatch
registerCommands()
expect(yargs.command._matchedCommand).to.not.exist
it 'should have a run public function', ->
expect(yargs.command.run).to.be.an.instanceof(Function)
describe '#run()', ->
it 'should call the action with the correct parameters', ->
yargs.argv = ARGS.appList
callback = sinon.spy()
yargs.command(COMMANDS.appList.signature, callback)
yargs.command.run()
expect(callback).to.have.been.calledWith(_.last(ARGS.appList._))

View File

@ -23,7 +23,10 @@
"gulp-coffeelint": "~0.4.0", "gulp-coffeelint": "~0.4.0",
"nock": "~0.48.2", "nock": "~0.48.2",
"mock-fs": "~2.3.2", "mock-fs": "~2.3.2",
"chai-as-promised": "~4.1.1" "chai-as-promised": "~4.1.1",
"chai-things": "~0.2.0",
"sinon": "~1.12.1",
"sinon-chai": "~2.6.0"
}, },
"dependencies": { "dependencies": {
"request": "~2.47.0", "request": "~2.47.0",
@ -37,6 +40,7 @@
"open": "0.0.5", "open": "0.0.5",
"inquirer": "~0.8.0", "inquirer": "~0.8.0",
"cliff": "~0.1.9", "cliff": "~0.1.9",
"underscore.string": "~2.4.0" "underscore.string": "~2.4.0",
"yargs": "~1.3.3"
} }
} }