improve parsing of --query parameter

The argument parser now converts the query string to an object, and it
casts booleans and numbers in the query string to the correct
JavaScript types.
This commit is contained in:
Jeff Williams 2012-11-06 20:24:48 -08:00
parent 37a7fd0f12
commit b0536de3ff
3 changed files with 107 additions and 25 deletions

View File

@ -319,17 +319,13 @@ function main() {
}
}
if (env.opts.query) {
env.opts.query = require('querystring').parse(env.opts.query);
}
// which version of javascript will be supported? (rhino only)
if (typeof version === 'function') {
version(env.conf.jsVersion || 180);
}
if (env.opts.help) {
console.log( jsdoc.opts.parser.help() );
console.log( jsdoc.opts.args.help() );
process.exit(0);
} else if (env.opts.test) {
include('test/runner.js');

View File

@ -7,22 +7,80 @@
var ArgParser = require('jsdoc/opts/argparser'),
argParser = new ArgParser(),
hasOwnProp = Object.prototype.hasOwnProperty,
ourOptions,
querystring = require('querystring'),
util = require('util'),
defaults = {
destination: './out/'
};
// cast strings to booleans or integers where appropriate
function castTypes(item) {
var result = item;
switch (result) {
case 'true':
return true;
case 'false':
return false;
default:
// might be an integer
var integer = parseInt(result, 10);
if (String(integer) === result && integer !== 'NaN') {
return integer;
} else {
return result;
}
}
}
// check for strings that we need to cast to other types
function fixTypes(item) {
var result = item;
// recursively process arrays and objects
if ( util.isArray(result) ) {
for (var i = 0, l = result.length; i < l; i++) {
result[i] = fixTypes(result[i]);
}
} else if (typeof result === 'object') {
for (var prop in result) {
if ( hasOwnProp.call(result, prop) ) {
result[prop] = fixTypes(result[prop]);
}
}
} else {
result = castTypes(result);
}
return result;
}
function parseQuery(str) {
var result = querystring.parse(str);
for (var prop in result) {
if ( hasOwnProp.call(result, prop) ) {
result[prop] = fixTypes(result[prop]);
}
}
return result;
}
argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template');
argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc env.dirname + /conf.json');
argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: utf-8');
argParser.addOption('T', 'test', false, 'Run all tests and quit.');
argParser.addOption('d', 'destination', true, 'The path to the output folder. Use "console" to dump data to the console. Default: console');
argParser.addOption('p', 'private', false, 'Display symbols marked with the @private tag. Default: false.');
argParser.addOption('p', 'private', false, 'Display symbols marked with the @private tag. Default: false');
argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.');
argParser.addOption('l', 'lenient', false, 'Continue to generate output if a doclet is incomplete or contains errors. Default: false.');
argParser.addOption('l', 'lenient', false, 'Continue to generate output if a doclet is incomplete or contains errors. Default: false');
argParser.addOption('h', 'help', false, 'Print this message and quit.');
argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.');
argParser.addOption('q', 'query', true, 'Provide a querystring to define custom variable names/values to add to the options hash.');
argParser.addOption('q', 'query', true, 'A query string to parse and store in env.opts.query. Example: foo=bar&baz=true', false, parseQuery);
argParser.addOption('u', 'tutorials', true, 'Directory in which JSDoc should search for tutorials.');
//TODO [-R, recurseonly] = a number representing the depth to recurse

View File

@ -1,6 +1,7 @@
/*global describe: true, expect: true, it: true */
describe("jsdoc/opts/args", function() {
var args = require('jsdoc/opts/args');
var querystring = require('querystring');
it("should exist", function() {
expect(args).toBeDefined();
@ -121,6 +122,20 @@ describe("jsdoc/opts/args", function() {
expect(r.recurse).toEqual(true);
});
it("should accept a '-l' option and return an object with a 'lenient' property", function() {
args.parse(['-l']);
var r = args.get();
expect(r.lenient).toEqual(true);
});
it("should accept a '--lenient' option and return an object with a 'lenient' property", function() {
args.parse(['--lenient']);
var r = args.get();
expect(r.lenient).toEqual(true);
});
it("should accept a '-h' option and return an object with a 'help' property", function() {
args.parse(['-h']);
var r = args.get();
@ -153,14 +168,27 @@ describe("jsdoc/opts/args", function() {
args.parse(['-q', 'foo=bar&fab=baz']);
var r = args.get();
expect(r.query).toEqual('foo=bar&fab=baz');
expect(r.query).toEqual({ foo: 'bar', fab: 'baz' });
});
it("should accept a '--query' option and return an object with a 'query' property", function() {
args.parse(['--query', 'foo=bar&fab=baz']);
var r = args.get();
expect(r.query).toEqual('foo=bar&fab=baz');
expect(r.query).toEqual({ foo: 'bar', fab: 'baz' });
});
it("should use type coercion on the 'query' property so it has real booleans and numbers", function() {
var obj = {
foo: 'fab',
bar: true,
baz: false,
qux: [1, -97]
};
args.parse(['-q', querystring.stringify(obj)]);
var r = args.get();
expect(r.query).toEqual(obj);
});
it("should accept a '-t' option and return an object with a 'tutorials' property", function() {
@ -177,13 +205,6 @@ describe("jsdoc/opts/args", function() {
expect(r.tutorials).toEqual('mytutorials');
});
it("should accept a naked option (i.e. no '-') and return an object with a '_' property", function() {
args.parse(['myfile1', 'myfile2']);
var r = args.get();
expect(r._).toEqual(['myfile1', 'myfile2']);
});
it("should accept a '--verbose' option and return an object with a 'verbose' property", function() {
args.parse(['--verbose']);
var r = args.get();
@ -191,13 +212,6 @@ describe("jsdoc/opts/args", function() {
expect(r.verbose).toEqual(true);
});
it("should accept a '--nocolor' option and return an object with a 'nocolor' property", function() {
args.parse(['--nocolor']);
var r = args.get();
expect(r.nocolor).toEqual(true);
});
it("should accept a '--match' option and return an object with a 'match' property", function() {
args.parse(['--match', '.*tag']);
var r = args.get();
@ -205,13 +219,27 @@ describe("jsdoc/opts/args", function() {
expect(r.match).toEqual('.*tag');
});
it("should accept a multiple '--match' options and return an object with a 'match' property", function() {
it("should accept multiple '--match' options and return an object with a 'match' property", function() {
args.parse(['--match', '.*tag', '--match', 'parser']);
var r = args.get();
expect(r.match).toEqual(['.*tag', 'parser']);
});
it("should accept a '--nocolor' option and return an object with a 'nocolor' property", function() {
args.parse(['--nocolor']);
var r = args.get();
expect(r.nocolor).toEqual(true);
});
it("should accept a naked option (i.e. no '-') and return an object with a '_' property", function() {
args.parse(['myfile1', 'myfile2']);
var r = args.get();
expect(r._).toEqual(['myfile1', 'myfile2']);
});
//TODO: tests for args that must have values
});
});