Use `stderr` instead of `STDERR.puts`

Teach `stderr` to handle a variable number of messages, which means that
we can use it in one place where we were manually invoking
`STDERR.puts`.

Change-Id: If9b0f80b5f9f0e548e52fef61127f2f531e6465f
Reviewed-on: https://gerrit.causes.com/25242
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Make sure we're always dealing with numeric days in `stats`

Change-Id: I6cb63d259a394f7576f60d85748dd1e3e8ce627a
Reviewed-on: https://gerrit.causes.com/25244
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Add first cut of "gerrit" subcommand

This is just a crude first cut. It basically works; you can clone and
update repos with it.

The `--verbose` switch is ignored because I'm setting the value in the
superclass, which instantiates an instance of the subclass, and that
instance variable is obviously not shared. I'll fix that in a later
commit.

I can see this subcommand evolving sub-subcommands of its own in the
future. For example:

  twigg gerrit clone # clone uncloned repos
  twigg gerrit update # update all repos
  twigg gerrit stats # show stats (changesets, comments, reviews etc)

Change-Id: I5970ab2f860ef6dbedded479e1c6e95679cd8838
Reviewed-on: https://gerrit.causes.com/25245
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Teach settings DSL to handle namespaces

This may be more cleverness than is prudent, but this commit enables us
to write things like:

  Config.gerrit.host
  Config.app.bind

instead of alternatives like:

  Config.gerrit_host
  Config.app[:bind]
  Config[:gerrit][:user]

and so on.

Change-Id: Id9a6040102af45b94bd03a2aa93ed1a6b8e31061
Reviewed-on: https://gerrit.causes.com/25246
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Use shorter option names in Twigg::Gerrit#address

Brevity is the soul of wit, as they say.

Change-Id: I87c4fa84de30ed6f7e8d3eba6edc8eb824cf283d
Reviewed-on: https://gerrit.causes.com/25249
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Prepare Command class for proper inheritance

As noted in prior commits, we're not making proper use of inheritance in
the Command subclasses. Basically, there is common code in the
superclass `#initialize` method that sets instance variables, but we
can't access them from the subclasses because they don't call `super`.

This commit paves the way for us to be able to do that by removing all
the stuff from the `Command#initialize` method that depends on instance
methods. We'll then be able to move the remaining stuff into a class
method, which itself will initialize the subclasses, and they'll be able
to call `super` and get access to the instance variables and share the
common initialization without repetition.

To make this happen:

  - move `#usage` down into the Help class, as a faux-sub-subcommand
  - make `#die` a public method, which means we can call it on the
    returned `Help` instance (the alternative of having Help methods
    automatically die once they've done their thing was eschewed in
    favor of having something easy to test)

Change-Id: Ief5188637e7ffd73155475a1533d745d4f71c2a7
Reviewed-on: https://gerrit.causes.com/25251
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Complete the inheritance overhaul for the Command class

This commit completes the work started in the previous commit; it moves
logic out of the Command#initialize method and up into a new Command.run
method. Several support methods were also promoted to class methods.
Added delegation so that the methods that still need to be used as
instance methods delegate to the class methods.

Note: there should be no behavioral change in this commit; it's just a
refactoring. In the next commit I will rework the subclasses to leverage
the functionality of their superclass.

Change-Id: I7288f0884563b6f39aca4916fd1945b4f53357f0
Reviewed-on: https://gerrit.causes.com/25252
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Teach Command subclasses to make use of superclass variables

With this change, the subclasses are making use of variables like
`@debug` and `@verbose` that get set by the superclass. This means that
commands like `twigg gerrit` and `twigg gerrit --verbose` actually have
differentiated behavior now.

Change-Id: I63cc326d753e45d95c8833f700d1ea246affa813
Reviewed-on: https://gerrit.causes.com/25253
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Remove a couple of `#die` calls

One of these we turn into a graceful exit (passing `-h` or `--help`
should just show the help without employing a non-zero exit code).

Another, the `Help#usage` method, we get rid of the `die` call, as the
caller does this for us via a call to `#run!` (which calls `#run` then
`#die`). This is good because it makes the `#usage` method more easily
testable.

Change-Id: I3a0e286e495a4049cd1a501405314337f3cd3d46
Reviewed-on: https://gerrit.causes.com/25254
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Replace an `exit 1` call with a call to `die`

Save a few bytes.

Change-Id: Ia10eaee9e86effb2f7d0b28f5636ec67d6850fa2
Reviewed-on: https://gerrit.causes.com/25255
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Remove unnecessary temporary variable in Command.run

I had originally assigned a value to the `debug` local variable in
`Command.run` to protect it from getting destroyed when calling the
subcommands (ie. which all call `super`, which ends up deleting it from
the args array).

It turns out we don't have to worry about this:

  args.include?('-d') # present
  some_method_which_deletes_an_item_in_args(*args)
  args.include?('-d') # still present

The value survives because the splat effectively creates a new "array",
and when `#some_method_which_deletes_an_item_in_args` mutates the
"array" (really a method parameter list) the original is untouched.

Change-Id: Iee05cef809af743e931713f3d8122dd840a3b24d
Reviewed-on: https://gerrit.causes.com/25256
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Fix a couple of subtle bugs in Settings magic

As clever is the new dumb, it should come as no surprise that my
"magic" in the Settings class had some bugs in it.

Firstly, we were assigning uselessly to the `_namespace` local variable.
This variable was only ever `true`, and passing it down into the
`override_methods!` call was, albeit harmlessly, constructing instance
variable names like `@true__user` instead of the desired `@gerrit_user`.

Secondly, and more nefariously, we observed that accessing values like
`Config.gerrit.user` worked perfectly on the first access, but returned
`nil` for subsequent accesses.

The culprit here was the line:

  return value if value = ...

I am not entirely sure why, but this invalid Ruby, which would normally
explode, was buzzing along quite happily in the context of this method;
perhaps there is some closure magic going on, or, more likely, the fact
that this method is being defined dynamically within a block is
suppressing the normal warning and error behavior.

A simple test shows that Ruby doesn't consider this pattern legit:

  def foo
    return bar if bar = true
  end

This method doesn't return `true`, but rather first warns:

  warning: found = in conditional, should be ==

at parse time, and then raises at runtime:

  NameError: undefined local variable or method `bar' for ...

Change-Id: I69638c9490b0c6b7ae3a36ecbb98f9db47cd2677
Reviewed-on: https://gerrit.causes.com/25257
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Split "gerrit" subcommand functionality into sub-subcommands

Now we have:

  twigg gerrit clone
  twigg gerrit update

instead of the slightly opaque:

  twigg gerrit

which did both of those things.

This is a nicer interface, and will make it trivial for us to expose a
`twigg gerrit stats` command in the future.

Change-Id: Ic2fb2b2efb3c56626298ad33326a1d502456c471
Reviewed-on: https://gerrit.causes.com/25258
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Make all commands handle common args

Make all commands handle the common args (`-d`, `--debug`, `-v`,
`--verbose`) nicely, and update the docs to reflect that fact.

With this change you can do things like:

  twigg help -v -d commands --foo

and it will gracefully accept the debug and verbose switches (although
it doesn't really do anything differently), and correctly ignore the
`--foo` switch, emitting a warning about skipping it.

In updating the docs I used multiple heading levels to make things more
readable, and rewrapped some of the lines for consistency.

Change-Id: I7e5ed654652722a9d523a5ca558d7d76f8472cd1
Reviewed-on: https://gerrit.causes.com/25259
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Replace a Sinatra variable with a Config one

There is really no reason for this one to be a Sinatra "settings"
variable; we may as well just access the Config directly.

As a bonus, it's terser.

Change-Id: I2a3a641527c068cf3647e4798150a2e479bb951d
Reviewed-on: https://gerrit.causes.com/25260
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Remove _namespace hack

We were using a `_namespace: true` key value pair as a signal that a
hash was a namespace and not a mere options hash. We can do this more
elegantly by just seeing if a hash is multi-level.

This means I can delete the comment about it being impossible to ever
have a setting called "_namespace" (although it's unlikely we'd ever
want that).

More importantly, it will come in handy when I remove the limitation
that namespaces cannot be nested. We don't have a specific need for
multi-level nesting yet, but the limitation is arbitrary and the code
will arguably be cleaner without it.

Change-Id: I521ef4372b382dded5a27f60e893d165c557cbcf
Reviewed-on: https://gerrit.causes.com/25261
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Add support for nested Settings namespaces

Pleasingly, this change doesn't even require us to modify the code that
runs at init time. All we have to do is replace our uni-level instance
variable with a stack, and prepare the nested hashes accordingly. This
will even support re-opening namespaces and adding additional settings
to them, if we ever want to.

There may be an element of YAGNI in all this, but I think the resulting
code is cleaner and more congruent.

Change-Id: I8d081cb5a694d82f32e4fb13972a9e12c56d1d3e
Reviewed-on: https://gerrit.causes.com/25262
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Extract Settings#namespace? method for readability

This method is only used in one place, so it is not being extracted for
re-use but rather for readability. The `#override_methods!` method is
relatively dense, so replacing some of the internal complexity with a
higher-level abstraction makes it easier to grok.

Change-Id: I00f749191fd67ef36629c1ca4a6905a42f747b04
Reviewed-on: https://gerrit.causes.com/25263
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Add `init` subcommand

This subcommand emits the sample YAML configuration file to standard
out, where it can be inspected or redirected to its final destination.

I considered adding bells and whistles, such as the ability to pass a
target filename (or alternatively a flag or special value like `-` to
make the subcommand emit to standard out) and a `-f`/`--force` switch
to overwrite existing files, but simplicity must win out here; there is
little sense in reimplementing shell features (`twigg init > ~/.twiggrc`
does the job well enough).

Change-Id: Ie7032daa6bf7fc5746f17a8c0c0cc1e180aa8f0c
Reviewed-on: https://gerrit.causes.com/25264
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>
Replace File.dirname(__FILE__) calls with __dir__

As we're on Ruby 2.0, the latter is a convenient shortcut which we
should use. Note I do not use it in the gemspec, because it is possible
that that may be evaluated by an older version of Ruby (for example, an
older Ruby might read this file and blow up on hitting `__dir__` and
before getting to the `required_ruby_version` setting).

Change-Id: I4dfa2b4aad53b1047c322fd40fac5a59babcf59a
Reviewed-on: https://gerrit.causes.com/25265
Reviewed-by: Greg Hurrell <greg@causes.com>
Tested-by: Greg Hurrell <greg@causes.com>