coa 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
package/.npmignore CHANGED
@@ -1,3 +1,6 @@
1
1
  .idea
2
2
  *.iml
3
- node_modules
3
+ node_modules/
4
+ !node_modules/coa*.js
5
+ lib-cov/
6
+ html-report/
package/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: node_js
2
+
3
+ node_js:
4
+ - "0.6"
5
+ - "0.8"
6
+ - "0.10"
7
+ - "0.11"
8
+
9
+ matrix:
10
+ allow_failures:
11
+ - node_js: 0.11
package/GNUmakefile CHANGED
@@ -1,15 +1,34 @@
1
+ BIN = ./node_modules/.bin
2
+
1
3
  .PHONY: all
2
4
  all: lib
3
5
 
4
6
  lib: $(foreach s,$(wildcard src/*.coffee),$(patsubst src/%.coffee,lib/%.js,$s))
5
7
 
8
+ lib-cov: clean-coverage lib
9
+ $(BIN)/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib
10
+
6
11
  lib/%.js: src/%.coffee
7
- coffee -cb -o $(@D) $<
12
+ $(BIN)/coffee -cb -o $(@D) $<
8
13
 
9
14
  .PHONY: test
10
15
  test: lib
11
- ./node_modules/.bin/vows --spec
16
+ $(BIN)/mocha
17
+
18
+ .PHONY: coverage
19
+ coverage: lib-cov
20
+ COVER=1 $(BIN)/mocha --reporter mocha-istanbul
21
+ @echo
22
+ @echo Open html-report/index.html file in your browser
12
23
 
13
24
  .PHONY: watch
14
25
  watch:
15
- coffee --watch --bare --output lib src/*.coffee
26
+ $(BIN)/coffee --watch --bare --output lib src/*.coffee
27
+
28
+ .PHONY: clean
29
+ clean: clean-coverage
30
+
31
+ .PHONY: clean-coverage
32
+ clean-coverage:
33
+ -rm -rf lib-cov
34
+ -rm -rf html-report
package/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Command-Option-Argument
2
+ [![build status](https://secure.travis-ci.org/veged/coa.png)](http://travis-ci.org/veged/coa)
2
3
 
3
4
  ## What is it?
4
5
 
@@ -226,6 +227,11 @@ Default value passed through validation function as ordinary value.<br>
226
227
  **@param** *Object* `_def`<br>
227
228
  **@returns** *COA.Opt* `this` instance (for chainability)
228
229
 
230
+ #### Opt.input
231
+ Make option value inputting stream.
232
+ It's add useful validation and shortcut for STDIN.
233
+ **@returns** *{COA.Opt}* `this` instance (for chainability)
234
+
229
235
  #### Opt.output
230
236
  Make option value outputing stream.<br>
231
237
  It's add useful validation and shortcut for STDOUT.<br>
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require(process.env.COVER? './lib-cov' : './lib');
package/lib/arg.js CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by CoffeeScript 1.3.3
1
+ // Generated by CoffeeScript 1.6.3
2
2
  var Arg, Cmd, Color, Opt;
3
3
 
4
4
  Color = require('./color').Color;
@@ -127,7 +127,13 @@ exports.Arg = Arg = (function() {
127
127
  };
128
128
 
129
129
  Arg.prototype._usage = function() {
130
- return Color('lpurple', this._name.toUpperCase()) + ' : ' + this._title;
130
+ var res;
131
+ res = [];
132
+ res.push(Color('lpurple', this._name.toUpperCase()), ' : ', this._title);
133
+ if (this._req) {
134
+ res.push(' ', Color('lred', '(required)'));
135
+ }
136
+ return res.join('');
131
137
  };
132
138
 
133
139
  Arg.prototype._requiredText = function() {
@@ -154,6 +160,16 @@ exports.Arg = Arg = (function() {
154
160
 
155
161
  Arg.prototype.end = Cmd.prototype.end;
156
162
 
163
+ /**
164
+ Apply function with arguments in context of arg instance.
165
+ @param {Function} fn
166
+ @param {Array} args
167
+ @returns {COA.Arg} this instance (for chainability)
168
+ */
169
+
170
+
171
+ Arg.prototype.apply = Cmd.prototype.apply;
172
+
157
173
  return Arg;
158
174
 
159
175
  })();
package/lib/cmd.js CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by CoffeeScript 1.3.3
1
+ // Generated by CoffeeScript 1.6.3
2
2
  var Cmd, Color, PATH, Q, UTIL,
3
3
  __slice = [].slice;
4
4
 
@@ -35,6 +35,7 @@ exports.Cmd = Cmd = (function() {
35
35
  this._opts = [];
36
36
  this._optsByKey = {};
37
37
  this._args = [];
38
+ this._ext = false;
38
39
  }
39
40
 
40
41
  Cmd.get = function(propertyName, func) {
@@ -70,10 +71,13 @@ exports.Cmd = Cmd = (function() {
70
71
  });
71
72
 
72
73
  Cmd.prototype._parent = function(cmd) {
74
+ this._cmd = cmd || this;
73
75
  if (cmd) {
74
76
  cmd._cmds.push(this);
77
+ if (this._name) {
78
+ this._cmd._cmdsByName[this._name] = this;
79
+ }
75
80
  }
76
- this._cmd = cmd || this;
77
81
  return this;
78
82
  };
79
83
 
@@ -221,6 +225,18 @@ exports.Cmd = Cmd = (function() {
221
225
  return this.cmd().name('completion').apply(require('./completion')).end();
222
226
  };
223
227
 
228
+ /**
229
+ Allow command to be extendable by external node.js modules.
230
+ @param {String} [pattern] Pattern of node.js module to find subcommands at.
231
+ @returns {COA.Cmd} this instance (for chainability)
232
+ */
233
+
234
+
235
+ Cmd.prototype.extendable = function(pattern) {
236
+ this._ext = pattern || true;
237
+ return this;
238
+ };
239
+
224
240
  Cmd.prototype._exit = function(msg, code) {
225
241
  return process.once('exit', function() {
226
242
  if (msg) {
@@ -304,7 +320,7 @@ exports.Cmd = Cmd = (function() {
304
320
  };
305
321
 
306
322
  Cmd.prototype._parseCmd = function(argv, unparsed) {
307
- var cmd, i, optSeen;
323
+ var c, cmd, cmdDesc, e, i, optSeen, pkg;
308
324
  if (unparsed == null) {
309
325
  unparsed = [];
310
326
  }
@@ -314,8 +330,46 @@ exports.Cmd = Cmd = (function() {
314
330
  if (!i.indexOf('-')) {
315
331
  optSeen = true;
316
332
  }
317
- if (!optSeen && /^\w[\w-_]*$/.test(i) && (cmd = this._cmdsByName[i])) {
318
- return cmd._parseCmd(argv, unparsed);
333
+ if (!optSeen && /^\w[\w-_]*$/.test(i)) {
334
+ cmd = this._cmdsByName[i];
335
+ if (!cmd && this._ext) {
336
+ if (typeof this._ext === 'string') {
337
+ if (~this._ext.indexOf('%s')) {
338
+ pkg = UTIL.format(this._ext, i);
339
+ } else {
340
+ pkg = this._ext + i;
341
+ }
342
+ } else if (this._ext === true) {
343
+ pkg = i;
344
+ c = this;
345
+ while (true) {
346
+ pkg = c._name + '-' + pkg;
347
+ if (c._cmd === c) {
348
+ break;
349
+ }
350
+ c = c._cmd;
351
+ }
352
+ }
353
+ try {
354
+ cmdDesc = require(pkg);
355
+ } catch (_error) {
356
+ e = _error;
357
+ }
358
+ if (cmdDesc) {
359
+ if (typeof cmdDesc === 'function') {
360
+ this.cmd().name(i).apply(cmdDesc).end();
361
+ } else if (typeof cmdDesc === 'object') {
362
+ this.cmd(cmdDesc);
363
+ cmdDesc.name(i);
364
+ } else {
365
+ throw new Error('Error: Unsupported command declaration type, ' + 'should be function or COA.Cmd() object');
366
+ }
367
+ cmd = this._cmdsByName[i];
368
+ }
369
+ }
370
+ if (cmd) {
371
+ return cmd._parseCmd(argv, unparsed);
372
+ }
319
373
  }
320
374
  unparsed.push(i);
321
375
  }
@@ -383,7 +437,7 @@ exports.Cmd = Cmd = (function() {
383
437
  };
384
438
 
385
439
  Cmd.prototype._processParams = function(params, desc) {
386
- var i, n, notExists, v, vals, _i, _j, _len, _len1;
440
+ var i, n, notExists, res, v, vals, _i, _j, _len, _len1;
387
441
  notExists = [];
388
442
  for (_i = 0, _len = desc.length; _i < _len; _i++) {
389
443
  i = desc[_i];
@@ -399,7 +453,9 @@ exports.Cmd = Cmd = (function() {
399
453
  }
400
454
  for (_j = 0, _len1 = vals.length; _j < _len1; _j++) {
401
455
  v = vals[_j];
402
- i._saveVal(params, v);
456
+ if (Q.isRejected(res = i._saveVal(params, v))) {
457
+ return res;
458
+ }
403
459
  }
404
460
  }
405
461
  return this._setDefaults(params, notExists);
@@ -454,10 +510,21 @@ exports.Cmd = Cmd = (function() {
454
510
  }
455
511
  };
456
512
  };
457
- Q.when(this._do(this._parseArr(argv)), cb(0), cb(1)).end();
513
+ Q.when(this["do"](argv), cb(0), cb(1)).done();
458
514
  return this;
459
515
  };
460
516
 
517
+ /**
518
+ Convenient function to run command from tests.
519
+ @param {Array} argv
520
+ @returns {Q.Promise}
521
+ */
522
+
523
+
524
+ Cmd.prototype["do"] = function(argv) {
525
+ return this._do(this._parseArr(argv || []));
526
+ };
527
+
461
528
  /**
462
529
  Invoke specified (or current) command using provided
463
530
  options and arguments.
@@ -493,16 +560,18 @@ exports.Cmd = Cmd = (function() {
493
560
  if (p.argv.length) {
494
561
  return _this.reject("Unknown command: " + cmds.join(' '));
495
562
  }
496
- return Q.fail(_this._do({
497
- cmd: p.cmd,
498
- opts: _this._processParams(opts, _this._opts),
499
- args: _this._processParams(args, _this._args)
500
- }), function(res) {
501
- if (res && res.exitCode === 0) {
502
- return res.toString();
503
- } else {
504
- return _this.reject(res);
505
- }
563
+ return Q.all([_this._processParams(opts, _this._opts), _this._processParams(args, _this._args)]).spread(function(opts, args) {
564
+ return _this._do({
565
+ cmd: p.cmd,
566
+ opts: opts,
567
+ args: args
568
+ }).fail(function(res) {
569
+ if (res && res.exitCode === 0) {
570
+ return res.toString();
571
+ } else {
572
+ return _this.reject(res);
573
+ }
574
+ });
506
575
  });
507
576
  });
508
577
  };
package/lib/color.js CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by CoffeeScript 1.3.3
1
+ // Generated by CoffeeScript 1.6.3
2
2
  var colors;
3
3
 
4
4
  colors = {
package/lib/completion.js CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by CoffeeScript 1.3.3
1
+ // Generated by CoffeeScript 1.6.3
2
2
  /**
3
3
  Most of the code adopted from the npm package shell completion code.
4
4
  See https://github.com/isaacs/npm/blob/master/lib/completion.js
@@ -21,7 +21,7 @@ module.exports = function() {
21
21
  e.errno = require('constants').ENOTSUP;
22
22
  return this.reject(e);
23
23
  }
24
- if (!(process.env.COMP_CWORD != null) || !(process.env.COMP_LINE != null) || !(process.env.COMP_POINT != null)) {
24
+ if ((process.env.COMP_CWORD == null) || (process.env.COMP_LINE == null) || (process.env.COMP_POINT == null)) {
25
25
  return dumpScript(this._cmd._name);
26
26
  }
27
27
  console.error('COMP_LINE: %s', process.env.COMP_LINE);
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // Generated by CoffeeScript 1.6.3
2
+ exports.Cmd = require('./cmd').Cmd;
3
+
4
+ exports.Opt = require('./cmd').Opt;
5
+
6
+ exports.Arg = require('./cmd').Arg;
7
+
8
+ exports.shell = require('./shell');
9
+
10
+ exports.require = require;
package/lib/opt.js CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by CoffeeScript 1.3.3
1
+ // Generated by CoffeeScript 1.6.3
2
2
  var Cmd, Color, Opt, Q, fs;
3
3
 
4
4
  fs = require('fs');
@@ -293,6 +293,9 @@ exports.Opt = Opt = (function() {
293
293
  }
294
294
  }
295
295
  res.push(' : ', this._title);
296
+ if (this._req) {
297
+ res.push(' ', Color('lred', '(required)'));
298
+ }
296
299
  return res.join('');
297
300
  };
298
301
 
@@ -320,6 +323,16 @@ exports.Opt = Opt = (function() {
320
323
 
321
324
  Opt.prototype.end = Cmd.prototype.end;
322
325
 
326
+ /**
327
+ Apply function with arguments in context of option instance.
328
+ @param {Function} fn
329
+ @param {Array} args
330
+ @returns {COA.Opt} this instance (for chainability)
331
+ */
332
+
333
+
334
+ Opt.prototype.apply = Cmd.prototype.apply;
335
+
323
336
  return Opt;
324
337
 
325
338
  })();
package/lib/shell.js CHANGED
@@ -1,5 +1,4 @@
1
- // Generated by CoffeeScript 1.3.3
2
-
1
+ // Generated by CoffeeScript 1.6.3
3
2
  exports.unescape = function(w) {
4
3
  w = w.charAt(0) === '"' ? w.replace(/^"|([^\\])"$/g, '$1') : w.replace(/\\ /g, ' ');
5
4
  return w.replace(/\\("|'|\$|`|\\)/g, '$1');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "coa",
3
3
  "description": "Command-Option-Argument: Yet another parser for command line options.",
4
- "version": "0.3.6",
4
+ "version": "0.4.0",
5
5
  "homepage": "http://github.com/veged/coa",
6
6
  "author": "Sergey Berezhnoy <veged@ya.ru> (http://github.com/veged)",
7
7
  "maintainers": [
@@ -18,12 +18,20 @@
18
18
  "directories": {
19
19
  "lib": "./lib"
20
20
  },
21
- "main": "./lib/coa.js",
22
21
  "dependencies": {
23
- "q": "~0.8.8"
22
+ "q": "~0.9.6"
24
23
  },
25
24
  "devDependencies": {
26
- "vows": "~0.6.4"
25
+ "coffee-script": "~1.6.3",
26
+ "istanbul": "~0.1.40",
27
+ "mocha-as-promised": "*",
28
+ "mocha-istanbul": "*",
29
+ "mocha": "~1.12.0",
30
+ "chai": "~1.7.2"
31
+ },
32
+ "scripts": {
33
+ "test": "make test",
34
+ "coverage": "make coverage"
27
35
  },
28
36
  "engines": {
29
37
  "node": ">= 0.6.0"
package/src/arg.coffee CHANGED
@@ -95,7 +95,13 @@ exports.Arg = class Arg
95
95
 
96
96
  _checkParsed: (opts, args) -> not args.hasOwnProperty(@_name)
97
97
 
98
- _usage: -> Color('lpurple', @_name.toUpperCase()) + ' : ' + @_title
98
+ _usage: ->
99
+ res = []
100
+
101
+ res.push Color('lpurple', @_name.toUpperCase()), ' : ', @_title
102
+ if @_req then res.push ' ', Color('lred', '(required)')
103
+
104
+ res.join ''
99
105
 
100
106
  _requiredText: -> 'Missing required argument:\n ' + @_usage()
101
107
 
@@ -114,3 +120,11 @@ exports.Arg = class Arg
114
120
  @returns {COA.Cmd} parent command
115
121
  ###
116
122
  end: Cmd::end
123
+
124
+ ###*
125
+ Apply function with arguments in context of arg instance.
126
+ @param {Function} fn
127
+ @param {Array} args
128
+ @returns {COA.Arg} this instance (for chainability)
129
+ ###
130
+ apply: Cmd::apply
package/src/cmd.coffee CHANGED
@@ -32,6 +32,8 @@ exports.Cmd = class Cmd
32
32
 
33
33
  @_args = []
34
34
 
35
+ @_ext = false
36
+
35
37
  @get: (propertyName, func) ->
36
38
  Object.defineProperty @::, propertyName,
37
39
  configurable: true
@@ -52,8 +54,10 @@ exports.Cmd = class Cmd
52
54
  @_api
53
55
 
54
56
  _parent: (cmd) ->
55
- if cmd then cmd._cmds.push @
56
57
  @_cmd = cmd or this
58
+ if cmd
59
+ cmd._cmds.push @
60
+ if @_name then @_cmd._cmdsByName[@_name] = @
57
61
  @
58
62
 
59
63
  ###*
@@ -163,6 +167,15 @@ exports.Cmd = class Cmd
163
167
  .apply(require './completion')
164
168
  .end()
165
169
 
170
+ ###*
171
+ Allow command to be extendable by external node.js modules.
172
+ @param {String} [pattern] Pattern of node.js module to find subcommands at.
173
+ @returns {COA.Cmd} this instance (for chainability)
174
+ ###
175
+ extendable: (pattern) ->
176
+ @_ext = pattern or true
177
+ @
178
+
166
179
  _exit: (msg, code) ->
167
180
  process.once 'exit', ->
168
181
  if msg then UTIL.error msg
@@ -233,8 +246,49 @@ exports.Cmd = class Cmd
233
246
  while i = argv.shift()
234
247
  if not i.indexOf '-'
235
248
  optSeen = true
236
- if not optSeen and /^\w[\w-_]*$/.test(i) and cmd = @_cmdsByName[i]
237
- return cmd._parseCmd argv, unparsed
249
+ if not optSeen and /^\w[\w-_]*$/.test(i)
250
+ cmd = @_cmdsByName[i]
251
+
252
+ if not cmd and @_ext
253
+ # construct package name to require
254
+ if typeof @_ext is 'string'
255
+ if ~@_ext.indexOf('%s')
256
+ # use formatted string
257
+ pkg = UTIL.format(@_ext, i)
258
+ else
259
+ # just append subcommand name to the prefix
260
+ pkg = @_ext + i
261
+ else if @_ext is true
262
+ # use default scheme: <command>-<subcommand>-<subcommand> and so on
263
+ pkg = i
264
+ c = @
265
+ loop
266
+ pkg = c._name + '-' + pkg
267
+ if c._cmd is c then break
268
+ c = c._cmd
269
+
270
+ try
271
+ cmdDesc = require(pkg)
272
+ catch e
273
+
274
+ if cmdDesc
275
+ if typeof cmdDesc == 'function'
276
+ # set create subcommand, set its name and apply imported function
277
+ @cmd()
278
+ .name(i)
279
+ .apply(cmdDesc)
280
+ .end()
281
+ else if typeof cmdDesc == 'object'
282
+ # register subcommand
283
+ @cmd(cmdDesc)
284
+ # set command name
285
+ cmdDesc.name(i)
286
+ else
287
+ throw new Error 'Error: Unsupported command declaration type, ' +
288
+ 'should be function or COA.Cmd() object'
289
+ cmd = @_cmdsByName[i]
290
+ if cmd
291
+ return cmd._parseCmd argv, unparsed
238
292
 
239
293
  unparsed.push i
240
294
 
@@ -305,7 +359,8 @@ exports.Cmd = class Cmd
305
359
  vals = [vals]
306
360
 
307
361
  for v in vals
308
- i._saveVal(params, v)
362
+ if Q.isRejected(res = i._saveVal(params, v))
363
+ return res
309
364
 
310
365
  # set defaults
311
366
  @_setDefaults params, notExists
@@ -341,9 +396,17 @@ exports.Cmd = class Cmd
341
396
  @_exit res.stack ? res.toString(), res.exitCode ? code
342
397
  else
343
398
  @_exit()
344
- Q.when(@_do(@_parseArr argv), cb(0), cb(1)).end()
399
+ Q.when(@do(argv), cb(0), cb(1)).done()
345
400
  @
346
401
 
402
+ ###*
403
+ Convenient function to run command from tests.
404
+ @param {Array} argv
405
+ @returns {Q.Promise}
406
+ ###
407
+ do: (argv) ->
408
+ @_do(@_parseArr argv || [])
409
+
347
410
  ###*
348
411
  Invoke specified (or current) command using provided
349
412
  options and arguments.
@@ -366,19 +429,15 @@ exports.Cmd = class Cmd
366
429
  if p.argv.length
367
430
  return @reject "Unknown command: " + cmds.join ' '
368
431
 
369
- # catch fails from .only() options
370
- Q.fail(
371
- @_do({
372
- cmd: p.cmd,
373
- opts: @_processParams(opts, @_opts),
374
- args: @_processParams(args, @_args)
375
- }),
376
- (res) =>
377
- if res and res.exitCode is 0
378
- res.toString()
379
- else
380
- @reject(res)
381
- )
432
+ Q.all([@_processParams(opts, @_opts), @_processParams(args, @_args)])
433
+ .spread (opts, args) =>
434
+ @_do({ cmd: p.cmd, opts: opts, args: args })
435
+ # catch fails from .only() options
436
+ .fail (res) =>
437
+ if res and res.exitCode is 0
438
+ res.toString()
439
+ else
440
+ @reject(res)
382
441
 
383
442
  ###*
384
443
  Return reject of actions results promise with error code.
@@ -0,0 +1,5 @@
1
+ exports.Cmd = require('./cmd').Cmd
2
+ exports.Opt = require('./cmd').Opt
3
+ exports.Arg = require('./cmd').Arg
4
+ exports.shell = require('./shell')
5
+ exports.require = require;
package/src/opt.coffee CHANGED
@@ -212,6 +212,8 @@ exports.Opt = class Opt
212
212
 
213
213
  res.push ' : ', @_title
214
214
 
215
+ if @_req then res.push ' ', Color('lred', '(required)')
216
+
215
217
  res.join ''
216
218
 
217
219
  _requiredText: -> 'Missing required option:\n ' + @_usage()
@@ -231,3 +233,11 @@ exports.Opt = class Opt
231
233
  @returns {COA.Cmd} parent command
232
234
  ###
233
235
  end: Cmd::end
236
+
237
+ ###*
238
+ Apply function with arguments in context of option instance.
239
+ @param {Function} fn
240
+ @param {Array} args
241
+ @returns {COA.Opt} this instance (for chainability)
242
+ ###
243
+ apply: Cmd::apply