From dbff5659a5395ae54baa87451ffcc7dccd32693d Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 10:12:55 -0700 Subject: [PATCH 01/12] added underscore --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 406f28c..8e7f56f 100644 --- a/package.json +++ b/package.json @@ -42,5 +42,8 @@ }, "keywords": [ "gruntplugin" - ] + ], + "dependencies": { + "underscore": "^1.6.0" + } } From b62b1fa9c807e4438832868a356e6ae9da201d00 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 17:04:08 -0700 Subject: [PATCH 02/12] added argument management library --- lib/arg.js | 158 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/argUtil.js | 70 ++++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 lib/arg.js create mode 100644 lib/argUtil.js diff --git a/lib/arg.js b/lib/arg.js new file mode 100644 index 0000000..5854c9b --- /dev/null +++ b/lib/arg.js @@ -0,0 +1,158 @@ +'use strict'; +// Helper libraries +var uscore = require('underscore'); +uscore.string = require('underscore.string'); + +// Static helper functions +/** + * Get keys in testObj that are not in standardObj + * @param [object] standardObj An object that contains acceptable keys + * @param [object] testObj An object whose keys are compared against standard + * @return [array] An array of keys that are in testObj but not in standardObj + */ +var getExtraKeys = function (standardObj, testObj) { + var stdKeys = uscore.keys(standardObj); + var testKeys = uscore.keys(testObj); + return uscore.difference(testKeys, stdKeys); +}; + +// Arg Prototype +/** + * Constructor for Arg prototype + * @param [object] config An object with properties for the new Arg + */ +var Arg = function (config) { + var errorMsg = ''; + // Set default value for config; + config = config || {}; + + // Validate config + if (!uscore.isObject(config)) { + errorMsg += 'Invalid config passed to Arg. '; + errorMsg += 'Expected `object` but receieved ' + typeof config; + throw new Error(errorMsg); + } + + // Set default properties + this.option = undefined; + this.defaultValue = undefined; + this.validationFn = undefined; + this.flag = undefined; + this.useDasherize = true; + this.required = false; + this.useValue = false; + this.useAsFlag = true; + this.flagify = undefined; + this.value = undefined; + this.customValueFn = undefined; + this.customFlagFn = undefined; + + // Verify that no invalid keys are specified in config + var invalidKeys = getExtraKeys(this, config); + if (invalidKeys.length) { + errorMsg += 'Unknown values passed to Arg constructor ('; + errorMsg += invalidKeys.join(', ') + ')'; + throw new Error(errorMsg); + } + + // Extend default options with config; + uscore.extend(this, config); + + // Verify that required properties have been set + if (!this.option) { + errorMsg += 'An `option` property must be specified in Arg'; + throw new Error(errorMsg); + } +}; + +/** + * Validate arg's value. + * If `arg.validationFn` is a function, then call it to validate + * If `arg.required == true`, check that + * arg.value is not undefined, null, or an empty string + * Note: 'false' and zero are consider valid values. + * @return [boolean] True if validation succeeds, false if not. + */ +Arg.prototype.validate = function () { + var testVal = this.value; + var errorMsg = ''; + if (uscore.isFunction(this.validationFn)) { + return this.validationFn.call(null, this); + } else if (this.required) { + if (uscore.isString(testVal)) { + testVal = testVal.trim(); + } + if (testVal !== null && testVal !== '' && testVal !== undefined) { + errorMsg += this.option + 'is required, but its value is ('; + errorMsg += this.value + ')'; + throw new Error(errorMsg); + } + } else { + return true; + } +}; + +/** + * Get this arg's flag value. + * @return [string] The flag or null (see below): + * If `arg.useAsFlag == false`, then null. + * If `arg.flag` was explicitly set, then that value. + * If `arg.useDasherize`, then a dasherized version of arg.option. + * If `arg.flagify` is a function, then the result of calling flagify. + * Otherwise, `arg.option`. + */ +Arg.prototype.getFlag = function () { + if (!this.validate) { + throw new Error(this.option + ' failed validation.'); + } + if (uscore.isFunction(this.customFlagFn)) { + return this.customFlagFn.call(null, this); + } else if (this.useAsFlag) { + // Only return flags for args with non-falsy and zero values + if (this.value || this.value === 0) { + if (this.flag) { + return this.flag; + } else if (this.useDasherize) { + return '--' + uscore.string.dasherize(this.option); + } else if (uscore.isFunction(this.flagify)) { + return this.flagify.call(null, this); + } else { + return this.option; + } + } + } + return null; +}; + +/** + * Set this arg's value. + * @param [object] Options object containing option:value pairs + * @return [object] this object + */ +Arg.prototype.setValueFromOptions = function (options) { + this.value = options[this.option]; + return this; +}; + +/** + * Get this arg's value. + * @param + * @return [string|int] The arg's value or null (see below): + * If `arg.useValue == false`, then null. + * Otherwise, `arg.value`. + */ +Arg.prototype.getValue = function () { + if (!this.validate) { + throw new Error(this.option + ' failed validation.'); + } + if (uscore.isFunction(this.customValueFn)) { + return this.customValueFn.call(null, this); + } else if (this.useValue) { + if (this.value || this.value === 0) { + return this.value; + } + } + return null; +}; + +module.exports = Arg; diff --git a/lib/argUtil.js b/lib/argUtil.js new file mode 100644 index 0000000..3f915e2 --- /dev/null +++ b/lib/argUtil.js @@ -0,0 +1,70 @@ +'use strict'; +var uscore = require('underscore'); +var Arg = require('../lib/arg.js'); + +// Static helper functions +/** + * Helper function to test for null values (useful in array.filter) + * @param [any] val The value to be tested + * @return [boolean] false if the value is null + */ +var isNotNull = function (val) { + return val !== null; +}; + +/** + * Constructor for ArgUtil prototype + * @param [function] gruntTask The current grunt task + * (usually `this` in the context of a grunt task, or `grunt.task.current` + * @param [array] configs an array of Argument configuration objects + */ +var ArgUtil = function (gruntTask, configs) { + var self = this; + this.args = configs.map(function createArg(config) { + return new Arg(config); + }); + var defaultOptions = this.generateDefaultOptions(self.args); + + var options = gruntTask.options.call(gruntTask, defaultOptions); + + this.setArgValuesFromOptions(options); +}; + +/** + * Generate an object containing the option and default value for each arg + * in this.args + * @return [object] and object containing `object: defaultValue` pairs + */ +ArgUtil.prototype.generateDefaultOptions = function () { + return this.args.reduce(function createDefOpt(options, arg) { + options[arg.option] = arg.defaultValue; + return options; + }, {}); +}; + +/** + * Set the value of each arg in this.args from the options + * @param [options] options An object containing option:value pairs + * @return [object] this ArgUtil object + */ +ArgUtil.prototype.setArgValuesFromOptions = function (options) { + this.args = this.args.map(function callSetValue(arg) { + return arg.setValueFromOptions(options); + }); + return this; +}; + +/** + * Get array of flags for all args + * @return [array] An array of flags generated by each arg in this.args + */ +ArgUtil.prototype.getArgFlags = function () { + return uscore.flatten(this.args.map(function callGetFlags(arg) { + return [ + arg.getFlag(), + arg.getValue() + ]; + })).filter(isNotNull); +}; + +module.exports = ArgUtil; From b72ecff08a19087661eb830abe631fe8e9ddbc40 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 17:04:50 -0700 Subject: [PATCH 03/12] added underscore.string to dependencies --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e7f56f..7e14442 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "gruntplugin" ], "dependencies": { - "underscore": "^1.6.0" + "underscore": "^1.6.0", + "underscore.string": "^2.3.3" } } From 90775e0e1259c9a1717cf4b350da294b7b459fc7 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 17:07:43 -0700 Subject: [PATCH 04/12] converted four grunt-git libs to use argUtil --- lib/command_archive.js | 113 ++++++++++++++++++++++------------------ lib/command_checkout.js | 47 +++++++++-------- lib/command_clean.js | 86 ++++++++++++++++++------------ lib/command_pull.js | 32 +++++++----- 4 files changed, 160 insertions(+), 118 deletions(-) diff --git a/lib/command_archive.js b/lib/command_archive.js index bd708f7..592d05c 100644 --- a/lib/command_archive.js +++ b/lib/command_archive.js @@ -2,65 +2,76 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { + var argUtil = new ArgUtil(task, [ + { + // --format= + // Format of the resulting archive: tar or zip. If this option is not given, and the output file is specified, the format is inferred from the filename if possible (e.g. writing to "foo.zip" makes the output to be in the zip format). Otherwise the output format is tar. + option: 'format', + defaultValue: null, + useAsFlag: true, + useValue: true + }, + { + // --prefix=/ + // Prepend / to each filename in the archive. + option: 'prefix', + defaultValue: null, + useAsFlag: true, + useValue: true + }, + { + // --output= + // Write the archive to instead of stdout. + option: 'output', + defaultValue: null, + useAsFlag: true, + useValue: true + }, + { + // --remote= + // Instead of making a tar archive from the local repository, retrieve a tar archive from a remote repository. + // Note: It seems that GitHub does not support the remote archiving feature. + option: 'remote', + defaultValue: null, + useAsFlag: true, + useValue: true + }, + { + // + // The tree or commit to produce an archive for. + option: 'treeIsh', + defaultValue: 'master', + useAsFlag: false, + useValue: true, + required: true + }, + { + // + // Without an optional path parameter, all files and subdirectories of the current working directory are included in the archive. If one or more paths are specified, only these are included. + option: 'path', + defaultValue: null, + useAsFlag: false, + useValue: true, + required: true, + customValueFn: function (arg) { + if (grunt.util.kindOf(arg.value) === 'string') { + // Backwards compatible to <= 0.2.8. + arg.value = [arg.value]; + } + return arg.value; + } + } + ]); var options = task.options({ treeIsh: 'master' }); - var args = ['archive']; - + var args = ['archive'].concat(argUtil.getArgFlags()); // git archive --format= --prefix=/ treeIsh --output= - if (!options.treeIsh || options.treeIsh.trim() === '') { - done(new Error('gitarchive requires a treeIsh parameter.')); - return; - } - - // --format= - // Format of the resulting archive: tar or zip. If this option is not given, and the output file is specified, the format is inferred from the filename if possible (e.g. writing to "foo.zip" makes the output to be in the zip format). Otherwise the output format is tar. - if (options.format && options.format.trim() !== '') { - args.push('--format'); - args.push(options.format.trim()); - } - - // --prefix=/ - // Prepend / to each filename in the archive. - if (options.prefix && options.prefix.trim() !== '') { - args.push('--prefix'); - args.push(options.prefix.trim()); - } - - // --output= - // Write the archive to instead of stdout. - if (options.output && options.output.trim() !== '') { - args.push('--output'); - args.push(options.output.trim()); - } - - // --remote= - // Instead of making a tar archive from the local repository, retrieve a tar archive from a remote repository. - // Note: It seems that GitHub does not support the remote archiving feature. - if (options.remote && options.remote.trim() !== '') { - args.push('--remote'); - args.push(options.remote.trim()); - } - - // - // The tree or commit to produce an archive for. - args.push(options.treeIsh.trim()); - - // - // Without an optional path parameter, all files and subdirectories of the current working directory are included in the archive. If one or more paths are specified, only these are included. - if (options.path) { - if (grunt.util.kindOf(options.path) === 'string') { - // Backwards compatible to <= 0.2.8. - options.path = [options.path]; - } - - args = args.concat(options.path); - } - // Add callback args.push(done); diff --git a/lib/command_checkout.js b/lib/command_checkout.js index 98e2e46..997ebbd 100644 --- a/lib/command_checkout.js +++ b/lib/command_checkout.js @@ -2,29 +2,34 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - branch: null, - create: false, - overwrite: false - }); - - var args = ['checkout']; - - if (!options.branch) { - done(new Error('gitcheckout tasks requires that you specify a "branch" (this can be a commit ID)')); - return; - } - - if (options.overwrite) { - args.push('-B'); - } else if (options.create) { - args.push('-b'); - } - - - args.push(options.branch); + var argUtil = new ArgUtil(task, [ + { + option: 'create', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-b' + }, + { + option: 'overwrite', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-B' + }, + { + option: 'branch', + defaultValue: undefined, + useAsFlag: false, + useValue: true, + required: true + } + ]); + + var args = ['checkout'].concat(argUtil.getArgFlags()); // Add callback args.push(done); diff --git a/lib/command_clean.js b/lib/command_clean.js index 4499655..ca21477 100644 --- a/lib/command_clean.js +++ b/lib/command_clean.js @@ -2,42 +2,62 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - force: true, - dry: false, - quiet: false, - exclude: false, - onlyignoredfiles: false, - nonstandard: false, - directories: false - }); - - var args = ['clean']; + var argUtil = new ArgUtil(task, [ + { + option: 'force', + defaultValue: true, + useAsFlag: true, + useValue: false, + flag: '-f' + }, + { + option: 'dry', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-n' + }, + { + option: 'quiet', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-q' + }, + { + option: 'exclude', + defaultValue: false, + useAsFlag: true, + useValue: true, + flag: '-e' + }, + { + option: 'onlyignoredfiles', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-X' + }, + { + option: 'nonstandard', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-x' + }, + { + option: 'directories', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-d' + }, + ]); - // Add the options to the command line arguments - if (options.force && options.dry === false) { - args.push('-f'); - } - if (options.dry) { - args.push('-n'); - } - if (options.quiet) { - args.push('-q'); - } - if (options.exclude) { - args.push('-e ' + options.exclude); - } - if (options.onlyignoredfiles) { - args.push('-X'); - } - if (options.nonstandard) { - args.push('-x'); - } - if (options.directories) { - args.push('-d'); - } + var args = ['clean'].concat(argUtil.getArgFlags()); // Add the file paths to the arguments. task.files.forEach(function (files) { diff --git a/lib/command_pull.js b/lib/command_pull.js index 825e02f..c9b6917 100644 --- a/lib/command_pull.js +++ b/lib/command_pull.js @@ -2,20 +2,26 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - remote: 'origin', - branch: null - }); - - var args = ['pull']; - - args.push(options.remote); - - if (options.branch) { - args.push(options.branch); - } + var argUtil = new ArgUtil(task, [ + { + option: 'remote', + defaultValue: 'origin', + useAsFlag: false, + useValue: true + }, + { + option: 'branch', + defaultValue: null, + useAsFlag: false, + useValue: true + } + ]); + + + var args = ['pull'].concat(argUtil.getArgFlags()); // Add callback args.push(done); @@ -23,4 +29,4 @@ module.exports = function (task, exec, done) { exec.apply(this, args); }; -module.exports.description = 'Pull a remote.'; \ No newline at end of file +module.exports.description = 'Pull a remote.'; From 0544f69ad34fa54c07275a6c7ace040f0003f760 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 17:09:13 -0700 Subject: [PATCH 05/12] fixed inconsistent tests: dry-run does not need to override force, and all flags should be separate entries in args array --- test/clean_test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/clean_test.js b/test/clean_test.js index 56972c8..7289e39 100644 --- a/test/clean_test.js +++ b/test/clean_test.js @@ -28,7 +28,7 @@ describe('clean', function () { }; new Test(command, options) - .expect(['clean', '-n']) + .expect(['clean', '-f', '-n']) .run(done); }); @@ -48,7 +48,7 @@ describe('clean', function () { }; new Test(command, options) - .expect(['clean', '-f', '-e *.log']) + .expect(['clean', '-f', '-e', '*.log']) .run(done); }); From a0550b877247d78e529c2583ae24dd72047c8b37 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 17:30:22 -0700 Subject: [PATCH 06/12] converted command_clone to use argUtils. This required doing away with not overriding --branch with --bare: The git CLI does not do this, so I do not think that it is our place to do it here --- lib/command_clone.js | 83 ++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/lib/command_clone.js b/lib/command_clone.js index 68d9de6..9218c59 100644 --- a/lib/command_clone.js +++ b/lib/command_clone.js @@ -2,49 +2,50 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - bare: false, - recursive: false, - branch: false, - repository: false, - directory: false - }); - - var args = ['clone']; - - // repo is the sole required option - if (!options.repository) { - done(new Error('gitclone tasks requires that you specify a "repository"')); - return; - } - - if (options.bare) { - args.push('--bare'); - } - - if (options.recursive) { - args.push('--recursive'); - } - - if (options.branch && !options.bare) { - args.push('--branch'); - args.push(options.branch); - } - - if (typeof options.depth !== 'undefined') { - args.push('--depth'); - args.push(options.depth); - } - - // repo comes after the options - args.push(options.repository); - - // final argument is checkout directory (optional) - if (options.directory) { - args.push(options.directory); - } + var argUtil = new ArgUtil(task, [ + { + option: 'bare', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'recursive', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'branch', + defaultValue: false, + useAsFlag: true, + useValue: true + }, + { + option: 'depth', + defaultValue: null, + useAsFlag: true, + useValue: true + }, + { + option: 'repository', + defaultValue: null, + useAsFlag: false, + useValue: true, + required: true + }, + { + option: 'directory', + defaultValue: null, + useAsFlag: false, + useValue: true + } + ]); + + var args = ['clone'].concat(argUtil.getArgFlags()); // Add callback args.push(done); From 70faea10b25358bed4a853f11a7f6108cf47456d Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 18:27:21 -0700 Subject: [PATCH 07/12] corrected enforcement of required args --- lib/arg.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/arg.js b/lib/arg.js index 5854c9b..f3757d5 100644 --- a/lib/arg.js +++ b/lib/arg.js @@ -82,14 +82,13 @@ Arg.prototype.validate = function () { if (uscore.isString(testVal)) { testVal = testVal.trim(); } - if (testVal !== null && testVal !== '' && testVal !== undefined) { - errorMsg += this.option + 'is required, but its value is ('; + if (testVal === null || testVal === '' || testVal === undefined) { + errorMsg += this.option + ' is required, but its value is ('; errorMsg += this.value + ')'; throw new Error(errorMsg); } - } else { - return true; } + return true; }; /** @@ -102,7 +101,7 @@ Arg.prototype.validate = function () { * Otherwise, `arg.option`. */ Arg.prototype.getFlag = function () { - if (!this.validate) { + if (!this.validate()) { throw new Error(this.option + ' failed validation.'); } if (uscore.isFunction(this.customFlagFn)) { @@ -142,7 +141,7 @@ Arg.prototype.setValueFromOptions = function (options) { * Otherwise, `arg.value`. */ Arg.prototype.getValue = function () { - if (!this.validate) { + if (!this.validate()) { throw new Error(this.option + ' failed validation.'); } if (uscore.isFunction(this.customValueFn)) { From a6dff00260691cc71b64552e4dbfbe4df00c4ad7 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 18:28:23 -0700 Subject: [PATCH 08/12] made options available as public property --- lib/argUtil.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/argUtil.js b/lib/argUtil.js index 3f915e2..957387d 100644 --- a/lib/argUtil.js +++ b/lib/argUtil.js @@ -25,9 +25,9 @@ var ArgUtil = function (gruntTask, configs) { }); var defaultOptions = this.generateDefaultOptions(self.args); - var options = gruntTask.options.call(gruntTask, defaultOptions); + this.options = gruntTask.options.call(gruntTask, defaultOptions); - this.setArgValuesFromOptions(options); + this.setArgValuesFromOptions(); }; /** @@ -47,9 +47,10 @@ ArgUtil.prototype.generateDefaultOptions = function () { * @param [options] options An object containing option:value pairs * @return [object] this ArgUtil object */ -ArgUtil.prototype.setArgValuesFromOptions = function (options) { +ArgUtil.prototype.setArgValuesFromOptions = function () { + var self = this; this.args = this.args.map(function callSetValue(arg) { - return arg.setValueFromOptions(options); + return arg.setValueFromOptions(self.options); }); return this; }; From 2d9bb874ddb06622fd859913e4f4c389f4910bf1 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 18:28:42 -0700 Subject: [PATCH 09/12] converted remaining files to use argUtil --- lib/command_archive.js | 12 +++-- lib/command_commit.js | 45 +++++++++++----- lib/command_merge.js | 116 ++++++++++++++++++++++------------------- lib/command_push.js | 74 ++++++++++++++------------ lib/command_rebase.js | 41 +++++++++------ lib/command_reset.js | 35 +++++++++---- lib/command_stash.js | 47 +++++++++++------ lib/command_tag.js | 47 +++++++++-------- 8 files changed, 246 insertions(+), 171 deletions(-) diff --git a/lib/command_archive.js b/lib/command_archive.js index 592d05c..75f43fe 100644 --- a/lib/command_archive.js +++ b/lib/command_archive.js @@ -55,13 +55,15 @@ module.exports = function (task, exec, done) { defaultValue: null, useAsFlag: false, useValue: true, - required: true, customValueFn: function (arg) { - if (grunt.util.kindOf(arg.value) === 'string') { - // Backwards compatible to <= 0.2.8. - arg.value = [arg.value]; + if (arg.value) { + if (grunt.util.kindOf(arg.value) === 'string') { + // Backwards compatible to <= 0.2.8. + arg.value = [arg.value]; + } + return arg.value; } - return arg.value; + return null; } } ]); diff --git a/lib/command_commit.js b/lib/command_commit.js index 64bd8c4..250bad8 100644 --- a/lib/command_commit.js +++ b/lib/command_commit.js @@ -1,23 +1,40 @@ 'use strict'; var async = require('grunt').util.async; +var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - message: 'Commit', - ignoreEmpty: false, - noVerify: false, - noStatus: false - }); - var args = ['commit', '-m', options.message]; - - if (options.noVerify) { - args.push('--no-verify'); - } + var argUtil = new ArgUtil(task, [ + { + option: 'message', + defaultValue: 'Commit', + useAsFlag: true, + useValue: true, + flag: '-m' + }, + { + option: 'ignoreEmpty', + defaultValue: false, + useAsFlag: false, + useValue: false + }, + { + option: 'noVerify', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'noStatus', + defaultValue: false, + useAsFlag: true, + useValue: false + } + ]); - if (options.noStatus) { - args.push('--no-status'); - } + var options = argUtil.options; + var args = ['commit'].concat(argUtil.getArgFlags()); args.push(done); diff --git a/lib/command_merge.js b/lib/command_merge.js index a10b8c3..dc9d3fa 100644 --- a/lib/command_merge.js +++ b/lib/command_merge.js @@ -1,62 +1,70 @@ 'use strict'; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - branch: null, - squash: false, - noff: false, - ffOnly: false, - edit: false, - noEdit: false, - message: null, - commit: false, - noCommit: false, - - }); - - var args = ['merge']; - - if (!options.branch) { - done(new Error('gitmerge task requires that you specify a "branch" to merge from.')); - return; - } - - args.push(options.branch); - - if (options.ffOnly) { - args.push('--ff-only'); - } - - if (options.squash) { - args.push('--squash'); - } - - if (options.noff) { - args.push('--no-ff'); - } - - if (options.edit) { - args.push('--edit'); - } - - if (options.noEdit) { - args.push('--no-edit'); - } - - if (options.message) { - args.push('-m'); - args.push(options.message); - } - - if (options.commit) { - args.push('--commit'); - } - - if (options.noCommit) { - args.push('--no-commit'); - } + var argUtil = new ArgUtil(task, [ + { + option: 'branch', + defaultValue: null, + useAsFlag: false, + useValue: true, + required: true + }, + { + option: 'ffOnly', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'squash', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'noff', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '--no-ff' + }, + { + option: 'edit', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'noEdit', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'message', + defaultValue: null, + useAsFlag: true, + useValue: true, + flag: '-m' + }, + { + option: 'commit', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'noCommit', + defaultValue: false, + useAsFlag: true, + useValue: false + } + ]); + + var args = ['merge'].concat(argUtil.getArgFlags()); // Add callback args.push(done); diff --git a/lib/command_push.js b/lib/command_push.js index 0f5205c..6caaf19 100644 --- a/lib/command_push.js +++ b/lib/command_push.js @@ -2,40 +2,50 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - remote: 'origin', - branch: null, - all: false, - upstream: false, - tags: false, - force: false - }); - - var args = ['push', ]; - - if (options.all) { - args.push('--all'); - } - - if (options.upstream) { - args.push('--set-upstream'); - } - - if (options.tags) { - args.push('--tags'); - } - - if (options.force) { - args.push('--force'); - } - - args.push(options.remote); - - if (options.branch) { - args.push(options.branch); - } + var argUtil = new ArgUtil(task, [ + { + option: 'all', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'upstream', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '--set-upstream' + }, + { + option: 'tags', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'force', + defaultValue: false, + useAsFlag: true, + useValue: false + }, + { + option: 'remote', + defaultValue: 'origin', + useAsFlag: false, + useValue: true + }, + { + option: 'branch', + defaultValue: null, + useAsFlag: false, + useValue: true + } + ]); + + var args = ['push'].concat(argUtil.getArgFlags()); // Add callback args.push(done); diff --git a/lib/command_rebase.js b/lib/command_rebase.js index 43599cd..72c56d6 100644 --- a/lib/command_rebase.js +++ b/lib/command_rebase.js @@ -2,25 +2,32 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - theirs: false, - branch: null - }); - - var args = ['rebase']; - - if (!options.branch) { - done(new Error('gitrebase requires a branch parameter.')); - return; - } - - if (options.theirs) { - args.push('--strategy=recursive', '-Xtheirs'); - } - - args.push(options.branch); + var argUtil = new ArgUtil(task, [ + { + option: 'theirs', + defaultValue: false, + useAsFlag: true, + useValue: false, + customFlagFn: function (arg) { + if (arg.value) { + return ['--strategy=recursive', '-Xtheirs']; + } + return null; + } + }, + { + option: 'branch', + defaultValue: null, + useAsFlag: false, + useValue: true, + required: true + } + ]); + + var args = ['rebase'].concat(argUtil.getArgFlags()); // Add callback args.push(done); diff --git a/lib/command_reset.js b/lib/command_reset.js index 4c5bfc3..a2afc65 100644 --- a/lib/command_reset.js +++ b/lib/command_reset.js @@ -2,19 +2,32 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - commit: 'HEAD' - }); - - var args = ['reset']; - - if (options.mode) { - args.push('--' + options.mode); - } - - args.push(options.commit); + var argUtil = new ArgUtil(task, [ + { + option: 'mode', + defaultValue: false, + useAsFlag: false, + useValue: true, + customValueFn: function (arg) { + if (arg.value) { + return ['--' + arg.value]; + } + return null; + } + }, + { + option: 'commit', + defaultValue: 'HEAD', + useAsFlag: false, + useValue: true + } + ]); + + var options = argUtil.options; + var args = ['reset'].concat(argUtil.getArgFlags()); if (!options.mode) { task.files.forEach(function (files) { diff --git a/lib/command_stash.js b/lib/command_stash.js index f636508..066882d 100644 --- a/lib/command_stash.js +++ b/lib/command_stash.js @@ -2,25 +2,38 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - command: 'save', - stash: null, - staged: false - }); - - var args = ['stash']; - - args.push(options.command); - - if (options.stash) { - args.push('stash@{' + options.stash + '}'); - } - - if (options.staged) { - args.push('--index'); - } + var argUtil = new ArgUtil(task, [ + { + option: 'command', + defaultValue: 'save', + useAsFlag: false, + useValue: true + }, + { + option: 'stash', + defaultValue: null, + useAsFlag: false, + useValue: true, + customValueFn: function (arg) { + if (arg.value) { + return ['stash@{' + arg.value + '}']; + } + return null; + } + }, + { + option: 'staged', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '--index' + } + ]); + + var args = ['stash'].concat(argUtil.getArgFlags()); // Add callback args.push(done); diff --git a/lib/command_tag.js b/lib/command_tag.js index 3408aee..c05e09e 100644 --- a/lib/command_tag.js +++ b/lib/command_tag.js @@ -2,29 +2,34 @@ var async = require('grunt').util.async; var grunt = require('grunt'); +var ArgUtil = require('../lib/argUtil.js'); module.exports = function (task, exec, done) { - var options = task.options({ - message: '' - }); - - var args = ['tag']; - - if (!options.tag) { - done(new Error('gittag requires a tag parameter.')); - return; - } - - if (options.remove === true) { - args.push('-d'); - } - - if (options.message && options.message.trim() !== '') { - args.push('-m'); - args.push(options.message); - } - - args.push(options.tag); + var argUtil = new ArgUtil(task, [ + { + option: 'remove', + defaultValue: false, + useAsFlag: true, + useValue: false, + flag: '-d' + }, + { + option: 'message', + defaultValue: '', + useAsFlag: true, + useValue: true, + flag: '-m' + }, + { + option: 'tag', + defaultValue: null, + useAsFlag: false, + useValue: true, + required: true + } + ]); + + var args = ['tag'].concat(argUtil.getArgFlags()); // Add callback args.push(done); From 10e62a682767ee43d65cb6b06ec5a488d1d0cf33 Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Sat, 12 Jul 2014 23:39:45 -0700 Subject: [PATCH 10/12] replaced argUtil.js with flopmang module --- lib/arg.js | 157 ---------------------------------------- lib/argUtil.js | 71 ------------------ lib/command_archive.js | 2 +- lib/command_checkout.js | 2 +- lib/command_clean.js | 2 +- lib/command_clone.js | 2 +- lib/command_commit.js | 2 +- lib/command_merge.js | 2 +- lib/command_pull.js | 2 +- lib/command_push.js | 2 +- lib/command_rebase.js | 2 +- lib/command_reset.js | 2 +- lib/command_stash.js | 2 +- lib/command_tag.js | 2 +- package.json | 1 + 15 files changed, 13 insertions(+), 240 deletions(-) delete mode 100644 lib/arg.js delete mode 100644 lib/argUtil.js diff --git a/lib/arg.js b/lib/arg.js deleted file mode 100644 index f3757d5..0000000 --- a/lib/arg.js +++ /dev/null @@ -1,157 +0,0 @@ -'use strict'; -// Helper libraries -var uscore = require('underscore'); -uscore.string = require('underscore.string'); - -// Static helper functions -/** - * Get keys in testObj that are not in standardObj - * @param [object] standardObj An object that contains acceptable keys - * @param [object] testObj An object whose keys are compared against standard - * @return [array] An array of keys that are in testObj but not in standardObj - */ -var getExtraKeys = function (standardObj, testObj) { - var stdKeys = uscore.keys(standardObj); - var testKeys = uscore.keys(testObj); - return uscore.difference(testKeys, stdKeys); -}; - -// Arg Prototype -/** - * Constructor for Arg prototype - * @param [object] config An object with properties for the new Arg - */ -var Arg = function (config) { - var errorMsg = ''; - // Set default value for config; - config = config || {}; - - // Validate config - if (!uscore.isObject(config)) { - errorMsg += 'Invalid config passed to Arg. '; - errorMsg += 'Expected `object` but receieved ' + typeof config; - throw new Error(errorMsg); - } - - // Set default properties - this.option = undefined; - this.defaultValue = undefined; - this.validationFn = undefined; - this.flag = undefined; - this.useDasherize = true; - this.required = false; - this.useValue = false; - this.useAsFlag = true; - this.flagify = undefined; - this.value = undefined; - this.customValueFn = undefined; - this.customFlagFn = undefined; - - // Verify that no invalid keys are specified in config - var invalidKeys = getExtraKeys(this, config); - if (invalidKeys.length) { - errorMsg += 'Unknown values passed to Arg constructor ('; - errorMsg += invalidKeys.join(', ') + ')'; - throw new Error(errorMsg); - } - - // Extend default options with config; - uscore.extend(this, config); - - // Verify that required properties have been set - if (!this.option) { - errorMsg += 'An `option` property must be specified in Arg'; - throw new Error(errorMsg); - } -}; - -/** - * Validate arg's value. - * If `arg.validationFn` is a function, then call it to validate - * If `arg.required == true`, check that - * arg.value is not undefined, null, or an empty string - * Note: 'false' and zero are consider valid values. - * @return [boolean] True if validation succeeds, false if not. - */ -Arg.prototype.validate = function () { - var testVal = this.value; - var errorMsg = ''; - if (uscore.isFunction(this.validationFn)) { - return this.validationFn.call(null, this); - } else if (this.required) { - if (uscore.isString(testVal)) { - testVal = testVal.trim(); - } - if (testVal === null || testVal === '' || testVal === undefined) { - errorMsg += this.option + ' is required, but its value is ('; - errorMsg += this.value + ')'; - throw new Error(errorMsg); - } - } - return true; -}; - -/** - * Get this arg's flag value. - * @return [string] The flag or null (see below): - * If `arg.useAsFlag == false`, then null. - * If `arg.flag` was explicitly set, then that value. - * If `arg.useDasherize`, then a dasherized version of arg.option. - * If `arg.flagify` is a function, then the result of calling flagify. - * Otherwise, `arg.option`. - */ -Arg.prototype.getFlag = function () { - if (!this.validate()) { - throw new Error(this.option + ' failed validation.'); - } - if (uscore.isFunction(this.customFlagFn)) { - return this.customFlagFn.call(null, this); - } else if (this.useAsFlag) { - // Only return flags for args with non-falsy and zero values - if (this.value || this.value === 0) { - if (this.flag) { - return this.flag; - } else if (this.useDasherize) { - return '--' + uscore.string.dasherize(this.option); - } else if (uscore.isFunction(this.flagify)) { - return this.flagify.call(null, this); - } else { - return this.option; - } - } - } - return null; -}; - -/** - * Set this arg's value. - * @param [object] Options object containing option:value pairs - * @return [object] this object - */ -Arg.prototype.setValueFromOptions = function (options) { - this.value = options[this.option]; - return this; -}; - -/** - * Get this arg's value. - * @param - * @return [string|int] The arg's value or null (see below): - * If `arg.useValue == false`, then null. - * Otherwise, `arg.value`. - */ -Arg.prototype.getValue = function () { - if (!this.validate()) { - throw new Error(this.option + ' failed validation.'); - } - if (uscore.isFunction(this.customValueFn)) { - return this.customValueFn.call(null, this); - } else if (this.useValue) { - if (this.value || this.value === 0) { - return this.value; - } - } - return null; -}; - -module.exports = Arg; diff --git a/lib/argUtil.js b/lib/argUtil.js deleted file mode 100644 index 957387d..0000000 --- a/lib/argUtil.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; -var uscore = require('underscore'); -var Arg = require('../lib/arg.js'); - -// Static helper functions -/** - * Helper function to test for null values (useful in array.filter) - * @param [any] val The value to be tested - * @return [boolean] false if the value is null - */ -var isNotNull = function (val) { - return val !== null; -}; - -/** - * Constructor for ArgUtil prototype - * @param [function] gruntTask The current grunt task - * (usually `this` in the context of a grunt task, or `grunt.task.current` - * @param [array] configs an array of Argument configuration objects - */ -var ArgUtil = function (gruntTask, configs) { - var self = this; - this.args = configs.map(function createArg(config) { - return new Arg(config); - }); - var defaultOptions = this.generateDefaultOptions(self.args); - - this.options = gruntTask.options.call(gruntTask, defaultOptions); - - this.setArgValuesFromOptions(); -}; - -/** - * Generate an object containing the option and default value for each arg - * in this.args - * @return [object] and object containing `object: defaultValue` pairs - */ -ArgUtil.prototype.generateDefaultOptions = function () { - return this.args.reduce(function createDefOpt(options, arg) { - options[arg.option] = arg.defaultValue; - return options; - }, {}); -}; - -/** - * Set the value of each arg in this.args from the options - * @param [options] options An object containing option:value pairs - * @return [object] this ArgUtil object - */ -ArgUtil.prototype.setArgValuesFromOptions = function () { - var self = this; - this.args = this.args.map(function callSetValue(arg) { - return arg.setValueFromOptions(self.options); - }); - return this; -}; - -/** - * Get array of flags for all args - * @return [array] An array of flags generated by each arg in this.args - */ -ArgUtil.prototype.getArgFlags = function () { - return uscore.flatten(this.args.map(function callGetFlags(arg) { - return [ - arg.getFlag(), - arg.getValue() - ]; - })).filter(isNotNull); -}; - -module.exports = ArgUtil; diff --git a/lib/command_archive.js b/lib/command_archive.js index 75f43fe..ba6c864 100644 --- a/lib/command_archive.js +++ b/lib/command_archive.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_checkout.js b/lib/command_checkout.js index 997ebbd..2c2b97a 100644 --- a/lib/command_checkout.js +++ b/lib/command_checkout.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_clean.js b/lib/command_clean.js index ca21477..9ae531e 100644 --- a/lib/command_clean.js +++ b/lib/command_clean.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_clone.js b/lib/command_clone.js index 9218c59..f55e548 100644 --- a/lib/command_clone.js +++ b/lib/command_clone.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_commit.js b/lib/command_commit.js index 250bad8..27c936b 100644 --- a/lib/command_commit.js +++ b/lib/command_commit.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_merge.js b/lib/command_merge.js index dc9d3fa..30fcb67 100644 --- a/lib/command_merge.js +++ b/lib/command_merge.js @@ -1,7 +1,7 @@ 'use strict'; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_pull.js b/lib/command_pull.js index c9b6917..e236e83 100644 --- a/lib/command_pull.js +++ b/lib/command_pull.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_push.js b/lib/command_push.js index 6caaf19..44a0bdb 100644 --- a/lib/command_push.js +++ b/lib/command_push.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_rebase.js b/lib/command_rebase.js index 72c56d6..60993a5 100644 --- a/lib/command_rebase.js +++ b/lib/command_rebase.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_reset.js b/lib/command_reset.js index a2afc65..12a8c15 100644 --- a/lib/command_reset.js +++ b/lib/command_reset.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_stash.js b/lib/command_stash.js index 066882d..577f804 100644 --- a/lib/command_stash.js +++ b/lib/command_stash.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/lib/command_tag.js b/lib/command_tag.js index c05e09e..5d9f0d4 100644 --- a/lib/command_tag.js +++ b/lib/command_tag.js @@ -2,7 +2,7 @@ var async = require('grunt').util.async; var grunt = require('grunt'); -var ArgUtil = require('../lib/argUtil.js'); +var ArgUtil = require('flopmang'); module.exports = function (task, exec, done) { var argUtil = new ArgUtil(task, [ diff --git a/package.json b/package.json index 7e14442..ab8f0e1 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "gruntplugin" ], "dependencies": { + "flopmang": "0.0.1", "underscore": "^1.6.0", "underscore.string": "^2.3.3" } From 40bb112eb7dd7ca7551a1c146e897077b18c869b Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Mon, 14 Jul 2014 08:59:41 -0700 Subject: [PATCH 11/12] remove unnecessary underscore dependencies and add ranged dependency for flopmang --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index ab8f0e1..fcfd57c 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,6 @@ "gruntplugin" ], "dependencies": { - "flopmang": "0.0.1", - "underscore": "^1.6.0", - "underscore.string": "^2.3.3" + "flopmang": "^0.0.1", } } From 17aef85ecb69cbfb61db70e6192ad3cc59919d4f Mon Sep 17 00:00:00 2001 From: Dylan Wood Date: Mon, 14 Jul 2014 09:05:04 -0700 Subject: [PATCH 12/12] remove trailing comma in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fcfd57c..b0bcac1 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,6 @@ "gruntplugin" ], "dependencies": { - "flopmang": "^0.0.1", + "flopmang": "^0.0.1" } }