Skip to content

Commit

Permalink
add default loader and some api break
Browse files Browse the repository at this point in the history
fixes goraz#2
  • Loading branch information
fzerorubigd committed Aug 15, 2015
1 parent 4105836 commit 609f7f0
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 125 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ before_install:
- go get -v github.com/mattn/goveralls
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
script:
- goveralls -v -service travis-ci -repotoken $COVERALLS_TOKEN || go test -v
- goveralls -v -race -service travis-ci -repotoken $COVERALLS_TOKEN || go test -v
matrix:
allow_failures:
- go: 1.1
124 changes: 98 additions & 26 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ Each config object can has more than one config layer. currently there is 3
layer type is supported.


### File layer
### Default layer

This layer is special layer to set default for configs. usage is simple :

l := onion.NewDefaultLayer()
l.SetDefault("my.daughter.name", "bita")

This layer must be addedbefore all other layer, and defaults must be added
before adding it to onion


File layer

File layer is the basic one.

Expand Down Expand Up @@ -52,6 +63,11 @@ l := onion.NewEnvLayer("PORT", "STATIC_ROOT", "NEXT")
this layer currently dose not support nested variables.


### YOUR layer

Just implement the onion.Layer interface!


## Getting from config

After adding layers to config, its easy to get the config values.
Expand Down Expand Up @@ -100,6 +116,32 @@ func RegisterLoader(l FileLoader)
```
RegisterLoader must be called to register a type loaer

#### type DefaultLayer

```go
type DefaultLayer interface {
Layer
// SetDefault set a default value for a key
SetDefault(string, interface{}) error
// GetDelimiter is used to get current delimiter for this layer. since
// this layer needs to work with keys, the delimiter is needed
GetDelimiter() string
// SetDelimiter is used to set delimiter on this layer
SetDelimiter(d string)
}
```

DefaultLayer is a layer to handle defalt 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
layer to onion.

#### type FileLoader

```go
Expand Down Expand Up @@ -174,65 +216,95 @@ 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) Get
#### func (*Onion) Get

```go
func (o Onion) Get(key string) (interface{}, bool)
func (o *Onion) Get(key string) (interface{}, bool)
```
Get try to get the key from config layers

#### func (Onion) GetBool
#### func (*Onion) GetBool

```go
func (o *Onion) GetBool(key string) bool
```
GetBool is used to get a boolean value fro config, with false as default

#### func (*Onion) GetBoolDefault

```go
func (o Onion) GetBool(key string, def bool) bool
func (o *Onion) GetBoolDefault(key string, def bool) bool
```
GetBool return bool value from Onion. if the value is not exists or if tha value
is not boolean, return the default
GetBoolDefault return bool value from Onion. if the value is not exists or if
tha value is not boolean, return the default

#### func (Onion) GetDelimiter
#### func (*Onion) GetDelimiter

```go
func (o Onion) GetDelimiter() string
func (o *Onion) GetDelimiter() string
```
GetDelimiter return the delimiter for nested key

#### func (Onion) GetInt
#### func (*Onion) GetInt

```go
func (o *Onion) GetInt(key string) int
```
GetInt return an int value, if the value is not there, then it return zero value

#### func (*Onion) GetInt64

```go
func (o *Onion) GetInt64(key string) int64
```
GetInt64 return the int64 value from config, if its not there, return zero

#### func (*Onion) GetInt64Default

```go
func (o *Onion) GetInt64Default(key string, def int64) int64
```
GetInt64Default return an int64 value from Onion, if the value is not exists or
if the value is not int64 then return the default

#### func (*Onion) GetIntDefault

```go
func (o Onion) GetInt(key string, def int) int
func (o *Onion) GetIntDefault(key string, def int) int
```
GetInt return an int value from Onion, if the value is not exists or its not an
integer , default is returned
GetIntDefault return an int value from Onion, if the value is not exists or its
not an integer , default is returned

#### func (Onion) GetInt64
#### func (*Onion) GetString

```go
func (o Onion) GetInt64(key string, def int64) int64
func (o *Onion) GetString(key string) string
```
GetInt64 return an int64 value from Onion, if the value is not exists or if the
value is not int64 then return the default
GetString is for getting an string from conig. if the key is not

#### func (Onion) GetString
#### func (*Onion) GetStringDefault

```go
func (o Onion) GetString(key string, def string) string
func (o *Onion) GetStringDefault(key string, def string) string
```
GetString get a string from Onion. if the value is not exists or if tha value is
not string, return the default
GetStringDefault get a string from Onion. if the value is not exists or if tha
value is not string, return the default

#### func (Onion) GetStringSlice
#### func (*Onion) GetStringSlice

```go
func (o Onion) GetStringSlice(key string) []string
func (o *Onion) GetStringSlice(key string) []string
```
GetStringSlice try to get a slice from the config

#### func (Onion) GetStruct
#### func (*Onion) GetStruct

```go
func (o Onion) GetStruct(prefix string, s interface{})
func (o *Onion) GetStruct(prefix string, s interface{})
```
GetStruct fill an structure base on the config nested set
GetStruct fill an structure base on the config nested set, this function use
reflection, and its not good (in my opinion) for frequent call. but its best if
you need the config to loaded in structure and use that structure after that.

#### func (*Onion) SetDelimiter

Expand Down
100 changes: 100 additions & 0 deletions default_layer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package onion

import (
"errors"
"strings"
)

// DefaultLayer is a layer to handle defalt value for layer.
type DefaultLayer interface {
Layer
// SetDefault set a default value for a key
SetDefault(string, interface{}) error
// GetDelimiter is used to get current delimiter for this layer. since
// this layer needs to work with keys, the delimiter is needed
GetDelimiter() string
// SetDelimiter is used to set delimiter on this layer
SetDelimiter(d string)
}

type defaultLayer struct {
delimiter string
data map[string]interface{}
}

// Again, the case of two identical function and not convert one to another
func stringSetDefault(k []string, v interface{}, scope map[string]interface{}) error {
if len(k) == 1 {
// this is the key. just set it
scope[k[0]] = v
return nil
}
t, ok := scope[k[0]]
if ok {
// the key is already there. check if its another map?
switch t.(type) {
case map[string]interface{}:
return stringSetDefault(k[1:], v, scope[k[0]].(map[string]interface{}))
case map[interface{}]interface{}:
return interfaceSetDefault(k[1:], v, scope[k[0]].(map[interface{}]interface{}))
default:
return errors.New("the key is not a map")
}
}

scope[k[0]] = make(map[string]interface{})
return stringSetDefault(k[1:], v, scope[k[0]].(map[string]interface{}))
}

func interfaceSetDefault(k []string, v interface{}, scope map[interface{}]interface{}) error {
if len(k) == 1 {
// this is the key. just set it
scope[k[0]] = v
return nil
}
t, ok := scope[k[0]]
if ok {
// the key is already there. check if its another map?
switch t.(type) {
case map[string]interface{}:
return stringSetDefault(k[1:], v, scope[k[0]].(map[string]interface{}))
case map[interface{}]interface{}:
return interfaceSetDefault(k[1:], v, scope[k[0]].(map[interface{}]interface{}))
default:
return errors.New("the key is not a map")
}
}

scope[k[0]] = make(map[string]interface{})
return stringSetDefault(k[1:], v, scope[k[0]].(map[string]interface{}))
}

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

func (dl *defaultLayer) SetDefault(k string, v interface{}) error {
ka := strings.Split(k, dl.GetDelimiter())
return stringSetDefault(ka, v, dl.data)
}

// GetDelimiter return the delimiter for nested key
func (dl defaultLayer) GetDelimiter() string {
if dl.delimiter == "" {
dl.delimiter = "."
}

return dl.delimiter
}

// SetDelimiter set the current delimiter
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
// adding this layer to onion.
func NewDefaultLayer() DefaultLayer {
return &defaultLayer{".", make(map[string]interface{})}
}
78 changes: 78 additions & 0 deletions default_layer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package onion

import (
"testing"

. "github.com/smartystreets/goconvey/convey"
)

func TestDefaultLayer(t *testing.T) {
Convey("Default layer basic test, some kind of coverage bitch :) ", t, func() {
l := NewDefaultLayer()
data, err := l.Load()
So(err, ShouldBeNil)
So(len(data), ShouldEqual, 0)

err = l.SetDefault("layer1", 1)
So(err, ShouldBeNil)
err = l.SetDefault("layer1.layer2", 42)
So(err, ShouldNotBeNil)

err = l.SetDefault("p1.p2", true)
So(err, ShouldBeNil)

err = l.SetDefault("map", make(map[interface{}]interface{}))
So(err, ShouldBeNil)

err = l.SetDefault("map.in", "inside")
So(err, ShouldBeNil)

tmp := make(map[interface{}]interface{})
tmp["data"] = "data"

tmp["map3"] = make(map[interface{}]interface{})
err = l.SetDefault("map.map2", tmp)
So(err, ShouldBeNil)

err = l.SetDefault("map.map2.another.int", 101)
So(err, ShouldBeNil)

err = l.SetDefault("map.map2.map3.int", 100)
So(err, ShouldBeNil)

err = l.SetDefault("map.map2.map3.int.other", 100)
So(err, ShouldNotBeNil)

tmp2 := make(map[string]interface{})
tmp2["data"] = "data"

tmp2["map3"] = make(map[string]interface{})
err = l.SetDefault("map.map5", tmp2)
So(err, ShouldBeNil)

err = l.SetDefault("map.map5.map3.int", 100)
So(err, ShouldBeNil)

err = l.SetDefault("map.map5.map3.int.other", 100)
So(err, ShouldNotBeNil)

So(l.GetDelimiter(), ShouldEqual, ".")
l.SetDelimiter("-")
So(l.GetDelimiter(), ShouldEqual, "-")
So(l.SetDefault("test-delim", 1), ShouldBeNil)
l.SetDelimiter("")
So(l.GetDelimiter(), ShouldEqual, ".")

o := New()
err = o.AddLayer(l)
So(err, ShouldBeNil)

So(o.GetInt("layer1"), ShouldEqual, 1)
So(o.GetBool("p1.p2"), ShouldBeTrue)
So(o.GetString("map.in"), ShouldEqual, "inside")
So(o.GetString("map.map2.data"), ShouldEqual, "data")
So(o.GetInt("map.map2.map3.int"), ShouldEqual, 100)
So(o.GetInt("map.map2.another.int"), ShouldEqual, 101)
So(o.GetInt("test.delim"), ShouldEqual, 1)
})
}
Loading

0 comments on commit 609f7f0

Please sign in to comment.