lotus/lotus — pull request #39 by jodosha (2014-07-03 08:48:26+00:00)
Title: Environment

Introducing `Lotus::Environment` as abstraction for Lotus env vars and for the options that the CLI receive.
When initialized, it sets the following env vars:

* `LOTUS_ENV` and `RACK_ENV`
* `LOTUS_HOST`
* `LOTUS_PORT`

It should be used by all the CLI commands and by `Lotus::Configuration`.

**Use case:**
When we use `lotus server --port=4000 --host=testhost.org` we expect that the application's router respect those settings.

**Constraints:**
We want the application to be _self loadable_. In every place when we do `Bookshelf::Application.new` the application should be ready to be run. Think of a simple `config.ru` where we require the app and we just write `run Bookshelf::Application.new` and it works.

**Alternative A (current implementation):**
_Scenarios_:

* When the application is started via `lotus server/console`, we initialize `Lotus::Environment` which reads CLI options and sets the env vars. Once they're set, a new instance of `Lotus::Environment` is created by `Lotus::Configuration` and we can access those values.

* When the application is started via another command (eg. `rackup` or `puma`), we don't have direct access to the CLI options, but we can read the env vars from `Lotus::Configuration` via `Lotus::Environment`.

* When the application is started via another command, and the env vars aren't set, we rely on the manual configuration (eg. `Bookshelf::Application.configure { port 1234 }`), otherwise we fallback to the default values.

_Pros:_

* We keep the constraints satisfied

_Cons:_

* We communicate via env vars

**Alternative B:**
_Scenarios_:

* When the application is started via `lotus server/console`, we read the env vars and the CLI options and we inject a complete configuration into `Bookshelf::Application.new(configuration)`

* When the application is started via external commands (eg `rackup` or `puma`), it relies on the correct manual `config.ru` configuration.

_Pros:_

* We communicate via message passing (pure OOP)

_Cons:_

* The constraints aren't satisfied because an application always need external loading mechanisms before to be instantiated.

* It complicates `config.ru` setup. Even if we generate that file, it may require future maintenance if the loading mechanisms will change. Better to protect developers from those changes.

* It requires complex tests setups every time we want to unit test an application

* When we start `lotus server/console` we don't know yet the `Lotus::Application` subclasses. We should implement a registry in order to instantiate applications. Because Ruby's `require` isn't thread safe, in multithreading environments like JRuby, Rubinius, it may be lead to subtle bugs.