worker.Worker Is A Sweet Ass-Abstraction

Part of https://discourse.jujucharms.com/t/read-before-contributing/47, an opinionated guide by William Reade

worker.Worker Is A Sweet Ass-Abstraction

Just two methods: Kill(), and Wait() error. Kill gives control of a
lifetime; Wait informs you of its end; the two are intimately linked,
and indeed sometimes used together, but each in fact stands effectively
alone (it is very common to find Kill and Wait invoked from different
goroutines). The interface binds them together mainly just because you
have to implement both of them to be valuable, even if some particular
clients only actually need you for Kill alone, for example.

You will find yourself implementing a lot of workers, and that the Kill
and Wait methods are the purest one-line boilerplate each:

func (w *Worker) Kill() {
    w.catacomb.Kill(nil)
}

func (w *Worker) Wait() error {
    return w.catacomb.Wait()
}

…and that if you follow the advice on writing workers and config
structs found in the wiki, your constructor will be pretty boilerplatey
itself:

func New(config Config) (*Worker, error) {
    if err := config.Validate(); err != nil {
        return nil, errors.Trace(err)
    }
    worker := &Worker{
        config: config,
        other:  make(someRuntimeFieldPerhaps),
    }
    err := catacomb.Invoke(catacomb.Plan{
        Site: &worker.catacomb,
        Work: worker.loop,
    })
    if err != nil {
        return nil, errors.Trace(err)
    }
    return worker, nil
}

…leaving all the hard and/or situation-specific work to your loop() error
func (and anything else it calls). See go doc ./worker/catacomb for
further discussion of effective worker lifetime management.

Also, when writing workers, use the workertest package. CleanKill,
DirtyKill, CheckKilled and CheckAlive are all one-liners that will
protect you from the consequences of many easy mistakes in SUT or test.