coa 0.3.7 → 0.4.1

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
- ./node_modules/.bin/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
 
@@ -8,6 +9,7 @@ Once you write definition in terms of commands, options and arguments you automa
8
9
  * Command line help text
9
10
  * Program API for use COA-based programs as modules
10
11
  * Shell completion
12
+ * Subcommand extendibility by external node modules
11
13
 
12
14
  ### Other features
13
15
 
@@ -18,6 +20,8 @@ Once you write definition in terms of commands, options and arguments you automa
18
20
 
19
21
  ### TODO
20
22
 
23
+ * --Subcommand extendibility--
24
+ * Shell completion helpers
21
25
  * Localization
22
26
  * Shell-mode
23
27
  * Configs
@@ -43,14 +47,22 @@ require('coa').Cmd() // main (top level) command declaration
43
47
  .version;
44
48
  })
45
49
  .end() // end option chain and return to main command
46
- .cmd().name('subcommand').apply(require('./subcommand').COA).end() // load subcommand from module
50
+ .cmd()
51
+ .name('subcommand')
52
+ .apply(require('./subcommand')) // load subcommand from module
53
+ .end()
47
54
  .cmd() // inplace subcommand declaration
48
- .name('othercommand').title('Awesome other subcommand').helpful()
55
+ .name('othercommand')
56
+ .title('Awesome other subcommand')
57
+ .helpful()
49
58
  .opt()
50
- .name('input').title('input file, required')
51
- .short('i').long('input')
59
+ .name('input')
60
+ .title('input file, required')
61
+ .short('i')
62
+ .long('input')
52
63
  .val(function(v) { // validator function, also for translate simple values
53
- return require('fs').createReadStream(v) })
64
+ return require('fs').createReadStream(v);
65
+ })
54
66
  .req() // make option required
55
67
  .end() // end option chain and return to command
56
68
  .end() // end subcommand chain and return to parent command
@@ -59,12 +71,14 @@ require('coa').Cmd() // main (top level) command declaration
59
71
 
60
72
  ````javascript
61
73
  // subcommand.js
62
- exports.COA = function() {
74
+ module.exports = function() {
63
75
  this
64
76
  .title('Awesome subcommand').helpful()
65
77
  .opt()
66
- .name('output').title('output file')
67
- .short('o').long('output')
78
+ .name('output')
79
+ .title('output file')
80
+ .short('o')
81
+ .long('output')
68
82
  .output() // use default preset for "output" option declaration
69
83
  .end()
70
84
  };
@@ -139,6 +153,16 @@ Adds shell completion to command, adds "completion" subcommand, that makes all t
139
153
  Must be called only on root command.<br>
140
154
  **@returns** *COA.Cmd* `this` instance (for chainability)
141
155
 
156
+ #### Cmd.extendable
157
+ Adds ability to extend command by external node modules.<br>
158
+ **@param** *String* `[pattern]` Pattern of modules names to search.
159
+ Should be simple string with `%s` placeholder like `coa-program-%s-subcommand`
160
+ or without it — it will be treated as module name prefix then. E.g. `coa-program-`.<br>
161
+ Node module should export function or `Cmd` object. Function will be passed
162
+ to `Cmd.apply()` method of created subcommand object using `Cmd.cmd()` method. And
163
+ `Cmd` object will be passed to `Cmd.cmd()` method.
164
+ **@returns** *COA.Cmd* `this` instance (for chainability)
165
+
142
166
  #### Cmd.usage
143
167
  Build full usage text for current command instance.<br>
144
168
  **@returns** *String* `usage` text
@@ -226,6 +250,11 @@ Default value passed through validation function as ordinary value.<br>
226
250
  **@param** *Object* `_def`<br>
227
251
  **@returns** *COA.Opt* `this` instance (for chainability)
228
252
 
253
+ #### Opt.input
254
+ Make option value inputting stream.
255
+ It's add useful validation and shortcut for STDIN.
256
+ **@returns** *{COA.Opt}* `this` instance (for chainability)
257
+
229
258
  #### Opt.output
230
259
  Make option value outputing stream.<br>
231
260
  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;
@@ -160,6 +160,16 @@ exports.Arg = Arg = (function() {
160
160
 
161
161
  Arg.prototype.end = Cmd.prototype.end;
162
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
+
163
173
  return Arg;
164
174
 
165
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
  }
@@ -456,10 +510,21 @@ exports.Cmd = Cmd = (function() {
456
510
  }
457
511
  };
458
512
  };
459
- Q.when(this._do(this._parseArr(argv)), cb(0), cb(1)).end();
513
+ Q.when(this["do"](argv), cb(0), cb(1)).done();
460
514
  return this;
461
515
  };
462
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
+
463
528
  /**
464
529
  Invoke specified (or current) command using provided
465
530
  options and arguments.
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);
@@ -112,15 +112,15 @@ complete = function(cmd, opts) {
112
112
  }
113
113
  if (optWord && (opt = cmd._optsByKey[optWord])) {
114
114
  if (!opt._flag && opt._comp) {
115
- compls = Q.join(compls, Q.when(opt._comp(opts), function(c, o) {
115
+ compls = Q.all([compls, opt._comp(opts)]).spread(function(c, o) {
116
116
  return c.concat(o.map(function(v) {
117
117
  return (optPrefix || '') + v;
118
118
  }));
119
- }));
119
+ });
120
120
  }
121
121
  }
122
122
  if (cmd._comp) {
123
- compls = Q.join(compls, Q.when(cmd._comp(opts)), function(c, o) {
123
+ compls = Q.all([compls, cmd._comp(opts)]).spread(function(c, o) {
124
124
  return c.concat(o);
125
125
  });
126
126
  }
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');
@@ -323,6 +323,16 @@ exports.Opt = Opt = (function() {
323
323
 
324
324
  Opt.prototype.end = Cmd.prototype.end;
325
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
+
326
336
  return Opt;
327
337
 
328
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.7",
4
+ "version": "0.4.1",
5
5
  "homepage": "http://github.com/veged/coa",
6
6
  "author": "Sergey Berezhnoy <veged@ya.ru> (http://github.com/veged)",
7
7
  "maintainers": [
@@ -18,13 +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
- "coffee-script": "~1.3.3",
27
- "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"
28
35
  },
29
36
  "engines": {
30
37
  "node": ">= 0.6.0"
package/src/arg.coffee CHANGED
@@ -120,3 +120,11 @@ exports.Arg = class Arg
120
120
  @returns {COA.Cmd} parent command
121
121
  ###
122
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
 
@@ -342,9 +396,17 @@ exports.Cmd = class Cmd
342
396
  @_exit res.stack ? res.toString(), res.exitCode ? code
343
397
  else
344
398
  @_exit()
345
- Q.when(@_do(@_parseArr argv), cb(0), cb(1)).end()
399
+ Q.when(@do(argv), cb(0), cb(1)).done()
346
400
  @
347
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
+
348
410
  ###*
349
411
  Invoke specified (or current) command using provided
350
412
  options and arguments.
@@ -137,15 +137,17 @@ complete = (cmd, opts) ->
137
137
  # complete on opt values: completion
138
138
  if optWord and opt = cmd._optsByKey[optWord]
139
139
  if not opt._flag and opt._comp
140
- compls = Q.join compls, Q.when opt._comp(opts), (c, o) ->
141
- c.concat o.map (v) -> (optPrefix or '') + v
140
+ compls = Q.all([compls, opt._comp(opts)])
141
+ .spread (c, o) ->
142
+ c.concat o.map (v) -> (optPrefix or '') + v
142
143
 
143
144
  # TODO: complete on args values (context aware, custom completion?)
144
145
 
145
146
  # custom completion on cmds
146
147
  if cmd._comp
147
- compls = Q.join compls, Q.when(cmd._comp(opts)), (c, o) ->
148
- c.concat o
148
+ compls = Q.all([compls, cmd._comp(opts)])
149
+ .spread (c, o) ->
150
+ c.concat o
149
151
 
150
152
  # TODO: context aware custom completion on cmds, opts and args
151
153
  # (can depend on already entered values, especially options)
@@ -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
@@ -233,3 +233,11 @@ exports.Opt = class Opt
233
233
  @returns {COA.Cmd} parent command
234
234
  ###
235
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