Skip to content

Commit

Permalink
sep. keyed&unkeyed getters
Browse files Browse the repository at this point in the history
  • Loading branch information
Firas Darwish committed Nov 15, 2024
1 parent 393b845 commit bbd07da
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 53 deletions.
16 changes: 8 additions & 8 deletions alias_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ func TestAliasWithDifferentScope(t *testing.T) {

ctx := context.Background()

person, ctx := Get[m.IPerson](ctx, module)
person, ctx := GetKeyed[m.IPerson](ctx, module)
assert.Equal(t, person.(*m.Broker).Name, "Scoped")

personList, _ := GetList[m.IPerson](ctx, module)
personList, _ := GetKeyedList[m.IPerson](ctx, module)
assert.Equal(t, len(personList), 3)
}

Expand Down Expand Up @@ -106,22 +106,22 @@ func TestAliasIsScopedByKeys(t *testing.T) {

ctx := context.Background()

person1, ctx := Get[m.IPerson](ctx, "module1") // will return the m.Broker John
person1, ctx := GetKeyed[m.IPerson](ctx, "module1") // will return the m.Broker John
assert.Equal(t, person1.(*m.Broker).Name, "John1")

personList1, ctx := GetList[m.IPerson](ctx, "module1") // will return all registered m.Broker and m.Trader
personList1, ctx := GetKeyedList[m.IPerson](ctx, "module1") // will return all registered m.Broker and m.Trader
assert.Equal(t, len(personList1), 3)

person2, ctx := Get[m.IPerson](ctx, "module2") // will return the m.Broker John
person2, ctx := GetKeyed[m.IPerson](ctx, "module2") // will return the m.Broker John
assert.Equal(t, person2.(*m.Broker).Name, "John2")

personList2, ctx := GetList[m.IPerson](ctx, "module2") // will return all registered m.Broker and m.Trader
personList2, ctx := GetKeyedList[m.IPerson](ctx, "module2") // will return all registered m.Broker and m.Trader
assert.Equal(t, len(personList2), 2)

person3, ctx := Get[m.IPerson](ctx, "module3") // will return the m.Trader Mary
person3, ctx := GetKeyed[m.IPerson](ctx, "module3") // will return the m.Trader Mary
assert.Equal(t, person3.(*m.Trader).Name, "Mary3")

personList3, ctx := GetList[m.IPerson](ctx, "module3") // will return all registered m.Broker and m.Trader
personList3, ctx := GetKeyedList[m.IPerson](ctx, "module3") // will return all registered m.Broker and m.Trader
assert.Equal(t, len(personList3), 1)

personListNoModule, _ := GetList[m.IPerson](ctx) // will return all registered m.Broker and m.Trader without keys
Expand Down
12 changes: 6 additions & 6 deletions container_keyed_getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import (
"context"
)

// GetFromContainer Retrieves an instance from the given container based on type and key (panics if no valid implementations)
func GetKeyedFromContainer[T any](con *Container, ctx context.Context, key ...KeyStringer) (T, context.Context) {
return getFromContainer[T](con, ctx, key...)
// GetKeyedFromContainer Retrieves an instance from the given container based on type and key (panics if no valid implementations)
func GetKeyedFromContainer[T any](con *Container, ctx context.Context, key KeyStringer) (T, context.Context) {
return getFromContainer[T](con, ctx, key)
}

// GetListFromContainer Retrieves a list of instances from the given container based on type and key
func GetKeyedListFromContainer[T any](con *Container, ctx context.Context, key ...KeyStringer) ([]T, context.Context) {
return getListFromContainer[T](con, ctx, key...)
// GetKeyedListFromContainer Retrieves a list of instances from the given container based on type and key
func GetKeyedListFromContainer[T any](con *Container, ctx context.Context, key KeyStringer) ([]T, context.Context) {
return getListFromContainer[T](con, ctx, key)
}
4 changes: 2 additions & 2 deletions container_unkeyed_getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (

// GetFromContainer Retrieves an instance from the given container based on type and key (panics if no valid implementations)
func GetFromContainer[T any](con *Container, ctx context.Context) (T, context.Context) {
return getFromContainer[T](con, ctx)
return getFromContainer[T](con, ctx, nil)
}

// GetListFromContainer Retrieves a list of instances from the given container based on type and key
func GetListFromContainer[T any](con *Container, ctx context.Context) ([]T, context.Context) {
return getListFromContainer[T](con, ctx)
return getListFromContainer[T](con, ctx, nil)
}

// TODO sep. to a file
Expand Down
6 changes: 3 additions & 3 deletions creator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestRegisterCreatorMultipleImplementationsKeyed(t *testing.T) {

RegisterCreator[interfaces.SomeCounter](registrationType, &models.SimpleCounter{})

counters, _ := GetList[interfaces.SomeCounter](context.Background(), "firas")
counters, _ := GetKeyedList[interfaces.SomeCounter](context.Background(), "firas")

if got := len(counters); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
Expand Down Expand Up @@ -162,15 +162,15 @@ func TestRegisterCreatorTransientState(t *testing.T) {
func TestRegisterCreatorNilKeyOnRegistering(t *testing.T) {
clearAll()
assert.Panics(t, func() {
RegisterKeyedCreator[interfaces.SomeCounter](Scoped, &models.SimpleCounter{}, "", nil)
RegisterKeyedCreator[interfaces.SomeCounter](Scoped, &models.SimpleCounter{}, nil)
})
}

func TestRegisterCreatorNilKeyOnGetting(t *testing.T) {
clearAll()
RegisterKeyedCreator[interfaces.SomeCounter](Scoped, &models.SimpleCounter{}, "firas")
assert.Panics(t, func() {
Get[interfaces.SomeCounter](context.Background(), nil)
GetKeyed[interfaces.SomeCounter](context.Background(), nil)
})
}

Expand Down
8 changes: 4 additions & 4 deletions default_container_keyed_getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
)

// GetKeyed Retrieves an instance based on type and key (panics if no valid implementations)
func GetKeyed[T any](ctx context.Context, key ...KeyStringer) (T, context.Context) {
return getFromContainer[T](DefaultContainer, ctx, key...)
func GetKeyed[T any](ctx context.Context, key KeyStringer) (T, context.Context) {
return getFromContainer[T](DefaultContainer, ctx, key)
}

// GetKeyedList Retrieves a list of instances based on type and key
func GetKeyedList[T any](ctx context.Context, key ...KeyStringer) ([]T, context.Context) {
return getListFromContainer[T](DefaultContainer, ctx, key...)
func GetKeyedList[T any](ctx context.Context, key KeyStringer) ([]T, context.Context) {
return getListFromContainer[T](DefaultContainer, ctx, key)
}
21 changes: 15 additions & 6 deletions default_container_keyed_registerers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,46 @@ import "context"

// RegisterKeyedCreator Registers a lazily initialized value using a `Creator[T]` interface
func RegisterKeyedCreator[T any](lifetime Lifetime, creator Creator[T], key KeyStringer) {
if key == nil {
panic(nilKey)
}
registerCreatorToContainer[T](DefaultContainer, lifetime, creator, key)
}

// RegisterKeyedSingleton Registers an eagerly instantiated singleton value
// To register an eagerly instantiated scoped value use [ProvideScopedValue]
func RegisterKeyedSingleton[T comparable](impl T, key KeyStringer) {
if key == nil {
panic(nilKey)
}
registerSingletonToContainer[T](DefaultContainer, impl, key)
}

// RegisterKeyedFunc Registers a lazily initialized value using an `Initializer[T]` function signature
func RegisterKeyedFunc[T any](lifetime Lifetime, initializer Initializer[T], key KeyStringer) {
if key == nil {
panic(nilKey)
}
registerFuncToContainer(DefaultContainer, lifetime, initializer, key)
}

// RegisterKeyedAlias Registers an interface type to a concrete implementation.
// Allowing you to register the concrete implementation to the default container and later get the interface from it.
func RegisterKeyedAlias[TInterface, TImpl any]() {
registerAliasToContainer[TInterface, TImpl](DefaultContainer)
}

// RegisterKeyedPlaceholder registers a future value with Scoped lifetime.
// This value will be injected in runtime using the [ProvideScopedValue] function.
// Resolving objects which depend on this value will panic if the value has not been provided.
// Placeholder with the same type and key can be registered only once.
func RegisterKeyedPlaceholder[T comparable](key KeyStringer) {
if key == nil {
panic(nilKey)
}
registerPlaceholderToContainer[T](DefaultContainer, key)
}

// ProvideKeyedScopedValue injects a concrete value into the given context.
// This value will be available only to the default container. And the container can only resolve this value if
// it has the matching (type and key's) Placeholder registered. Checkout the [RegisterPlaceholder] function for more info.
func ProvideKeyedScopedValue[T comparable](ctx context.Context, value T, key KeyStringer) context.Context {
if key == nil {
panic(nilKey)
}
return provideScopedValueToContainer(DefaultContainer, ctx, value, key)
}
4 changes: 2 additions & 2 deletions default_container_unkeyed_getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (

// Get Retrieves an instance based on type and key (panics if no valid implementations)
func Get[T any](ctx context.Context) (T, context.Context) {
return getFromContainer[T](DefaultContainer, ctx)
return getFromContainer[T](DefaultContainer, ctx, nil)
}

// GetList Retrieves a list of instances based on type and key
func GetList[T any](ctx context.Context) ([]T, context.Context) {
return getListFromContainer[T](DefaultContainer, ctx)
return getListFromContainer[T](DefaultContainer, ctx, nil)
}
6 changes: 3 additions & 3 deletions eager_singleton_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestRegisterEagerSingletonMultipleImplementationsKeyed(t *testing.T) {

RegisterSingleton[interfaces.SomeCounter](&models.SimpleCounter{})

counters, _ := GetList[interfaces.SomeCounter](context.Background(), "firas")
counters, _ := GetKeyedList[interfaces.SomeCounter](context.Background(), "firas")

if got := len(counters); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
Expand Down Expand Up @@ -85,15 +85,15 @@ func TestRegisterEagerSingletonSingletonState(t *testing.T) {
func TestRegisterEagerSingletonNilKeyOnRegistering(t *testing.T) {
clearAll()
assert.Panics(t, func() {
RegisterKeyedSingleton[interfaces.SomeCounter](&models.SimpleCounter{}, nil, "")
RegisterKeyedSingleton[interfaces.SomeCounter](&models.SimpleCounter{}, nil)
})
}

func TestRegisterEagerSingletonNilKeyOnGetting(t *testing.T) {
clearAll()
RegisterKeyedSingleton[interfaces.SomeCounter](&models.SimpleCounter{}, "firas")
assert.Panics(t, func() {
Get[interfaces.SomeCounter](context.Background(), nil, "")
GetKeyed[interfaces.SomeCounter](context.Background(), nil)
})
}

Expand Down
2 changes: 1 addition & 1 deletion get_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestGetListKeyed(t *testing.T) {
RegisterKeyedCreator[interfaces.SomeCounter](registrationType, &models.SimpleCounter{}, key)
RegisterKeyedCreator[interfaces.SomeCounter](registrationType, &models.SimpleCounter{}, "Firas")

counters, _ := GetList[interfaces.SomeCounter](context.Background(), key)
counters, _ := GetKeyedList[interfaces.SomeCounter](context.Background(), key)
if got := len(counters); got != 3 {
t.Errorf("got %v, expected %v", got, 3)
}
Expand Down
6 changes: 3 additions & 3 deletions get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestGetKeyed(t *testing.T) {

RegisterKeyedCreator[interfaces.SomeCounter](registrationType, &m.SimpleCounter{}, key)

c, _ := Get[interfaces.SomeCounter](context.Background(), key)
c, _ := GetKeyed[interfaces.SomeCounter](context.Background(), key)

c.AddOne()
c.AddOne()
Expand Down Expand Up @@ -124,7 +124,7 @@ func TestGetResolvedSingletons(t *testing.T) {
assert.Equal(t, 5, len(disposables))

//invoke X2 in "somekey" scope
_, _ = GetList[fmt.Stringer](ctx, "somekey")
_, _ = GetKeyedList[fmt.Stringer](ctx, "somekey")

//Act
//all invoked singleton would be returned whatever keys they are registered with
Expand Down Expand Up @@ -218,7 +218,7 @@ func TestGetResolvedScopedInstances(t *testing.T) {
assert.Equal(t, "S2", disposables[0].String())

//invoke the keyed service T1
_, ctx = GetList[*m.DisposableService2](ctx, "module1")
_, ctx = GetKeyedList[*m.DisposableService2](ctx, "module1")

//Act
disposables = GetResolvedScopedInstances[m.Disposer](ctx) //S2, T1
Expand Down
6 changes: 3 additions & 3 deletions initializer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func TestRegisterFuncMultipleImplementationsKeyed(t *testing.T) {
return &models.SimpleCounter{}, ctx
})

counters, _ := GetList[interfaces.SomeCounter](context.Background(), "firas")
counters, _ := GetKeyedList[interfaces.SomeCounter](context.Background(), "firas")

if got := len(counters); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
Expand Down Expand Up @@ -184,7 +184,7 @@ func TestRegisterFuncNilKeyOnRegistering(t *testing.T) {
assert.Panics(t, func() {
RegisterKeyedFunc[interfaces.SomeCounter](Scoped, func(ctx context.Context) (interfaces.SomeCounter, context.Context) {
return &models.SimpleCounter{}, ctx
}, "", nil)
}, nil)
})
}

Expand All @@ -195,7 +195,7 @@ func TestRegisterFuncNilKeyOnGetting(t *testing.T) {
}, "firas")

assert.Panics(t, func() {
Get[interfaces.SomeCounter](context.Background(), "", nil)
GetKeyed[interfaces.SomeCounter](context.Background(), "")
})
}

Expand Down
10 changes: 5 additions & 5 deletions internal_getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ func sortAndSelect[TInterface any](list []*concrete) []TInterface {
return result
}

func getFromContainer[T any](con *Container, ctx context.Context, key ...KeyStringer) (T, context.Context) {
func getFromContainer[T any](con *Container, ctx context.Context, key KeyStringer) (T, context.Context) {
pointerTypeName := getPointerTypeName[T]()
typeID := getTypeID(pointerTypeName, key...)
typeID := getTypeID(pointerTypeName, key)
lastRegisteredResolver := con.getLastRegisteredResolver(typeID)
if lastRegisteredResolver == nil { //not found, T is an alias

Expand All @@ -62,7 +62,7 @@ func getFromContainer[T any](con *Container, ctx context.Context, key ...KeyStri
}
for i := count - 1; i >= 0; i-- {
impl := implementations[i]
typeID = getTypeID(impl, key...)
typeID = getTypeID(impl, key)
lastRegisteredResolver = con.getLastRegisteredResolver(typeID)
if lastRegisteredResolver != nil {
break
Expand All @@ -76,7 +76,7 @@ func getFromContainer[T any](con *Container, ctx context.Context, key ...KeyStri
return concrete.value.(T), ctx
}

func getListFromContainer[T any](con *Container, ctx context.Context, key ...KeyStringer) ([]T, context.Context) {
func getListFromContainer[T any](con *Container, ctx context.Context, key KeyStringer) ([]T, context.Context) {
inputPointerTypeName := getPointerTypeName[T]()

con.lock.RLock()
Expand All @@ -94,7 +94,7 @@ func getListFromContainer[T any](con *Container, ctx context.Context, key ...Key
for i := 0; i < len(pointerTypeNames); i++ {
pointerTypeName := pointerTypeNames[i]
// generate type identifier
typeID := getTypeID(pointerTypeName, key...)
typeID := getTypeID(pointerTypeName, key)

// try to get service resolver from container
con.lock.RLock()
Expand Down
14 changes: 7 additions & 7 deletions registrars_placeholder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ func TestPlaceHolder_OverridePlaceHolder(t *testing.T) {
ctx := ProvideKeyedScopedValue[*m.Trader](context.Background(), &m.Trader{Name: "John"}, "module2")

//get the placeHolder value would success
trader, ctx := Get[*m.Trader](ctx, "module2")
trader, ctx := GetKeyed[*m.Trader](ctx, "module2")
assert.Equal(t, "John", trader.Name)

//replace the placeHolder value "John" with a new value "David"
ctx = ProvideKeyedScopedValue[*m.Trader](ctx, &m.Trader{Name: "David"}, "module2")
trader, ctx = Get[*m.Trader](ctx, "module2")
trader, ctx = GetKeyed[*m.Trader](ctx, "module2")
assert.Equal(t, "David", trader.Name)

traders, ctx := GetList[*m.Trader](ctx, "module2")
traders, ctx := GetKeyedList[*m.Trader](ctx, "module2")
assert.Equal(t, 1, len(traders))
assert.Equal(t, "David", traders[0].Name)

Expand All @@ -111,11 +111,11 @@ func TestPlaceHolder_OverridePlaceHolder(t *testing.T) {
return &m.Trader{Name: "Mary"}, ctx
}, "module2")

trader, ctx = Get[*m.Trader](ctx, "module2")
trader, ctx = GetKeyed[*m.Trader](ctx, "module2")
assert.Equal(t, "Mary", trader.Name)

//Get both the placeHolder value ("David") and the real resolver value ("Mary")
traders, ctx = GetList[*m.Trader](ctx, "module2")
traders, ctx = GetKeyedList[*m.Trader](ctx, "module2")
assert.Equal(t, 2, len(traders)) //David and Mary
assert.True(t, tradersListContainsName(traders, "David"))
assert.True(t, tradersListContainsName(traders, "Mary"))
Expand All @@ -124,11 +124,11 @@ func TestPlaceHolder_OverridePlaceHolder(t *testing.T) {
ctx = ProvideKeyedScopedValue[*m.Trader](ctx, &m.Trader{Name: "Nathan"}, "module2")

//the placeHolder value cannot override the real resolver value
trader, ctx = Get[*m.Trader](ctx, "module2")
trader, ctx = GetKeyed[*m.Trader](ctx, "module2")
assert.Equal(t, "Mary", trader.Name)

//but it replaces the old placeHolder value ("Nathan" will replace "David")
traders, _ = GetList[*m.Trader](ctx, "module2")
traders, _ = GetKeyedList[*m.Trader](ctx, "module2")
assert.Equal(t, 2, len(traders)) //Nathan and Mary
assert.True(t, tradersListContainsName(traders, "Nathan"))
assert.True(t, tradersListContainsName(traders, "Mary"))
Expand Down

0 comments on commit bbd07da

Please sign in to comment.