A command implements
ICommand, gets its command-line metadata from attributes (
Properties of the command become command line parameters if decorated with
ParameterAttribute and all possible features of the parameter (long name, short name, optionality, default value) are provided via constructor with default values.
Implementing our example
The implementation of our commands is pretty straightforward:
Just translate the properties of the
ICommand to the argument object and call the wrapped command.
If the framework was embraced completely, we would implement our commands directly.
Mandatory arguments are the default and optional arguments is a matter of changing the value:
[Parameter("awesome", "a", optional: true)].
Reporting a missing mandatory argument just works:
Any extra, non-mapped arguments will also throw an exception, which is nice, unless that behavior in unwanted.
Declaring the right type for the property takes one very far and flags (boolean properties) are supported OOTB.
Failures, however, are not handled gracefully and the exception flows:
Default values are provided as strings, so expect the same conversion rules.
As one can see from the implementation of the locations parameter of somethin-else, such property is not a collection, but a string property that needs to be parsed into a collection of strings. Unfortunately, out-of-the-box values are converted using
Convert.ChangeTpe() which is very limited and does not support collections and there is no way to inject some custom behavior.
It is not a deal breaker but is something to keep in mind. Fortunately, we can help our users by providing examples of valid values:
Running the program without arguments provides a nice overview:
Which can be extended to the command level by using the -help command argument:
Being an attribute-driven framework, localization becomes a challenge and there is nothing I could see to help us with that. Code is simple enough and well-factored to send a pull request to support resource files and string keys, the same way we can do in other attribute-driven frameworks, such as MVC.
The concept of a command is The central concept of the library, so it fits nicely into our example.
On the other hand, if what we have is single command utility, sticking the sole command after the executable might be confusing.
There is an open issue so it is not far-fetched to assume that a default, single command feature will be added.
Invoking the command is a matter of calling
Go.Run() from your console program entry point.
One nice and not widely announced feature is that it provides a way to hook up your IOC container to create command instances, by implementing the
In our sample, I just did an extremly poor man's DI, but plugging in your favorite container is a breeze:
When using command factories, one just specifies the type with the generic call
GoCommando it is indeed small, useful and focused. The code is extremely well structured and the help is about right (bringing the extra features such as command factories from code samples into the wiki would cherry-top the package).
Definitely worth considering for simple (and not so simple) scenarios.