RadiusNetworks/radius-toolbelt — pull request #1 by cupakromer (2014-09-30 19:49:26+00:00)
Title: Commander for the CLI

Use [`commander`](https://github.com/visionmedia/commander/) for the CLI interface.

We looked at [`Thor`](https://github.com/erikhuda/thor) and [`GLI`](https://github.com/davetron5000/gli/) as well. We've had poor experiences with Thor in the past so that was taken off the list. GLI was next up. We decided it was a bit too heavy weight for us. We would have ended up moving lots of parts around to namespace things cleanly.

At that point we considered how we wanted things to work. We spiked a simple interface (ended up being a DSL). The resulting code looked very much like how commander is setup. Instead of re-inventing the wheel, we decided to just use `Commander` for the CLI.

In an effort to not include all of the Commander DSL on `main` we chose to **not** go with the ["Classic Style"](https://github.com/visionmedia/commander/#classic-style). We could have gone with the ["Block Style"](https://github.com/visionmedia/commander/#block-style) inside the script, which is probably how Commander _should_ be used, but it also doesn't fit well with us packaging this as a CLI toolset. This left the ["Modular Style"](https://github.com/visionmedia/commander/#modular-style).

It felt very strange to define all of the "CLI class" properties in the `run` method. One one level, this makes some sense. You could pass in all sorts of options, or even sets of commands, to then configure the CLI. In our case, most of these properties are relatively static. It "feels" more correct (for now) that these are defined at the CLI class level.

Ideally, defining these at the class level would mean instances of said class having all of those properties / actions set. Unfortunately, this is not how Commander works. It's DSL can be defined ANYWHERE. Ever place it is defined does the same thing. They all [delegate back](https://github.com/visionmedia/commander/blob/v4.2.1/lib/commander/delegates.rb#L8-L10) to the same static [singleton](https://github.com/visionmedia/commander/blob/v4.2.1/lib/commander/runner.rb#L42-L44)! :rage:

Since :wrench: fixing Commander is out of scope for now, we are just going to go with the class level implementation and try to keep it compartmentalized as much as possible.

## Notes

- `sbin` is where binstubs are installed on CI

Hosting our gem's executable in `bin` causes bundler binstub problems. When running `--binstubs` with bundler it will overwrite the gem's executable (`bin/radius`); causing a self referencing loop. To handle we can:

- Manually create the binstubs we want `bundle binstubs rspec-core`; however this doesn't preserve our `--standalone` flag :disappointed:
- Tell binstubs to install into another directory

We'll go with the later and use `sbin` for `stubbed bin`. Locally, feel free to use `--binstubs sbin` and then make symlinks in `bin` to them to make your workflow the same.

- Feature specs

These are both workflow and full end-to-end integration / acceptance specs. To handle shelling out, but not polluting RSpec output, we use [`Open3`](http://ruby-doc.org/stdlib-2.1.3/libdoc/open3/rdoc/Open3.html). Since we'll often have to run the same things, it is all wrapped up in a convenient helper under [`spec/support/subcommand.rb`](spec/support/subcommand.rb).

One issue that came up is that the output with command is both indented AND uses terminal colors. I couldn't find any way to configure these and turn them off. Normally in testing you do care about whitespace, but you don't usually care about terminal colors. To preserve when we want colors we can get them with `Subcommand#raw_stdout`. Otherwise `Subcommand#stdout` filters them out. It does not however filter out the space padding. If you want to get rid of that, use `strip_heredoc` which is defined in [`spec/support/string.rb`](spec/support/string.rb) if it isn't already defined.