Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options Parser #118

Open
mollymorphic opened this issue Jun 19, 2014 · 15 comments
Open

Options Parser #118

mollymorphic opened this issue Jun 19, 2014 · 15 comments

Comments

@mollymorphic
Copy link
Contributor

Higgs allows users to pass arguments to scripts by including them after "--", like so:

./higgs script.js -- foo bar

However, this just provides an array of strings (in this case ["foo", "bar"]). Higgs should include a library that makes it easy for scripts to declare what arguments and options they take, which ones are required, etc. The library should then take an argument array like:

["plain arg", "second plain arg", "--opt", "value", "--secondopt", "value", "value"]

and produce an array of the arguments like:

["plain arg", "second plain arg"]

as well as an opts object like

{
    "opt" : "value",
    "secondopt" : ["value", "value"]
}

If all required args/options are not present an error should be thrown and/or a message displayed and the program exited.

A possible enhancement would be to take an optional description of what each option does, and use this to construct a "usage" message to display when no/incorrect args/opts are passed or the "-help" option is encountered.

Another possible enhancement would be to take an optional type for each arg/opt and then convert the argument/opt value to that type before passing it along to the library user, or an optional "converter function" to be applied to the opt/arg before passing it on to the library user.

Feedback for possible extra features, ideas for what the API should look like, and any other general thoughts are very welcome (^_^).

@sbstp
Copy link
Member

sbstp commented Jun 20, 2014

Well the pocoo guys recently released a pretty awesome CLI API in Python. I think it's one of the cleanest APIs I've seen, but it makes heavy usage of annotations which are not available in JavaScript. One attempt at function annotation I've seen (in JavaScript) was in Angular.js. You can annotate using one of the following methods, array wrapper:

var annotated = [annotation1, annotation2, annotation3, funcToAnnotate]

and properties on function:

funcToAnnotate.annotation1 = "..."
funcToAnnotate.annotation2 = "..."
// etc
function funcToAnnotate() {
}

I think the second approach might be able to produce pretty clean results.

/**
 * ex: ls -l Folder/
 * options: { l: true }
 * text: "Folder/"
 */
function cmd(options, text) {

}
cmd.help = 'List stuff in a folder.'
cmd.options = {
 '-l': {
    help: 'Give more details.'
  }
}
// call the cli API with the args and the annotated function.
cli(args, cmd);

It could also be done with jQuery style chaining around a method, like so:

var cmd = command(function (options, text) {
})
  .help('List stuff in a folder.')
  .option('-l', 'Gives more details', { extra: information })
// or
var cmd = command()
  .help('List stuff in a folder.')
  .option('-l', 'Gives more details', { extra: information })
  .exec(function (options, text) {
  })
// or
var cmd = command()
  .help('List stuff in a folder.')
  .option('-l', 'Gives more details', { extra: information })
{options,text} = cmd.exec(args)

@mollymorphic
Copy link
Contributor Author

Nice, some good ideas, thanks @sbstp (^_^).

I think to start with we want something pretty simple; we can always build it up or have another lib later. So, I prefer the latter style over trying to decorate function objects for now. I don't think we need to handle subcommands and stuff either yet,we can just let the user do it.

So to expand on this with a more concrete example, maybe something like this:

function main(args, opts)
{
    // args is an object
    var op = args.op;
    var numbers = args.numbers;
    var initValue = 0;
    var fun;
    // user has to do some validation
    if (op === "plus")
        fun = function(a, b){ return a + b};
    else if (op === "minus")
        fun = function(a, b){ return a - b};
    else
        throw "Expected \'plus\' or \'minus\'";

    // opts is an object
    if ("initValue" in opts)
        initValue = Number(opts.initValue);

    // numbers is an array
    var result = numbers.reduce(fun, initValue);
    print(result);
}

optParser()
    .desc("A program to do something with numbers")
    .arg("op", {
        required : true,
        desc : "The operation to apply."
    })
    // note the use of ... to consume the rest of the args
    .arg("numbers...", {
        required : true,
        desc : "A list of numbers to operate on."
    })
    .opt("initValue", {
        desc : "an initial value."
    })
    .exec(global.arguments, main);

Later we could add more advanced features like being able to provide a validation function or list of good values for arguments, figuring out valid subcommand combos, checking/converting types for arguments.

Whoever wants to tackle this can make the final call on how fancy they want to make it and what names they want to give everything, but it should provide a simple api for simple cases and only get more complex if needed.

@maximecb
Copy link
Contributor

Not sure that I love the chaining notation, but that's not really relevant. You can support that notation without forcing people to use it. Having desc strings seems like a good idea. I'd suggest naming the library "options", not just "opt".

@mollymorphic
Copy link
Contributor Author

@maximecb yea, should be easy to have it support just passing in an object that describes all the options and everything

@yawnt
Copy link
Contributor

yawnt commented Sep 21, 2014

i think higgs itself should not include an option parser, as this sounds too "high level" to me.. i'd rather see higgs with a js core with just barebone functionalities and an officially supported separate package providing higher-level abstractions (akin to ruby's or go's stdlib) :)

@maximecb
Copy link
Contributor

The options parser is going to be in a library.

@maximecb
Copy link
Contributor

I'd like to maybe add "shebang" mode to Higgs as well as command-line mode. I'm guessing this might affect the options parser a bit.

@maximecb
Copy link
Contributor

I added the shebang mode support a little while back: 165b3f8

@maximecb
Copy link
Contributor

Since @sbstp contributed an options parser, should we close this issue for now, reopen it later?

@sbstp
Copy link
Member

sbstp commented Apr 11, 2015

There were still a few questions about the API (the error was fixed, but not the API). I could fix that before we close this issue.

@maximecb
Copy link
Contributor

Which questions do you feel still need to be resolved?

@sbstp
Copy link
Member

sbstp commented Apr 11, 2015

I created the singleton, but the way I created it conflicted with the way module are imported. Every time you'd require('lib/options.js') you'd get a warning because I modify the exports variable. So I'm not sure if I should rewrite the way the singleton is implemented or if we should remove the warning from the require function.

@maximecb
Copy link
Contributor

Why modify the exports variable? The way modules work now, the library will only be loaded/initialized once.

@sbstp
Copy link
Member

sbstp commented Apr 12, 2015

It was designed to be a constructor, so using it to instantiate an object and assigning it to exports way really easy. It's also a common pattern in the node world. I could easily drop the constructor, though.

@maximecb
Copy link
Contributor

Do eet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants