config: demote *Environment to interface

In order to support lazily loading the values stored in a user's `.gitconfig`,
we must wait until calling `*config.Configuration.loadGitConfig()` until it is
_absolutely necessary_.

To accomplish this, it was proposed that we introduce a wrapped variant of the
`*Environment` type, only for interacting with the `GitFetcher` that was
capable of supporting such beahvior.

As such, a new implementation of the `Environment` type must be defined. Since
previously there only existed the concrete type `*Environment`, this commit
demotes that down to `*enviornment`, and introduces the interface
`Environment`, which it implements.
This commit is contained in:
Taylor Blau 2016-08-15 13:17:11 -06:00
parent b546ba4db3
commit 6c60d2b2db
3 changed files with 44 additions and 44 deletions

@ -50,14 +50,13 @@ type Configuration struct {
// Os provides a `*Environment` used to access to the system's
// environment through os.Getenv. It is the point of entry for all
// system environment configuration.
Os *Environment
Os Environment
// Git provides a `*Environment` used to access to the various levels of
// `.gitconfig`'s. It is the point of entry for all Git environment
// configuration.
Git *Environment
Git Environment
//
gitConfig map[string]string
CurrentRemote string
@ -187,7 +186,7 @@ func (c *Configuration) Unmarshal(v interface{}) error {
// both is not.
//
// If neither field was found, then a nil environment will be returned.
func (c *Configuration) parseTag(tag reflect.StructTag) (key string, env *Environment, err error) {
func (c *Configuration) parseTag(tag reflect.StructTag) (key string, env Environment, err error) {
git, os := tag.Get("git"), tag.Get("os")
if len(git) != 0 && len(os) != 0 {

@ -11,33 +11,50 @@ import (
// `Environment`s are the primary way to communicate with various configuration
// sources, such as the OS environment variables, the `.gitconfig`, and even
// `map[string]string`s.
type Environment struct {
// Fetcher is the `Environment`'s source of data.
type Environment interface {
// Get is shorthand for calling `e.Fetcher.Get(key)`.
Get(key string) (val string, ok bool)
// Bool returns the boolean state associated with a given key, or the
// value "def", if no value was associated.
//
// The "boolean state associated with a given key" is defined as the
// case-insensitive string comparison with the following:
//
// 1) true if...
// "true", "1", "on", "yes", or "t"
// 2) false if...
// "false", "0", "off", "no", "f", or otherwise.
Bool(key string, def bool) (val bool)
// Int returns the int value associated with a given key, or the value
// "def", if no value was associated.
//
// To convert from a the string value attached to a given key,
// `strconv.Atoi(val)` is called. If `Atoi` returned a non-nil error,
// then the value "def" will be returned instead.
//
// Otherwise, if the value was converted `string -> int` successfully,
// then it will be returned wholesale.
Int(key string, def int) (val int)
}
type environment struct {
// Fetcher is the `environment`'s source of data.
Fetcher Fetcher
}
// EnvironmentOf creates a new `*Environment` initialized with the givne
// EnvironmentOf creates a new `Environment` initialized with the givne
// `Fetcher`, "f".
func EnvironmentOf(f Fetcher) *Environment {
return &Environment{f}
func EnvironmentOf(f Fetcher) Environment {
return &environment{f}
}
// Get is shorthand for calling `e.Fetcher.Get(key)`.
func (e *Environment) Get(key string) (val string, ok bool) {
func (e *environment) Get(key string) (val string, ok bool) {
return e.Fetcher.Get(key)
}
// Bool returns the boolean state associated with a given key, or the value
// "def", if no value was associated.
//
// The "boolean state associated with a given key" is defined as the
// case-insensitive string comparison with the following:
//
// 1) true if...
// "true", "1", "on", "yes", or "t"
// 2) false if...
// "false", "0", "off", "no", "f", or otherwise.
func (e *Environment) Bool(key string, def bool) (val bool) {
func (e *environment) Bool(key string, def bool) (val bool) {
s, _ := e.Fetcher.Get(key)
if len(s) == 0 {
return def
@ -53,16 +70,7 @@ func (e *Environment) Bool(key string, def bool) (val bool) {
}
}
// Int returns the int value associated with a given key, or the value "def",
// if no value was associated.
//
// To convert from a the string value attached to a given key,
// `strconv.Atoi(val)` is called. If `Atoi` returned a non-nil error, then the
// value "def" will be returned instead.
//
// Otherwise, if the value was converted `string -> int` successfully, then it
// will be returned wholesale.
func (e *Environment) Int(key string, def int) (val int) {
func (e *environment) Int(key string, def int) (val int) {
s, _ := e.Fetcher.Get(key)
if len(s) == 0 {
return def

@ -7,13 +7,6 @@ import (
"github.com/stretchr/testify/assert"
)
func TestEnvironmentOfReturnsCorrectlyInitializedEnvironment(t *testing.T) {
fetcher := MapFetcher(map[string]string{})
env := EnvironmentOf(fetcher)
assert.Equal(t, fetcher, env.Fetcher)
}
func TestEnvironmentGetDelegatesToFetcher(t *testing.T) {
fetcher := MapFetcher(map[string]string{
"foo": "bar",
@ -68,18 +61,18 @@ type EnvironmentConversionTestCase struct {
Val string
Expected interface{}
GotFn func(env *Environment, key string) interface{}
GotFn func(env Environment, key string) interface{}
}
var (
GetBoolDefault = func(def bool) func(e *Environment, key string) interface{} {
return func(e *Environment, key string) interface{} {
GetBoolDefault = func(def bool) func(e Environment, key string) interface{} {
return func(e Environment, key string) interface{} {
return e.Bool(key, def)
}
}
GetIntDefault = func(def int) func(e *Environment, key string) interface{} {
return func(e *Environment, key string) interface{} {
GetIntDefault = func(def int) func(e Environment, key string) interface{} {
return func(e Environment, key string) interface{} {
return e.Int(key, def)
}
}