Skip to content

Commit

Permalink
Separate the lazy loader interface from the loader interface
Browse files Browse the repository at this point in the history
  • Loading branch information
fzerorubigd committed Jan 29, 2016
1 parent 50fd88b commit a2be21f
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 144 deletions.
49 changes: 35 additions & 14 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Package onion is a layer based, pluggable config manager for golang.


## Layers
### Layers

Each config object can has more than one config layer. currently there is 3
layer type is supported.
Expand Down Expand Up @@ -66,7 +66,7 @@ this layer currently dose not support nested variables.

###YOUR layer

Just implement the onion.Layer interface!
Just implement the onion.Layer or onion.LazyLayer interface!


##Getting from config
Expand Down Expand Up @@ -114,10 +114,15 @@ Also nested struct (and embeded ones) are supported too.

## Usage

### constants
```go
const DefaultDelimiter = "."
```

DefaultDelimiter is the default delimiter for the config scope

### functions

#### func RegisterLoader

```go
Expand All @@ -141,15 +146,15 @@ type DefaultLayer interface {
}
```

DefaultLayer is a layer to handle defalt value for layer.
DefaultLayer is a layer to handle default value for layer.

#### func NewDefaultLayer

```go
func NewDefaultLayer() DefaultLayer
```
NewDefaultLayer is used to return a default layer. shoud load this layer before
any other layer, and before ading it, must add default value before adding this
NewDefaultLayer is used to return a default layer. should load this layer before
any other layer, and before adding it, must add default value before adding this
layer to onion.

#### type FileLoader
Expand All @@ -169,14 +174,9 @@ FileLoader is an interface to handle load config from a file

```go
type Layer interface {
// IsLazy return true if the loader is lazy. if this return false, then
// the Load method is called once.
IsLazy() bool
// Load a layer into the Onion. if this is lazy, then the call is only done in the
// registration, if not, the load method with empty parameter is used to initialize
// the loader
// multiple parameter is for scope. for example database.password means two parameter
Load(string, ...string) (map[string]interface{}, error)
// Load a layer into the Onion. the call is only done in the
// registration
Load() (map[string]interface{}, error)
}
```

Expand All @@ -188,7 +188,7 @@ Layer is an interface to handle the load phase.
func NewEnvLayer(whiteList ...string) Layer
```
NewEnvLayer create a environment loader. this loader accept a whitelist of
allowed variables TODO : find a way to map env variable with different name
allowed variables DEPRECATED : use the extraenv loader

#### func NewFileLayer

Expand All @@ -208,6 +208,19 @@ NewFolderLayer return a new folder layer, this layer search in a folder for all
supported file, and when it hit the first loadable file then simply return it
the config name must not contain file extension

#### type LazyLayer

```go
type LazyLayer interface {
// Get return the value for this config in this layer, if exists, if not return
// false as the 2nd return value
Get(...string) (interface{}, bool)
}
```

LazyLayer is the layer for lazy config sources, when the entire configs is not
available at the registration

#### type Onion

```go
Expand All @@ -232,6 +245,14 @@ func (o *Onion) AddLayer(l Layer) error
AddLayer add a new layer to the end of config layers. last layer is loaded after
all other layer

#### func (*Onion) AddLazyLayer

```go
func (o *Onion) AddLazyLayer(l LazyLayer)
```
AddLazyLayer add a new lazy layer to the end of config layers. last layer is
loaded after all other layer

#### func (*Onion) Get

```go
Expand Down
17 changes: 8 additions & 9 deletions default_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strings"
)

// DefaultLayer is a layer to handle defalt value for layer.
// DefaultLayer is a layer to handle default value for layer.
type DefaultLayer interface {
Layer
// SetDefault set a default value for a key
Expand Down Expand Up @@ -69,11 +69,7 @@ func interfaceSetDefault(k []string, v interface{}, scope map[interface{}]interf
return stringSetDefault(k[1:], v, scope[k[0]].(map[string]interface{}))
}

func (dl *defaultLayer) IsLazy() bool {
return false
}

func (dl *defaultLayer) Load(string, ...string) (map[string]interface{}, error) {
func (dl *defaultLayer) Load() (map[string]interface{}, error) {
return dl.data, nil
}

Expand All @@ -96,9 +92,12 @@ func (dl *defaultLayer) SetDelimiter(d string) {
dl.delimiter = d
}

// NewDefaultLayer is used to return a default layer. shoud load this layer
// before any other layer, and before ading it, must add default value before
// NewDefaultLayer is used to return a default layer. should load this layer
// before any other layer, and before adding it, must add default value before
// adding this layer to onion.
func NewDefaultLayer() DefaultLayer {
return &defaultLayer{".", make(map[string]interface{})}
return &defaultLayer{
delimiter: DefaultDelimiter,
data: make(map[string]interface{}),
}
}
2 changes: 1 addition & 1 deletion default_layer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func TestDefaultLayer(t *testing.T) {
Convey("Default layer basic test, some kind of coverage bitch :) ", t, func() {
l := NewDefaultLayer()
data, err := l.Load(DefaultDelimiter)
data, err := l.Load()
So(err, ShouldBeNil)
So(len(data), ShouldEqual, 0)

Expand Down
14 changes: 7 additions & 7 deletions env_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ type envLoader struct {
data map[string]interface{}
}

func (el *envLoader) IsLazy() bool {
return false
}

func (el *envLoader) Load(string, ...string) (map[string]interface{}, error) {
func (el *envLoader) Load() (map[string]interface{}, error) {
if el.loaded {
return el.data, nil
}
Expand All @@ -31,7 +27,11 @@ func (el *envLoader) Load(string, ...string) (map[string]interface{}, error) {
}

// NewEnvLayer create a environment loader. this loader accept a whitelist of allowed variables
// TODO : find a way to map env variable with different name
// DEPRECATED : use the extraenv loader
func NewEnvLayer(whiteList ...string) Layer {
return &envLoader{whiteList, false, make(map[string]interface{})}
return &envLoader{
whiteList: whiteList,
loaded: false,
data: make(map[string]interface{}),
}
}
4 changes: 2 additions & 2 deletions env_layer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ func TestEnvLayer(t *testing.T) {
Convey("Check if there is anything loaded", func() {
el := NewEnvLayer("TEST1", "test2", "Test3")

data, err := el.Load(DefaultDelimiter)
data, err := el.Load()
So(err, ShouldBeNil)
So(len(data), ShouldEqual, 0)
})

Convey("Check if the variable is loaded correctly", func() {
el := NewEnvLayer("BLACK")

data, err := el.Load(DefaultDelimiter)
data, err := el.Load()
So(err, ShouldBeNil)
So(len(data), ShouldEqual, 1)
So(data["BLACK"], ShouldEqual, "blacklisted")
Expand Down
24 changes: 4 additions & 20 deletions extraenv/env_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,17 @@ type envLoader struct {
prefix string
}

func (el *envLoader) IsLazy() bool {
return true
}

func createNestedMap(v interface{}, p ...string) interface{} {
if len(p) == 0 {
return v
}
return map[string]interface{}{p[0]: createNestedMap(v, p[1:]...)}
}

func (el *envLoader) Load(d string, path ...string) (map[string]interface{}, error) {
if len(path) == 0 {
return nil, nil
}

func (el *envLoader) Get(path ...string) (interface{}, bool) {
p := el.prefix + "_" + strings.ToUpper(strings.Join(path, "_"))
v := os.Getenv(p)
m := make(map[string]interface{})
if v != "" && len(p) > 0 {
m = createNestedMap(v, path...).(map[string]interface{})
return v, true
}
return m, nil
return nil, false
}

// NewExtraEnvLayer create a environment loader. this layer is base on the influxdb config
func NewExtraEnvLayer(prefix string) onion.Layer {
func NewExtraEnvLayer(prefix string) onion.LazyLayer {
return &envLoader{
prefix: strings.ToUpper(prefix),
}
Expand Down
9 changes: 7 additions & 2 deletions extraenv/env_layer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ import (
"os"
"testing"

. "gopkg.in/fzerorubigd/onion.v2"
. "github.com/smartystreets/goconvey/convey"
. "gopkg.in/fzerorubigd/onion.v2"
)

func TestExtraEnvLoader(t *testing.T) {
Convey("Extra env in config", t, func() {
o := New()
layer := NewExtraEnvLayer("test")
o.AddLayer(layer)
o.AddLazyLayer(layer)
Convey("check data from env", func() {
os.Setenv("TEST_DATA_NESTED", "TDN")
So(o.GetString("data.nested"), ShouldEqual, "TDN")
})

Convey("check data not in env", func() {
So(o.GetString("not.valid.data"), ShouldEqual, "")
So(o.GetString(""), ShouldEqual, "")
})

})

}
7 changes: 1 addition & 6 deletions file_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ type fileLayer struct {
data map[string]interface{}
}

// IsLazy for file layer, is always return true
func (fl *fileLayer) IsLazy() bool {
return false
}

// Load a file. also save it's data so the next time it can simply return it
// may be I should remove cache?
func (fl *fileLayer) Load(string, ...string) (map[string]interface{}, error) {
func (fl *fileLayer) Load() (map[string]interface{}, error) {
if fl.loaded {
return fl.data, nil
}
Expand Down
8 changes: 2 additions & 6 deletions flagslayer/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ type flagLayer struct {
data map[string]interface{}
}

func (fl *flagLayer) IsLazy() bool {
return false
}

func (fl *flagLayer) Load(d string, p ...string) (map[string]interface{}, error) {
func (fl *flagLayer) Load() (map[string]interface{}, error) {
if !fl.flags.Parsed() {
fl.flags.Parse(os.Args[1:])
}
Expand All @@ -62,7 +58,7 @@ func (fl *flagLayer) Load(d string, p ...string) (map[string]interface{}, error)
}
}

return inner.Load(d, p...)
return inner.Load()
}

// SetBool set a boolean value
Expand Down
19 changes: 10 additions & 9 deletions folder_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@ type folderLayer struct {
file Layer
}

func (fl *folderLayer) IsLazy() bool {
return false
}

func (fl *folderLayer) Load(d string, p ...string) (map[string]interface{}, error) {
func (fl *folderLayer) Load() (map[string]interface{}, error) {
if fl.loaded {
return fl.file.Load(d, p...)
return fl.file.Load()
}

files, err := filepath.Glob(fl.folder + "/" + fl.configName + ".*")
Expand All @@ -29,7 +25,7 @@ func (fl *folderLayer) Load(d string, p ...string) (map[string]interface{}, erro
for i := range files {
// Try to load each file, until the first one is accepted
fl.file = NewFileLayer(files[i])
data, err := fl.file.Load(d, p...)
data, err := fl.file.Load()
if err == nil {
fl.loaded = true
return data, nil
Expand All @@ -43,10 +39,15 @@ func (fl *folderLayer) Load(d string, p ...string) (map[string]interface{}, erro
// all supported file, and when it hit the first loadable file then simply return it
// the config name must not contain file extension
func NewFolderLayer(folder, configName string) Layer {
// TODO : os specific seperator
// TODO : os specific separator
if folder[len(folder)-1:] != "/" {
folder += "/"
}

return &folderLayer{folder, configName, false, nil}
return &folderLayer{
folder: folder,
configName: configName,
loaded: false,
file: nil,
}
}
Loading

0 comments on commit a2be21f

Please sign in to comment.