redding/sanford — pull request #120 by jcredding (2014-06-23 16:31:57+00:00)
Title: Add `Process` logic, for running a Sanford server process

This adds the logic related to running a Sanford server process.
This logic replaces the `Manager` logic and will be used by the
CLI. The `Process` class takes a server and sets up the current
ruby process for it when run. This involves optionally daemonizing
the process, managing a PID file, and starting the server. The
process also traps signals for controlling the process after its
running. The server returns a thread when its started and this is
joined to the main thread, so the process will continue forever
until it receives a signal. It is setup to receive `TERM` which
will stop it gracefully, `INT` which will halt it immediately (not
graceful), and `USR2` which will perform a hot restart. To perform
the hot restart, the process runs a `Kernel.exec` which replaces
the current process with the new process defined by the args passed
to the `exec`. The TCP server is only paused when this happens, so
its still accepting connections, just not processing them.

This also requires that the server have a name since the `Process`
logic uses it to name the system process and also in logging. This
is handled in the configuration validation, which is called when
the server is initialized.

This fixes some bad practices in the old `Manager` logic related
to the trapping of signals. The server commands for `stop`, `halt`
and `pause` do not perform any of the logic for actually stopping
the server. They proxy DatTCP which simply writes the command to
a `IO.pipe` and then exits. The original thread running the server
then gets this and handles shutting down the server as needed.
This is a better practice than doing all the processing in the
actual signal traps. I ran into problems with the old logic that
are not present with this implementation.

@kellyredding - Sorry about the largeness of this. Minus the tests for the process, its only about 200 lines. The unit tests for the `Process` are lengthy because there is a lot of `ENV` usage, lots of stubbing for `Signal` and `Kernel` and then there are a number of conditionals to test (daemonize or not, listen with custom ip/port or file descriptor, start with client file descriptors, does it try to restart or not). Ready for review.