coa 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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