From 1bc78edf71983bc689815e1b31b37cc06f72ce2e Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Thu, 13 Aug 2015 08:11:58 -0400 Subject: [PATCH] Refactor help module Main changes: - Use the `columnify` module to display the commands instead of using manual parsing. - Extract logic to create a string representation from an option signature to Capitano, and reuse here. See https://github.com/resin-io/capitano/pull/28 Some bugs were caught and fixes during the refactoring: - In command help, if the command didn't exist, we reused default Capitanos command not found function which uses `process.exit(1)`. This was changed to pass a custom error to `done()`, so the command fails correctly when using programatically. - General help didn't call `done()` at all, thus causing problems if using the command programatically someday. --- build/actions/help.js | 136 +++++++++------------------------------- lib/actions/help.coffee | 112 ++++++++++----------------------- package.json | 3 +- 3 files changed, 65 insertions(+), 186 deletions(-) diff --git a/build/actions/help.js b/build/actions/help.js index 99ffbad0..db720511 100644 --- a/build/actions/help.js +++ b/build/actions/help.js @@ -1,5 +1,5 @@ (function() { - var PADDING_INITIAL, PADDING_MIDDLE, _, addAlias, addOptionPrefix, buildHelpString, buildOptionSignatureHelp, capitano, command, general, getCommandHelp, getFieldMaxLength, getOptionHelp, getOptionsParsedSignatures; + var _, capitano, columnify, command, general, indent, parse, print; _ = require('lodash'); @@ -7,119 +7,56 @@ capitano = require('capitano'); - PADDING_INITIAL = ' '; + columnify = require('columnify'); - PADDING_MIDDLE = '\t'; - - getFieldMaxLength = function(array, field) { - return _.max(_.map(array, function(item) { - return item[field].toString().length; - })); - }; - - buildHelpString = function(firstColumn, secondColumn) { - var result; - result = "" + PADDING_INITIAL + firstColumn; - result += "" + PADDING_MIDDLE + secondColumn; - return result; - }; - - addOptionPrefix = function(option) { - if (option.length <= 0) { - return; - } - if (option.length === 1) { - return "-" + option; - } else { - return "--" + option; - } - }; - - addAlias = function(alias) { - return ", " + (addOptionPrefix(alias)); - }; - - buildOptionSignatureHelp = function(option) { - var alias, i, len, ref, result; - result = addOptionPrefix(option.signature.toString()); - if (_.isString(option.alias)) { - result += addAlias(option.alias); - } else if (_.isArray(option.alias)) { - ref = option.alias; - for (i = 0, len = ref.length; i < len; i++) { - alias = ref[i]; - result += addAlias(alias); + parse = function(object) { + return _.object(_.map(object, function(item) { + var signature; + if (item.alias != null) { + signature = item.toString(); + } else { + signature = item.signature.toString(); } - } - if (option.parameter != null) { - result += " <" + option.parameter + ">"; - } - return result; - }; - - getCommandHelp = function(command) { - var commandSignature, maxSignatureLength; - maxSignatureLength = getFieldMaxLength(capitano.state.commands, 'signature'); - commandSignature = _.str.rpad(command.signature.toString(), maxSignatureLength, ' '); - return buildHelpString(commandSignature, command.description); - }; - - getOptionsParsedSignatures = function(optionsHelp) { - var maxLength; - maxLength = _.max(_.map(optionsHelp, function(signature) { - return signature.length; + return [signature, item.description]; })); - return _.map(optionsHelp, function(signature) { - return _.str.rpad(signature, maxLength, ' '); + }; + + indent = function(text) { + text = _.map(_.str.lines(text), function(line) { + return ' ' + line; }); + return text.join('\n'); }; - getOptionHelp = function(option, maxLength) { - var result; - result = PADDING_INITIAL; - result += _.str.rpad(option.signature, maxLength, ' '); - result += PADDING_MIDDLE; - result += option.description; - return result; + print = function(data) { + return console.log(indent(columnify(data, { + showHeaders: false, + minWidth: 35 + }))); }; - general = function() { - var command, i, j, len, len1, option, optionSignatureMaxLength, options, ref; + general = function(params, options, done) { + var commands; console.log('Usage: resin [COMMAND] [OPTIONS]\n'); console.log('Commands:\n'); - ref = capitano.state.commands; - for (i = 0, len = ref.length; i < len; i++) { - command = ref[i]; - if (command.isWildcard()) { - continue; - } - console.log(getCommandHelp(command)); - } + commands = _.reject(capitano.state.commands, function(command) { + return command.isWildcard(); + }); + print(parse(commands)); if (!_.isEmpty(capitano.state.globalOptions)) { console.log('\nGlobal Options:\n'); + print(parse(capitano.state.globalOptions)); } - options = _.map(capitano.state.globalOptions, function(option) { - option.signature = buildOptionSignatureHelp(option); - return option; - }); - optionSignatureMaxLength = _.max(_.map(options, function(option) { - return option.signature.length; - })); - for (j = 0, len1 = options.length; j < len1; j++) { - option = options[j]; - console.log(getOptionHelp(option, optionSignatureMaxLength)); - } - return console.log(); + return done(); }; command = function(params, options, done) { return capitano.state.getMatchCommand(params.command, function(error, command) { - var i, len, option, optionSignatureMaxLength; if (error != null) { return done(error); } if ((command == null) || command.isWildcard()) { - return capitano.defaults.actions.commandNotFound(params.command); + return done(new Error("Command not found: " + params.command)); } console.log("Usage: " + command.signature); if (command.help != null) { @@ -129,18 +66,7 @@ } if (!_.isEmpty(command.options)) { console.log('\nOptions:\n'); - options = _.map(command.options, function(option) { - option.signature = buildOptionSignatureHelp(option); - return option; - }); - optionSignatureMaxLength = _.max(_.map(options, function(option) { - return option.signature.toString().length; - })); - for (i = 0, len = options.length; i < len; i++) { - option = options[i]; - console.log(getOptionHelp(option, optionSignatureMaxLength)); - } - console.log(); + print(parse(command.options)); } return done(); }); diff --git a/lib/actions/help.coffee b/lib/actions/help.coffee index 7c986249..55476d33 100644 --- a/lib/actions/help.coffee +++ b/lib/actions/help.coffee @@ -1,93 +1,56 @@ _ = require('lodash') _.str = require('underscore.string') capitano = require('capitano') +columnify = require('columnify') -# TODO: Refactor this terrible mess +parse = (object) -> + return _.object _.map object, (item) -> -PADDING_INITIAL = ' ' -PADDING_MIDDLE = '\t' + # Hacky way to determine if an object is + # a function or a command + if item.alias? + signature = item.toString() + else + signature = item.signature.toString() -getFieldMaxLength = (array, field) -> - return _.max _.map array, (item) -> - return item[field].toString().length + return [ + signature + item.description + ] -buildHelpString = (firstColumn, secondColumn) -> - result = "#{PADDING_INITIAL}#{firstColumn}" - result += "#{PADDING_MIDDLE}#{secondColumn}" - return result +indent = (text) -> + text = _.map _.str.lines(text), (line) -> + return ' ' + line + return text.join('\n') -addOptionPrefix = (option) -> - return if option.length <= 0 - if option.length is 1 - return "-#{option}" - else - return "--#{option}" +print = (data) -> + console.log indent columnify data, + showHeaders: false + minWidth: 35 -addAlias = (alias) -> - return ", #{addOptionPrefix(alias)}" - -buildOptionSignatureHelp = (option) -> - result = addOptionPrefix(option.signature.toString()) - - if _.isString(option.alias) - result += addAlias(option.alias) - else if _.isArray(option.alias) - for alias in option.alias - result += addAlias(alias) - - if option.parameter? - result += " <#{option.parameter}>" - - return result - -getCommandHelp = (command) -> - maxSignatureLength = getFieldMaxLength(capitano.state.commands, 'signature') - commandSignature = _.str.rpad(command.signature.toString(), maxSignatureLength, ' ') - return buildHelpString(commandSignature, command.description) - -getOptionsParsedSignatures = (optionsHelp) -> - maxLength = _.max _.map optionsHelp, (signature) -> - return signature.length - - return _.map optionsHelp, (signature) -> - return _.str.rpad(signature, maxLength, ' ') - -getOptionHelp = (option, maxLength) -> - result = PADDING_INITIAL - result += _.str.rpad(option.signature, maxLength, ' ') - result += PADDING_MIDDLE - result += option.description - return result - -general = -> +general = (params, options, done) -> console.log('Usage: resin [COMMAND] [OPTIONS]\n') console.log('Commands:\n') - for command in capitano.state.commands - continue if command.isWildcard() - console.log(getCommandHelp(command)) + # We do not want the wildcard command + # to be printed in the help screen. + commands = _.reject capitano.state.commands, (command) -> + return command.isWildcard() + + print(parse(commands)) if not _.isEmpty(capitano.state.globalOptions) console.log('\nGlobal Options:\n') + print(parse(capitano.state.globalOptions)) - options = _.map capitano.state.globalOptions, (option) -> - option.signature = buildOptionSignatureHelp(option) - return option - - optionSignatureMaxLength = _.max _.map options, (option) -> - return option.signature.length - - for option in options - console.log(getOptionHelp(option, optionSignatureMaxLength)) - - console.log() + return done() command = (params, options, done) -> capitano.state.getMatchCommand params.command, (error, command) -> return done(error) if error? if not command? or command.isWildcard() - return capitano.defaults.actions.commandNotFound(params.command) + return done(new Error("Command not found: #{params.command}")) console.log("Usage: #{command.signature}") @@ -98,18 +61,7 @@ command = (params, options, done) -> if not _.isEmpty(command.options) console.log('\nOptions:\n') - - options = _.map command.options, (option) -> - option.signature = buildOptionSignatureHelp(option) - return option - - optionSignatureMaxLength = _.max _.map options, (option) -> - return option.signature.toString().length - - for option in options - console.log(getOptionHelp(option, optionSignatureMaxLength)) - - console.log() + print(parse(command.options)) return done() diff --git a/package.json b/package.json index 9b6089b2..bfcdc696 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,9 @@ "dependencies": { "async": "^1.3.0", "bluebird": "^2.9.34", - "capitano": "~1.6.1", + "capitano": "~1.7.0", "coffee-script": "^1.9.3", + "columnify": "^1.5.2", "html-to-text": "^1.3.1", "lodash": "^3.10.0", "mkdirp": "~0.5.0",