Skip to content

Commit

Permalink
remove Watch from global (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktong authored Nov 14, 2023
1 parent eb5d538 commit 14617d4
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 97 deletions.
18 changes: 5 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ configuration source(s) (implementation) it actually wants to use. Something lik
func main() {
// Create the global Config that loads configuration
// from embed file system and environment variables.
cfg, err := konf.New(
config, err := konf.New(
konf.WithLoader(
fs.New(config, "config/config.json"),
env.New(env.WithPrefix("server")),
Expand All @@ -40,24 +40,16 @@ configuration source(s) (implementation) it actually wants to use. Something lik
if err != nil {
// Handle error here.
}
konf.SetGlobal(cfg)
// ... other setup code ...
}
```

Application also can watch the changes of configuration like:

```
func main() {
// ... setup global Config ...
// Watch the changes of configuration.
go func() {
if err := konf.Watch(ctx); err != nil {
if err := config.Watch(ctx); err != nil {
// Handle error here.
}
}
konf.SetGlobal(config)
// ... other setup code ...
}
```
Expand Down
24 changes: 6 additions & 18 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
package konf_test

import (
"context"
"sync/atomic"
"testing"

"github.com/ktong/konf"
Expand Down Expand Up @@ -42,27 +40,17 @@ func BenchmarkGet(b *testing.B) {
assert.Equal(b, "v", value)
}

func BenchmarkWatch(b *testing.B) {
watcher := mapWatcher(make(chan map[string]any))
config, err := konf.New(konf.WithLoader(watcher))
func BenchmarkUnmarshal(b *testing.B) {
config, err := konf.New(konf.WithLoader(mapLoader{"k": "v"}))
assert.NoError(b, err)
konf.SetGlobal(config)

assert.Equal(b, "string", konf.Get[string]("config"))
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
assert.NoError(b, konf.Watch(ctx))
}()
b.ResetTimer()

var cfg atomic.Value
konf.OnChange(func() {
cfg.Store(konf.Get[string]("config"))
})
var value string
for i := 0; i < b.N; i++ {
watcher.change(map[string]any{"config": "changed"})
_ = konf.Unmarshal("k", &value)
}
b.StopTimer()
assert.Equal(b, "changed", cfg.Load())

assert.Equal(b, "v", value)
}
58 changes: 29 additions & 29 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,38 @@ func TestConfig_Unmarshal(t *testing.T) {
{
description: "empty values",
assert: func(config konf.Config) {
var cfg string
assert.NoError(t, config.Unmarshal("config", &cfg))
assert.Equal(t, "", cfg)
var value string
assert.NoError(t, config.Unmarshal("config", &value))
assert.Equal(t, "", value)
},
},
{
description: "nil loader",
opts: []konf.Option{konf.WithLoader(nil)},
assert: func(config konf.Config) {
var cfg string
assert.NoError(t, config.Unmarshal("config", &cfg))
assert.Equal(t, "", cfg)
var value string
assert.NoError(t, config.Unmarshal("config", &value))
assert.Equal(t, "", value)
},
},
{
description: "for primary type",
opts: []konf.Option{konf.WithLoader(mapLoader{"config": "string"})},
assert: func(config konf.Config) {
var cfg string
assert.NoError(t, config.Unmarshal("config", &cfg))
assert.Equal(t, "string", cfg)
var value string
assert.NoError(t, config.Unmarshal("config", &value))
assert.Equal(t, "string", value)
},
},
{
description: "config for struct",
opts: []konf.Option{konf.WithLoader(mapLoader{"config": "struct"})},
assert: func(config konf.Config) {
var cfg struct {
var value struct {
Config string
}
assert.NoError(t, config.Unmarshal("", &cfg))
assert.Equal(t, "struct", cfg.Config)
assert.NoError(t, config.Unmarshal("", &value))
assert.Equal(t, "struct", value.Config)
},
},
{
Expand All @@ -71,9 +71,9 @@ func TestConfig_Unmarshal(t *testing.T) {
),
},
assert: func(config konf.Config) {
var cfg string
assert.NoError(t, config.Unmarshal("config.nest", &cfg))
assert.Equal(t, "string", cfg)
var value string
assert.NoError(t, config.Unmarshal("config.nest", &value))
assert.Equal(t, "string", value)
},
},
{
Expand All @@ -89,9 +89,9 @@ func TestConfig_Unmarshal(t *testing.T) {
),
},
assert: func(config konf.Config) {
var cfg string
assert.NoError(t, config.Unmarshal("config_nest", &cfg))
assert.Equal(t, "string", cfg)
var value string
assert.NoError(t, config.Unmarshal("config_nest", &value))
assert.Equal(t, "string", value)
},
},
{
Expand All @@ -106,9 +106,9 @@ func TestConfig_Unmarshal(t *testing.T) {
),
},
assert: func(config konf.Config) {
var cfg string
assert.NoError(t, config.Unmarshal("config.nest", &cfg))
assert.Equal(t, "", cfg)
var value string
assert.NoError(t, config.Unmarshal("config.nest", &value))
assert.Equal(t, "", value)
},
},
}
Expand Down Expand Up @@ -139,24 +139,24 @@ func TestConfig_Watch(t *testing.T) {
config, err := konf.New(konf.WithLoader(watcher))
assert.NoError(t, err)

var cfg string
assert.NoError(t, config.Unmarshal("config", &cfg))
assert.Equal(t, "string", cfg)
var value string
assert.NoError(t, config.Unmarshal("config", &value))
assert.Equal(t, "string", value)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
assert.NoError(t, config.Watch(ctx))
}()

var newCfg atomic.Value
var newValue atomic.Value
config.OnChange(func(unmarshaler konf.Unmarshaler) {
var cfg string
assert.NoError(t, config.Unmarshal("config", &cfg))
newCfg.Store(cfg)
var value string
assert.NoError(t, config.Unmarshal("config", &value))
newValue.Store(value)
})
watcher.change(map[string]any{"config": "changed"})
assert.Equal(t, "changed", newCfg.Load())
assert.Equal(t, "changed", newValue.Load())
}

type mapWatcher chan map[string]any
Expand Down
3 changes: 1 addition & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
// and reloads latest configuration when it has changes from a Watcher interface.
//
// Config has following main methods:
// - Config.Watch reloads configuration when it changes.
// - Config.Unmarshal loads configuration under the given path
// into the given object pointed to by target.
// - Config.Watch reloads configuration when it changes.
// - Config.OnChange register callback on configuration changes.
//
// # Global Config
Expand All @@ -26,6 +26,5 @@
// It returns zero value if there is an error while getting configuration.
// - Unmarshal loads configuration under the given path
// into the given object pointed to by target.
// - Watch reloads configuration when it changes.
// - OnChange register callback on configuration changes.
package konf
12 changes: 0 additions & 12 deletions global.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package konf

import (
"context"
"log/slog"
"reflect"
"sync"
Expand Down Expand Up @@ -46,17 +45,6 @@ func Unmarshal(path string, target any) error {
return global.Unmarshal(path, target)
}

// Watch watches and updates configuration when it changes.
// It blocks until ctx is done, or the service returns an error.
//
// It only can be called once. Call after first has no effects.
func Watch(ctx context.Context) error {
mux.RLock()
defer mux.RUnlock()

return global.Watch(ctx)
}

// OnChange executes the given onChange function while the value of any given path
// (or any value is no paths) have been changed.
//
Expand Down
23 changes: 0 additions & 23 deletions global_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ package konf_test

import (
"bytes"
"context"
"log"
"sync/atomic"
"testing"

"github.com/ktong/konf"
Expand Down Expand Up @@ -51,24 +49,3 @@ func TestGet_error(t *testing.T) {
" path=config type=bool\n"
assert.Equal(t, expected, buf.String())
}

func TestWatch(t *testing.T) {
watcher := mapWatcher(make(chan map[string]any))
config, err := konf.New(konf.WithLoader(watcher))
assert.NoError(t, err)
konf.SetGlobal(config)
assert.Equal(t, "string", konf.Get[string]("config"))

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
assert.NoError(t, konf.Watch(ctx))
}()

var cfg atomic.Value
konf.OnChange(func() {
cfg.Store(konf.Get[string]("config"))
})
watcher.change(map[string]any{"config": "changed"})
assert.Equal(t, "changed", cfg.Load())
}

0 comments on commit 14617d4

Please sign in to comment.