/**
* Parse a command line options & environment variables and create a configuration object.
*
var
cmdLineOpt = require('cmd/options'),
argv = require('cmd/argv');
var opts = cmdLineOpt.describe('cmd/generateDDL', 'Check database structure for application domain. Generate DDL (both create and alter) if need and optionally run it')
.add(argv.establishConnectionFromCmdLineAttributes._cmdLineParams)
.add({short: 'm', long: 'models', param: 'modelsList', defaultValue: '*', help: 'Comma separated model names for DDL generation. If -e specified this options is ignored'})
.add({short: 'e', long: 'entities', param: 'entitiesList', defaultValue: '*', help: 'Comma separated entity names list for DDL generation'})
.add({short: 'out', long: 'out', param: 'outputPath', defaultValue: process.cwd(), help: 'Folder to output generated DDLs (one file per connection)'})
.add({short: 'autorun', long: 'autorun', defaultValue: false, help: 'execute DDL statement after generation. BE CAREFUL! DO NOT USE ON PRODUCTION'});
options = opts.parseVerbose({}, true);
*
* @author pavel.mash
* @module cmd/options
*/
//[{short: 'u', long: 'user', param: 'userName', defaultValue: true, searchInEnv: true, help: 'A user name for server connection'}]
/**
* @class
* @param commandName
* @param commandDescription
* @constructor
*/
function Options(commandName, commandDescription){
this.commandName = commandName || '';
this.commandDescription = commandDescription || '';
this.options = [];
}
/**
* @typedef {Object} Option
* @property {String} short - a short option name
* @property {String} long - a long option name. This name are used in `parse` result
* @property {String} [param] - if parameter has a value - help string for a parameter name `-short param`
* @property {*} [defaultValue] - a default value for a property. For a string properties what allow empty value set it to `*`
* @property {Boolean} [searchInEnv=false] - if property do not passed as a cmd line switch then
* perform search of `UB_`+long.toUpperCase() in environment variables
* @property {String} help - a help string for a `usage()` call
*/
/**
* Add a option(s) definition.
* @param {Option|Array.<Option>} otherOptions
* @return {Options}
*/
Options.prototype.add = function add(otherOptions){
if (Array.isArray(otherOptions)){
this.options = this.options.concat(otherOptions);
} else {
this.options.push(otherOptions);
}
return this;
};
/**
*
* Parse a command line & env variables for a options and create a configuration object.
* Return `undefined` in case options is not valid or a object with keys - options.long & values
* @param {Object} [defaults] Override for a command line attributes. If any - this one will be used
* @param {Array} [errors] If passed will bw filled by a errors in passed parameters
*/
Options.prototype.parse = function parse(defaults, errors){
var result = _.defaults({}, defaults);
//[{short: 'u', long: 'user', param: 'userName', defaultValue: true, searchInEnv: true, help: 'A user name for server connection'}]
var valid = true;
this.options.forEach(function(option){
var val, t;
if (!result.hasOwnProperty(option.long)) { // not passed in defaults
if (option.param) { // option with parameter `-http register`
val = switchValue(option.short);
if (typeof val === 'undefined') {
val = switchValue(option.long);
}
if ((typeof val === 'undefined') && option.searchInEnv) {
val = process.env['UB_' + option.long.toUpperCase()]
}
} else { // boolean option without parameter `-createDB`
if (switchIndex(option.short) !== -1) {
val = true
} else if (switchIndex(option.long) !== -1) {
val = true
} else if (option.searchInEnv) {
t = process.env['UB_' + option.long.toUpperCase()];
if (typeof t !== 'undefined') {
val = (t === 'true') || (t === 'TRUE');
}
}
}
if ((typeof val === 'undefined')) {
val = option.defaultValue;
}
if ((typeof val === 'undefined')) {
valid = false;
if (errors){
errors.push('expected parameter "' + option.long + '" not found');
}
} else {
result[option.long] = (val === '*' ? '' : val);
}
}
});
return valid ? result : undefined;
};
/**
* In case `-help` or '-?' command line switch found or passed options not match a options set
* will output a usage help to console and return `undefined`, else - return a parsed options
*
* @param {Object} [defaults] - Override passed parameter values by this one
* @param {Boolean} [outputParsed=false] output a parsed parameters to a log
*/
Options.prototype.parseVerbose = function parseVerbose(defaults, outputParsed){
var result, errors = [];
if (switchIndex('?') !==-1 || switchIndex('help') !==-1) {
console.log(this.usage());
} else {
result = this.parse(defaults, errors);
if (!result) console.log(this.usage());
if (errors.length) {
console.error('\nInvalid usage');
console.error('\t' + errors.join('\r\t'));
}
if (outputParsed) {
console.info('Run a command "%s" using %j', this.commandName, result);
}
}
return result;
};
Options.prototype.howParamsAppearInCommandLine = function(){
var res = [], elm;
this.options.forEach(function(option){
elm = '-' + option.short + (option.param ? ' ' + option.param : '');
if (option.defaultValue) elm = '[' + elm + ']';
res.push(elm);
});
return res.join(' ');
};
/**
* Output a usage info to console
*/
Options.prototype.usage = function usage(){
if (this.commandDescription){
console.info('\n' + this.commandDescription);
}
console.info('\nUsage: ub %s %s', this.commandName, this.howParamsAppearInCommandLine());
console.info('\nwhere');
var envs = [];
var res = [], elm;
// create a parameters description
this.options.forEach(function(option){
elm = '-' + option.short;
if (option.short !== option.long) elm += ' | ' + option.long;
if (option.searchInEnv) elm += '*';
elm += '\t' + option.help;
if (typeof option.defaultValue !== 'undefined') elm += ', default: ' + option.defaultValue;
res.push(elm);
if (option.searchInEnv) {
envs.push('UB_' + option.long.toUpperCase());
}
});
res = '\t' + res.join('\n\t');
if (envs.length){
res += '\n\n* will lookup a environment variable in case switch omitted: ' + JSON.stringify(envs);
}
return res;
};
/**
* Create a new options definition.
* @example
var cmdLineOpt = require('cmd/options');
var opts = cmdLineOpt.describe('cmd/createStore',
'Create internal store structure (folders) for specifies FileSystem store'
)
.add({short: 'cfg', long: 'cfg', param: 'serverConfig', defaultValue: 'ubConfig.json', help: 'Server config'})
.add({short: 'store', long: 'store', param: 'storesList', defaultValue: '*', help: 'Comma separated blob stores list'});
options = opts.parseVerbose({}, true);
* @param {String} commandName Name of a command then executed from a command line
* @param {String} commandDescription Command description for help (-help switch)
* @return {Options}
*/
exports.describe = function describe(commandName, commandDescription){
return new Options(commandName || '', commandDescription || '');
};
/**
* Determines whether a switchName was passed as a command-line argument to the application
* Switch may be specified in the following ways on the command line:
* -switchName
* or
* /switchName
* @param switchName
* @returns {Number} switch index if found or -1 otherwise
*/
exports.switchIndex = function switchIndex(switchName){
var
res = process.argv.indexOf('-' + switchName);
return (res === -1) ? process.argv.indexOf('/' + switchName) : res;
};
var switchIndex = exports.switchIndex;
/**
* Determines whether a switchName was passed as a command-line argument to the application and have VALUE
* Switch values may be specified in the following ways on the command line:
* -switchName Value
* or
* /switchName Value
* @param switchName
* @returns {String} switch value or `undefined` in case switch not found or switch not have value
*/
exports.switchValue = function switchValue(switchName){
var
idx = switchIndex(switchName) + 1,
val;
return (idx && (val = process.argv[idx]) && val.charAt !== '-' && val.charAt !== '/') ? process.argv[idx]: undefined;
};
var switchValue = exports.switchValue;