README.md
# **Yumparse**
A simple, lightweight and flexible command line argument parser.
[![Npm Downloads][downloads-image]][npm-url]
[![Npm Version][npm-image]][npm-url]
[![Test Coverage][wercker-image]][github-url]
[![Code Coverage][coverage-image]][climate-url]
[View the documentation][github-pages-url]
## Basic usage
Yumparse is both simple and very flexible. Here is a simple example:
```js
var yum = require('yumparse');
var parser = new yum.Parser({
// Enter command line options: shortFlag, type, and description are required parameters
options: [
{ shortFlag: '-f',
longFlag: '--foo',
type: String,
description: 'The foo string' }
]
});
parser.parse(); // Parse the options. The parser type checks to see if a String is passed
var fooString = parser.parsedOptions.foo || parser.parsedOptions.f; // Get the option if it was passed
if (fooString) // Do something with the option if it exists
console.log('Hello ' + fooString.value + '!');
```
Save it as `basic.js`, and then in the shell:
```bash
$ node basic.js
$ node basic.js -h
Usage
basic.js [options]
Options
--foo, -f The foo string
$ node basic.js -f "Mr. Bubbles"
Hello Mr. Bubbles!
$ node basic.js -f
/usr/lib/node_modules/yumparse/src/yumparse.js:452
throw new YumparseError(JSON.stringify(option.value) + ' is not
^
YumparseError: undefined is not a string
```
See more examples below or in the **examples** folder from the project's [GitHub page][github-url].
### Changing the error message
Suppose you wanted to pretty up the error message. You could wrap a try block around `parser.parse()`:
```js
try {
parser.parse();
}
catch (error) {
console.error(error.name + ': ' + error.message);
process.exit(1);
}
```
Which would look like this in the shell:
```bash
$ node basic.js -f
YumparseError: undefined is not a string
```
Or you could change the message to your tastes:
```js
try {
parser.parse();
}
catch (error) {
if (error.message.match(/\w is not a string/))
console.error(error.name + ': Please enter a string');
else
console.error(error.name + ': ' + error.message);
process.exit(1);
}
```
```bash
$ node basic.js -f
YumparseError: Please enter a string
```
## Creating a rule
This example introduces the use of **rules**. Some of the power of Yumparse comes from its powerful built-in type checking abilities, but it really shines when **rules** are introduced.
Here is a simple example rule:
```js
var myRule = {
message: function() {
return 'You must pass the flag "-f" with the value "foo"'
},
check: function() {
return this.parsedOptions.f ? this.parsedOptions.f.value === 'foo' : true;
}
}
```
As you can see in this example, the rule checks to see if the flag `-f` has the value `foo`. If it doesn't then it fails the rule and an error is thrown. You can add the rule like this:
```js
parser.addRule(myRule);
```
Yumparse fortunately already comes with several rule *factory* functions that will return a rule object that you can add to your parser. They are available under `yumparse`.`rules`, and they use helper functions from `yumparse`.`helpers`. See the API, JSDoc and source code for more information.
## An example with a rule
The example walks through the built-in rule factory `yumparse`.`rules`.`requiredOrFlags`:
```js
// yumparse.rules.requiredOrFlags
requiredOrFlags: function() {
'use strict';
var args = Array.prototype.slice.call(arguments);
return {
message: function() {
return 'You must pass either ' + helpers.argsToOptions.call(this, args) +
' as a parameter.';
},
check: function() {
return helpers.oneFlagPassed.call(this, args);
}
};
},
```
As you can see, the built-in **rules** from `yumparse`.`rules` are not actually rule objects: they are *factories* that return rule objects. This is so that a user can supply a variable number of `arguments` that will return the rule in the proper format.
The call to `Array.prototype.slice.call(arguments)` converts the function arguments from the `arguments` keyword into an Array. Note also that it calls the helper functions in `message` and `call` with `call(this, args)`, so that the parser instance is available in those functions as `this`.
To learn more about what's going on under the hood, we need to see the helper functions it calls:
```js
// yumparse.helpers.argsToOptions
argsToOptions: function(args, delimiter) {
'use strict';
if (!delimiter) {
delimiter = ' or ';
}
return args.map(function(arg) {
return this.options[this.flagToName(arg)];
}, this)
.reduce(function(previous, current) {
return (previous ? previous + delimiter : '') +
(current ? '['+ current.shortFlag +' | '+ current.longFlag +']' : '');
}, '');
},
// yumparse.helpers.oneFlagPassed
oneFlagPassed: function(flags) {
'use strict';
var numFlagsPassed = 0;
flags.forEach(function(flag) {
var option = this.options[this.flagToName(flag)];
if (option) {
if (this.parsedOptions[option.shortFlagName] !== undefined ||
this.parsedOptions[option.longFlagName] !== undefined) {
numFlagsPassed += 1;
}
}
}, this);
return numFlagsPassed === 1;
},
```
You can see that `argsToOptions` first maps the list of flags to their corresponding `options` objects, and then reduces it into a string that display the short and long forms of the flags.
The `oneFlagPassed` helper function gets the flags and checks to see if one and only one flag is passed from the given list of flags.
So if we call something like `yumparse.rules.requiredOrFlags('-i', '-o')`, it will find and check both the `shortFlag` and `longFlag` values for those flags, and if exactly one of these flags is passed it will return `true`. Otherwise it will return `false`, which is exactly what is needed for an **OR** rule that required a flag to be given.
### Tying it all together
Here is the example that ties it all together. It uses the the built-in `yumparse`.`rules`.`requiredOrFlags` rule:
```js
// temperature.js
var yum = require('yumparse');
var parser = new yum.Parser({
options: [
{ shortFlag: '-v',
longFlag: '--verbose',
type: Boolean,
description: 'Verbose output' },
{ shortFlag: '-f',
longFlag: '--fahrenheit',
type: Number,
description: 'Convert celsius to fahrenheit' },
{ shortFlag: '-c',
longFlag: '--celsius',
type: Number,
description: 'Convert fahrenheit to celsius' },
{ shortFlag: '-k',
longFlag: '--kelvin',
type: Number,
description: 'Convert kelvin to fahrenheit' }
],
program: {
name: 'foobar',
description: 'a very fooish barish bazish program'
}
});
parser.addRule(yum.rules.requiredOrFlags('--fahrenheit', '--celsius', '--kelvin'));
try {
parser.parse();
}
catch (e) {
console.error(e.name + ': ' + e.message);
}
if (parser.parsedOptions.v || parser.parsedOptions.verbose) {
console.log('parser.options:\n', parser.options, '\n');
console.log('parser.parsedOptions:\n', parser.parsedOptions, '\n');
}
var fahrenheit = parser.parsedOptions.f || parser.parsedOptions.fahrenheit;
var celsius = parser.parsedOptions.celsius || parser.parsedOptions.c;
var kelvin = parser.parsedOptions.kelvin || parser.parsedOptions.k;
if (fahrenheit)
console.log((fahrenheit.value - 32) * 5/9 + '° celsius');
else if (celsius)
console.log(celsius.value * 9/5 + 32 + '° fahrenheit');
else if (kelvin)
console.log((kelvin.value - 273.15) * 9/5 + 32 + '° fahrenheit');
```
And then in the shell:
```bash
$ node temperature.js -h
foobar - a very fooish barish bazish program
Usage
foobar [options]
Options
--verbose, -v Verbose output
--fahrenheit, -f Convert celsius to fahrenheit
--celsius, -c Convert fahrenheit to celsius
--kelvin, -k Convert kelvin to fahrenheit
$ node temperature.js -c 0
32° fahrenheit
$ node temperature.js -f 32
0° celsius
$ node temperature.js -k 273.15
31.73000000000004° fahrenheit
$ node temperature.js
YumparseError: You must pass either [-f | --fahrenheit] or [-c | --celsius] or [-k | --kelvin] as a parameter.
```
## The help/usage menu
The help/usage menu is also customizable. The built-in default template looks like this:
```js
this.template = '' +
'{{#program.description}}' +
_B+'{{{program.name}}}'+B_ + ' - {{{program.description}}}\n' +
'{{/program.description}}' +
'\n\n' +
_U+'Usage'+U_+'\n' +
' {{{program.name}}} [options]\n\n' +
_U+'Options'+U_+'\n' +
'{{#optionsList}}' +
' {{{flagBlock}}} {{{description}}}\n' +
'{{/optionsList}}' +
'{{#example}}' +
'\n\n' +
_U+'Example'+U_+'\n' +
' {{{example}}}\n' +
'{{/example}}';
...
this.flagBlock = function() {
return (this.longFlag ?
(new Array(longestFlag - this.longFlag.length + 1)).join(' ') +
this.longFlag + ', ' :
(new Array(longestFlag + 1)).join(' ') + ' ') +
this.shortFlag;
};
```
As you can see, the [Mustache][mustache-url] template has access to the parser instance (`this`). You might wonder what `_B`, `B_`, `_U` and `U_` are: They are xterm colors that refer to opening and closing **bold** and **underline**, respectively.
Feel free the overwrite the template string `parser`.`template` to your liking. You can also append your own Mustache functions (like `this`.`flagBlock`) to the parser by adding the property to your Parser object instance.
## API
### *Module* `yumparse`
##### *Function* `yumparse`.`Parser`(*Object* args)
The parser object. Takes a JSON object `args`:
*String* `args`.`template` - The [Mustache][mustache-url] template to use for the help/usage menu. (*optional*)
*Array* `args`.`options` - The options array that the parser should use when parsing arguments. (*required*)
*Object* **`args`.`options`**:
*String* `args`.`options`.`shortFlag` - The short flag to use for the option (e.g. `'-i'`). (*required*)
*Object* `args`.`options`.`type` - The type to typecheck for. (*required*)
Available options are in *yumparse.flagTypeOptions*: `Boolean`, `Number`, `String`, `Array`, `Object`.
*String* `args`.`options`.`description` - The description of the option (used in the help/usage menu). (*required*)
*String* `args`.`options`.`longFlag` - The long flag to use for the option (e.g. `'--input-file'`). (*optional*)
*Boolean* `args`.`options`.`required` - Set to true to make the option required. (*optional*)
*Object* `args`.`options`.`defaultValue` - The default value for the option. (*optional*)
##### *Object* `yumparse`.`rules`
An alias that fetches the `rules` module. It contains built-in rule *factory* functions that can be used by the parser.
##### *Object* `yumparse`.`helpers`
An alias that fetches the `helpers` module. It contains the helper functions that are used for the built-in rules.
### *Class* `Parser`
##### *Array* `Parser`.`optionsList`
The original list of obtions that were passed into the constructor in its original `Array` form.
##### *Object* `Parser`.`options`
The list of options that were passed into the constructor in `Object` form. Allows for quick access to options by accessing them through their (camel-cased) variable form (e.g. `Parser.options.inputFile` or `Parser.options.i` for the option with flags `-i` and `--input-file`).
##### *Object* `Parser`.`parsedOptions`
The successfully parsed options that the user gave from the command line. Contains a subset of the items in `Parser`.`options`.
##### *Function* `Parser`.`parse`()
Parse the command line parameters from `process`.`argv`. If the function succeeds, it will populate `Parser`.`parsedOptions` with the parsed options. If it fails, it will throw an error.
##### *Function* `Parser`.`addRule`(*Object* rule)
The function that is used to add rules to the rules array. It wraps the rule into a function that throws an error if the rule returns `false` or continues it the rule returns `true`.
*Object* **`rule`**:
*Function* `rule`.`message` -> *String* - A function that creates the user message. `this` refers to the Parser instance.
*Function* `rule`.`check` -> *Boolean* - A checking function that performs the checks for the rule. It should return `true` when it succeeds and `false` otherwise. `this` refers to the Parser instance.
##### *Array* `Parser`.`rules`
A list of rule functions to be checked during parsing. Rules are added using the `Parser`.`addRule` function.
##### *Array* `Parser`.`flagTypeOptions`
The list of types that are available for *args.options.type*: `Boolean`, `Number`, `String`, `Array`, `Object`.
##### *Array* `Parser`.`requiredList`
The list of options from `Parser`.`optionsList` that have the `option`.`required` field set to `true`.
##### *String* `Parser`.`template`
The default [Mustache][mustache-url] template used for the help/usage menu. `this` refers to the Parser instance (e.g. `{{program.name}} - {{program.description}}`, `{{#optionsList}}`, etc).
##### *Function* `Parser`.`flagBlock`() -> *String*
A [Mustache][mustache-url] helper function for formatting the list of options in the help/usage menu. References an option in `Parser`.`optionsList`. Returns a formatted String.
##### *Function* `Parser`.`flagToName`(*String* flag) -> *String*
A helper function that converts a flag name (spinal case) to a variable name (camel case) for accessing options from `Parser`.`options` or `Parser`.`parsedOptions` (e.g. `-i` returns `i`, `--input-file` returns `inputFile`).
##### *Function* `Parser`.`nameToFlag`(*String* name) -> *String*
A helper function that converts a variable name (camel case) to a flag name (spinal case) (e.g. `i` returns `-i`, `inputFile` returns `--input-file`).
##### *Function* `Parser`.`displayHelp`()
Displays the help/usage menu.
##### *Function* `Parser`.`helpString`() -> *String*
Returns the rendered [Mustache][mustache-url] template for the help/usage menu as a String.
### *Object* `yumparse`.`rules`
Contains built-in rule *factory* functions that return a rule object, which can then be passed to `parser`.`addRule`.
##### *Function* **`yumparse`.`rules`.`requiredOrFlags`**(*String* flags...) -> *Object*
A rule *factory* that takes a variable number of Strings in `shortFlag` or `longFlag` format.
Returns a rule that checks *if one and* **exactly** *one flag from the list of flags passed was parsed*.
##### *Function* **`yumparse`.`rules`.`orFlags`**(*String* flags...) -> *Object*
A rule *factory* that takes a variable number of Strings in `shortFlag` or `longFlag` format.
Returns a rule that checks *if zero or* **only** *one flag from the list of flags passed was parsed*. It will **not** fail if no flags are passed.
##### *Function* **`yumparse`.`rules`.`andFlagSets`**(*Array* flagSets...) -> *Object*
A rule *factory* that takes a variable number of Arrays containing a variable number of Strings in `shortFlag` or `longFlag` format.
Returns a rule that checks *if one and* **exactly** *one flag from each array passed was parsed* **and** *a flag from every array passed was parsed*. It can be thought of as a combination of the rules `yumparse`.`rules`.`andFlags` and `yumparse`.`rules`.`requiredOrFlags`.
##### *Function* **`yumparse`.`rules`.`andFlags`**(*String* flags...) -> *Object*
A rule *factory* that takes a variable number of Strings in `shortFlag` or `longFlag` format.
Returns a rule that checks *if* **every** *flag from the list of flags passed was parsed*.
##### *Function* **`yumparse`.`rules`.`fileExists`**(*String* flags...) -> *Object*
A rule *factory* that takes a variable number of Strings in `shortFlag` or `longFlag` format.
Returns a rule that checks *if the* **value** *from each of the list of flags passed is a path to a file that* **exists**.
### *Object* `yumparse`.`helpers`
##### *Function* **`yumparse`.`helpers`.`argsToOptions`**(*String[]* args, *String* delimiter) -> *String*
A helper function that takes a array of flags and a delimiter, and returns a String display of the `shortFlag` and `longFlag` of each of the passed options, delimited by the passed `delimiter` string.
##### *Function* **`yumparse`.`helpers`.`allFlagsPassed`**(*String[]* flags) -> *Boolean*
A helper function that takes an array of flags, and returns `true` if all of those flags were parsed (i.e. they exist in `parser`.`parsedOptions`), and `false` otherwise.
##### *Function* **`yumparse`.`helpers`.`allFlagSetsPassed`**(*String[][]* flagSets) -> *Boolean*
A helper function that takes an array of an array of flags (a flagSet is an array of flag Strings), and returns `true` if exactly one flag from each flagSet was parsed.
##### *Function* **`yumparse`.`helpers`.`oneFlagPassed`**(*String[]* flags) -> *Boolean*
A helper function that takes an array of flags, and returns `true` if one and *exactly* one flag was passed, and `false` otherwise.
##### *Function* **`yumparse`.`helpers`.`fileExists`**(*String[]* flags) -> *Boolean*
A helper function that takes an array of flags, and returns `true` if the *value* from each of the flags is a path to a file that *exists*.
* * *
Copyright © 2014, Risto Stevcev
[coverage-image]: https://img.shields.io/codeclimate/coverage/github/Risto-Stevcev/yumparse.svg?style=flat
[wercker-image]: https://img.shields.io/wercker/ci/549198836b3ba8733d8da6cd.svg?style=flat
[downloads-image]: https://img.shields.io/npm/dm/yumparse.svg?style=flat
[npm-image]: https://img.shields.io/npm/v/yumparse.svg?style=flat
[climate-url]: https://codeclimate.com/github/Risto-Stevcev/yumparse
[npm-url]: https://npmjs.org/package/yumparse
[mustache-url]: https://www.npmjs.com/package/mustache
[github-url]: https://github.com/Risto-Stevcev/yumparse
[github-pages-url]: http://risto-stevcev.github.io/yumparse