config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
package config_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
. "github.com/github/git-lfs/config"
|
2016-08-04 21:43:36 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestEnvironmentGetDelegatesToFetcher(t *testing.T) {
|
2016-08-05 00:58:43 +00:00
|
|
|
fetcher := MapFetcher(map[string]string{
|
|
|
|
"foo": "bar",
|
|
|
|
})
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
|
2016-08-05 00:58:43 +00:00
|
|
|
env := EnvironmentOf(fetcher)
|
2016-08-05 21:59:57 +00:00
|
|
|
val, ok := env.Get("foo")
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
|
2016-08-05 21:59:57 +00:00
|
|
|
assert.True(t, ok)
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
assert.Equal(t, "bar", val)
|
|
|
|
}
|
|
|
|
|
2016-08-05 21:59:57 +00:00
|
|
|
func TestEnvironmentUnsetBoolDefault(t *testing.T) {
|
|
|
|
env := EnvironmentOf(MapFetcher(nil))
|
|
|
|
assert.True(t, env.Bool("unset", true))
|
|
|
|
}
|
|
|
|
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
func TestEnvironmentBoolTruthyConversion(t *testing.T) {
|
|
|
|
for _, c := range []EnvironmentConversionTestCase{
|
|
|
|
{"", false, GetBoolDefault(false)},
|
|
|
|
|
|
|
|
{"true", true, GetBoolDefault(false)},
|
|
|
|
{"1", true, GetBoolDefault(false)},
|
|
|
|
{"on", true, GetBoolDefault(false)},
|
|
|
|
{"yes", true, GetBoolDefault(false)},
|
|
|
|
{"t", true, GetBoolDefault(false)},
|
|
|
|
|
|
|
|
{"false", false, GetBoolDefault(true)},
|
|
|
|
{"0", false, GetBoolDefault(true)},
|
|
|
|
{"off", false, GetBoolDefault(true)},
|
|
|
|
{"no", false, GetBoolDefault(true)},
|
|
|
|
{"f", false, GetBoolDefault(true)},
|
|
|
|
} {
|
|
|
|
c.Assert(t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEnvironmentIntTestCases(t *testing.T) {
|
|
|
|
for _, c := range []EnvironmentConversionTestCase{
|
|
|
|
{"", 1, GetIntDefault(1)},
|
|
|
|
|
|
|
|
{"1", 1, GetIntDefault(0)},
|
|
|
|
{"3", 3, GetIntDefault(0)},
|
|
|
|
|
|
|
|
{"malformed", 7, GetIntDefault(7)},
|
|
|
|
} {
|
|
|
|
c.Assert(t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type EnvironmentConversionTestCase struct {
|
|
|
|
Val string
|
|
|
|
Expected interface{}
|
|
|
|
|
2016-08-15 19:17:11 +00:00
|
|
|
GotFn func(env Environment, key string) interface{}
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2016-08-15 19:17:11 +00:00
|
|
|
GetBoolDefault = func(def bool) func(e Environment, key string) interface{} {
|
|
|
|
return func(e Environment, key string) interface{} {
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
return e.Bool(key, def)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 19:17:11 +00:00
|
|
|
GetIntDefault = func(def int) func(e Environment, key string) interface{} {
|
|
|
|
return func(e Environment, key string) interface{} {
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
return e.Int(key, def)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c *EnvironmentConversionTestCase) Assert(t *testing.T) {
|
2016-08-05 00:58:43 +00:00
|
|
|
fetcher := MapFetcher(map[string]string{
|
|
|
|
c.Val: c.Val,
|
|
|
|
})
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
|
2016-08-05 00:58:43 +00:00
|
|
|
env := EnvironmentOf(fetcher)
|
config: demote Fetcher, introduce Environment
Previously, to fetch data out of the `*config.Configuration` type, a reference
to a `Fetcher` was used, a-la:
```
cfg.Env.Get(...)
```
This is quite convenient, however, it forces the LFS client to implement
several methods more than once. Consider the interface:
```
type Fetcher interface {
Get(key string) (val string)
Bool(key string, def bool) (val bool)
// et. al.
}
```
In order to return typed information from a configuration instance, _each_
`Fetcher` must implement its own `N` methods for `Int`, `Bool`, etc.
To remedy this, the `Environment` type was introduced. It instead _has_ a
`Fetcher`, and defines its own type conversions, like so:
```
type Environment struct {
f Fetcher
}
func (e *Environment) Bool(key string, def bool) (val bool) { }
func (e *Environment) Int(key string, def int) (val int) { }
// et. al.
```
Now, the `config.Configuration` type holds a reference to an `Environment`, and
all type conversion methods are defined only once, saving time, and enforcing
consistency across multiple sources.
2016-08-04 20:19:02 +00:00
|
|
|
got := c.GotFn(env, c.Val)
|
|
|
|
|
|
|
|
if c.Expected != got {
|
|
|
|
t.Errorf("lfs/config: expected val=%q to be %q (got: %q)", c.Val, c.Expected, got)
|
|
|
|
}
|
|
|
|
}
|