mcmp: make thread-safe, add InheritedValue, refactor Context/Annotation related methods
This commit is contained in:
parent
dedf3a0368
commit
7f9b0d5591
@ -257,7 +257,7 @@ func (cli *SourceCLI) parse(
|
|||||||
pvStrValOk = false
|
pvStrValOk = false
|
||||||
}
|
}
|
||||||
if pOk && !pvStrValOk {
|
if pOk && !pvStrValOk {
|
||||||
ctx := mctx.Annotate(p.Component.Annotated(), "param", key)
|
ctx := mctx.Annotate(p.Component.Context(), "param", key)
|
||||||
return nil, merr.New("param expected a value", ctx)
|
return nil, merr.New("param expected a value", ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ func Populate(cmp *mcmp.Component, src Source) error {
|
|||||||
if !p.Required {
|
if !p.Required {
|
||||||
continue
|
continue
|
||||||
} else if _, ok := pvM[hash]; !ok {
|
} else if _, ok := pvM[hash]; !ok {
|
||||||
ctx := mctx.Annotate(p.Component.Annotated(),
|
ctx := mctx.Annotate(p.Component.Context(),
|
||||||
"param", paramFullName(p.Component.Path(), p.Name))
|
"param", paramFullName(p.Component.Path(), p.Name))
|
||||||
return merr.New("required parameter is not set", ctx)
|
return merr.New("required parameter is not set", ctx)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/mediocregopher/mediocre-go-lib/mctx"
|
"github.com/mediocregopher/mediocre-go-lib/mctx"
|
||||||
)
|
)
|
||||||
@ -28,37 +29,76 @@ type child struct {
|
|||||||
// A new Component, i.e. the root Component in the hierarchy, can be initialized
|
// A new Component, i.e. the root Component in the hierarchy, can be initialized
|
||||||
// by doing:
|
// by doing:
|
||||||
// new(Component).
|
// new(Component).
|
||||||
|
//
|
||||||
|
// Method's on Component are thread-safe.
|
||||||
type Component struct {
|
type Component struct {
|
||||||
|
l sync.RWMutex
|
||||||
|
|
||||||
path []string
|
path []string
|
||||||
parent *Component
|
parent *Component
|
||||||
children []child
|
children []child
|
||||||
|
|
||||||
kv map[interface{}]interface{}
|
kv map[interface{}]interface{}
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetValue sets the given key to the given value on the Component, overwriting
|
// SetValue sets the given key to the given value on the Component, overwriting
|
||||||
// any previous value for that key.
|
// any previous value for that key.
|
||||||
func (c *Component) SetValue(key, value interface{}) {
|
func (c *Component) SetValue(key, value interface{}) {
|
||||||
|
c.l.Lock()
|
||||||
|
defer c.l.Unlock()
|
||||||
if c.kv == nil {
|
if c.kv == nil {
|
||||||
c.kv = make(map[interface{}]interface{}, 1)
|
c.kv = make(map[interface{}]interface{}, 1)
|
||||||
}
|
}
|
||||||
c.kv[key] = value
|
c.kv[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the value which has been set for the given key.
|
func (c *Component) value(key interface{}) (interface{}, bool) {
|
||||||
func (c *Component) Value(key interface{}) interface{} {
|
c.l.RLock()
|
||||||
return c.kv[key]
|
defer c.l.RUnlock()
|
||||||
|
if c.kv == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
value, ok := c.kv[key]
|
||||||
|
return value, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values returns all key/value pairs which have been set via SetValue. The
|
// Value returns the value which has been set for the given key.
|
||||||
// returned map should _not_ be modified.
|
func (c *Component) Value(key interface{}) interface{} {
|
||||||
|
value, _ := c.value(key)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// InheritedValue returns the value which has been set for the given key. It first
|
||||||
|
// looks for the key on the receiver Component. If not found, it will look on
|
||||||
|
// its parent Component, and so on, until the key is found. If the key is not
|
||||||
|
// found on the root Component then false is returned.
|
||||||
|
func (c *Component) InheritedValue(key interface{}) (interface{}, bool) {
|
||||||
|
value, ok := c.value(key)
|
||||||
|
if ok {
|
||||||
|
return value, ok
|
||||||
|
} else if c.parent == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return c.parent.InheritedValue(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns all key/value pairs which have been set via SetValue.
|
||||||
func (c *Component) Values() map[interface{}]interface{} {
|
func (c *Component) Values() map[interface{}]interface{} {
|
||||||
return c.kv
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
|
out := make(map[interface{}]interface{}, len(c.kv))
|
||||||
|
for k, v := range c.kv {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasValue returns true if the given key has had a value set on it with
|
// HasValue returns true if the given key has had a value set on it with
|
||||||
// SetValue.
|
// SetValue.
|
||||||
func (c *Component) HasValue(key interface{}) bool {
|
func (c *Component) HasValue(key interface{}) bool {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
_, ok := c.kv[key]
|
_, ok := c.kv[key]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
@ -69,6 +109,8 @@ func (c *Component) HasValue(key interface{}) bool {
|
|||||||
//
|
//
|
||||||
// If a child of the given name has already been created this method will panic.
|
// If a child of the given name has already been created this method will panic.
|
||||||
func (c *Component) Child(name string) *Component {
|
func (c *Component) Child(name string) *Component {
|
||||||
|
c.l.Lock()
|
||||||
|
defer c.l.Unlock()
|
||||||
for _, child := range c.children {
|
for _, child := range c.children {
|
||||||
if child.name == name {
|
if child.name == name {
|
||||||
panic(fmt.Sprintf("child with name %q already exists", name))
|
panic(fmt.Sprintf("child with name %q already exists", name))
|
||||||
@ -86,6 +128,8 @@ func (c *Component) Child(name string) *Component {
|
|||||||
// Children returns all Components created via the Child method on this
|
// Children returns all Components created via the Child method on this
|
||||||
// Component, in the order they were created.
|
// Component, in the order they were created.
|
||||||
func (c *Component) Children() []*Component {
|
func (c *Component) Children() []*Component {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
children := make([]*Component, len(c.children))
|
children := make([]*Component, len(c.children))
|
||||||
for i := range c.children {
|
for i := range c.children {
|
||||||
children[i] = c.children[i].Component
|
children[i] = c.children[i].Component
|
||||||
@ -97,6 +141,8 @@ func (c *Component) Children() []*Component {
|
|||||||
// or false if this Component was not created via Child (and is therefore the
|
// or false if this Component was not created via Child (and is therefore the
|
||||||
// root Component).
|
// root Component).
|
||||||
func (c *Component) Name() (string, bool) {
|
func (c *Component) Name() (string, bool) {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
if len(c.path) == 0 {
|
if len(c.path) == 0 {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
@ -115,11 +161,14 @@ func (c *Component) Name() (string, bool) {
|
|||||||
// fmt.Printf("%#v\n", grandChild.Path()) // []string{"child", "grandchild"}
|
// fmt.Printf("%#v\n", grandChild.Path()) // []string{"child", "grandchild"}
|
||||||
//
|
//
|
||||||
func (c *Component) Path() []string {
|
func (c *Component) Path() []string {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
return c.path
|
return c.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Component) pathStr() string {
|
func (c *Component) pathStr() string {
|
||||||
path := c.Path()
|
path := make([]string, len(c.path))
|
||||||
|
copy(path, c.path)
|
||||||
for i := range path {
|
for i := range path {
|
||||||
path[i] = strings.ReplaceAll(path[i], "/", `\/`)
|
path[i] = strings.ReplaceAll(path[i], "/", `\/`)
|
||||||
}
|
}
|
||||||
@ -128,14 +177,28 @@ func (c *Component) pathStr() string {
|
|||||||
|
|
||||||
type annotateKey string
|
type annotateKey string
|
||||||
|
|
||||||
// Annotate annotates the given Context with information about the Component.
|
func (c *Component) getCtx() context.Context {
|
||||||
func (c *Component) Annotate(ctx context.Context) context.Context {
|
if c.ctx == nil {
|
||||||
return mctx.Annotate(ctx, annotateKey("componentPath"), c.pathStr())
|
c.ctx = mctx.Annotated(annotateKey("componentPath"), c.pathStr())
|
||||||
|
}
|
||||||
|
return c.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Annotated is a shortcut for `c.Annotate(context.Background())`.
|
// Annotate annotates the Component's internal Context in-place, such that they
|
||||||
func (c *Component) Annotated() context.Context {
|
// will be included in any future calls to the Context method.
|
||||||
return c.Annotate(context.Background())
|
func (c *Component) Annotate(kv ...interface{}) {
|
||||||
|
c.l.Lock()
|
||||||
|
defer c.l.Unlock()
|
||||||
|
c.ctx = mctx.Annotate(c.getCtx(), kv...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns a Context which has been annotated with any annotations from
|
||||||
|
// Annotate calls to this Component, as well as some default annotations which
|
||||||
|
// are always included.
|
||||||
|
func (c *Component) Context() context.Context {
|
||||||
|
c.l.Lock()
|
||||||
|
defer c.l.Unlock()
|
||||||
|
return c.getCtx()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BreadthFirstVisit visits this Component and all of its children, and their
|
// BreadthFirstVisit visits this Component and all of its children, and their
|
||||||
|
@ -52,6 +52,24 @@ func TestComponent(t *T) {
|
|||||||
assertValue(c, "bar", nil),
|
assertValue(c, "bar", nil),
|
||||||
assertValue(child, "bar", 2),
|
assertValue(child, "bar", 2),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assertInheritedValue := func(c *Component, key, expectedValue interface{}) massert.Assertion {
|
||||||
|
val, ok := c.InheritedValue(key)
|
||||||
|
return massert.All(
|
||||||
|
massert.Equal(expectedValue, val),
|
||||||
|
massert.Equal(expectedValue != nil, ok),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that InheritedValue does what it's supposed to
|
||||||
|
massert.Require(t,
|
||||||
|
assertInheritedValue(c, "foo", 1),
|
||||||
|
assertInheritedValue(child, "foo", 1),
|
||||||
|
assertInheritedValue(c, "bar", nil),
|
||||||
|
assertInheritedValue(child, "bar", 2),
|
||||||
|
assertInheritedValue(c, "xxx", nil),
|
||||||
|
assertInheritedValue(child, "xxx", nil),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
func TestBreadFirstVisit(t *T) {
|
func TestBreadFirstVisit(t *T) {
|
||||||
cmp := new(Component)
|
cmp := new(Component)
|
||||||
|
Loading…
Reference in New Issue
Block a user