Skip to content

Commit

Permalink
R/0.6.1 (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
firasdarwish authored Nov 15, 2024
2 parents 50afc6b + c492a9f commit 91d373a
Show file tree
Hide file tree
Showing 22 changed files with 417 additions and 247 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ Alias is also scoped by key. When you "Get" an alias with keys for eg: `ore.Get[

### Registration Validation

Once you're done with registering all the services, it is recommended to call `ore.Seal()`, then `ore.Validate()`, then finally `ore.DisableValidation=true`.
Once you're done with registering all the services, it is recommended to call `ore.Seal()`, then `ore.Validate()`, then finally `ore.DefaultContainer.DisableValidation=true`.

`ore.Validate()` invokes ALL your registered resolvers. The purpose is to panic early if your registrations were in bad shape:

Expand All @@ -313,7 +313,7 @@ Option 1 (run `ore.Validate` on test) is usually a better choice.

(2) It is recommended to seal your container `ore.Seal()` (which seals the container) on application start => Please don't call `ore.RegisterXX` all over the place.

(3) A combination of `ore.Buile()` and then `ore.Validate()` and then `ore.DisabledValidation=true` ensures no more new resolvers will be registered AND all registered resolvers are validated, this will
(3) A combination of `ore.Buile()` and then `ore.Validate()` and then `ore.DefaultContainer.DisabledValidation=true` ensures no more new resolvers will be registered AND all registered resolvers are validated, this will
prevent any further validation each time a resolver is invoked (`ore.Get`) which greatly enhances performance.

(4) Keep the object creation function (a.k.a resolvers) simple. Their only responsibility should be **object creation**.
Expand Down
6 changes: 3 additions & 3 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ func (this *Container) Validate() {
}
ctx := context.Background()

//provide default value for all placeHolders
//provide default value for all placeholders
for _, resolvers := range this.resolvers {
for _, resolver := range resolvers {
if resolver.isPlaceHolder() {
ctx = resolver.providePlaceHolderDefaultValue(this, ctx)
if resolver.isPlaceholder() {
ctx = resolver.providePlaceholderDefaultValue(this, ctx)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions container_keyed_registerers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func RegisterKeyedCreatorToContainer[T any](con *Container, lifetime Lifetime, c

// RegisterKeyedSingletonToContainer Registers an eagerly instantiated singleton value to the given container.
// To register an eagerly instantiated scoped value use [ProvideScopedValueToContainer]
func RegisterKeyedSingletonToContainer[T comparable](con *Container, impl T, key KeyStringer) {
func RegisterKeyedSingletonToContainer[T any](con *Container, impl T, key KeyStringer) {
if key == nil {
panic(nilKey)
}
Expand All @@ -33,7 +33,7 @@ func RegisterKeyedFuncToContainer[T any](con *Container, lifetime Lifetime, init
// 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 RegisterKeyedPlaceholderToContainer[T comparable](con *Container, key KeyStringer) {
func RegisterKeyedPlaceholderToContainer[T any](con *Container, key KeyStringer) {
if key == nil {
panic(nilKey)
}
Expand All @@ -43,7 +43,7 @@ func RegisterKeyedPlaceholderToContainer[T comparable](con *Container, key KeySt
// ProvideKeyedScopedValueToContainer injects a concrete value into the given context.
// This value will be available only to the given container. And the container can only resolve this value if
// it has the matching (type and key's) Placeholder registered. Checkout the [RegisterPlaceholderToContainer] function for more info.
func ProvideKeyedScopedValueToContainer[T comparable](con *Container, ctx context.Context, value T, key KeyStringer) context.Context {
func ProvideKeyedScopedValueToContainer[T any](con *Container, ctx context.Context, value T, key KeyStringer) context.Context {
if key == nil {
panic(nilKey)
}
Expand Down
6 changes: 3 additions & 3 deletions container_unkeyed_registerers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func RegisterCreatorToContainer[T any](con *Container, lifetime Lifetime, creato

// RegisterSingletonToContainer Registers an eagerly instantiated singleton value to the given container.
// To register an eagerly instantiated scoped value use [ProvideScopedValueToContainer]
func RegisterSingletonToContainer[T comparable](con *Container, impl T) {
func RegisterSingletonToContainer[T any](con *Container, impl T) {
registerSingletonToContainer(con, impl, nil)
}

Expand All @@ -24,13 +24,13 @@ func RegisterFuncToContainer[T any](con *Container, lifetime Lifetime, initializ
// 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 RegisterPlaceholderToContainer[T comparable](con *Container) {
func RegisterPlaceholderToContainer[T any](con *Container) {
registerPlaceholderToContainer[T](con, nil)
}

// ProvideScopedValueToContainer injects a concrete value into the given context.
// This value will be available only to the given container. And the container can only resolve this value if
// it has the matching (type and key's) Placeholder registered. Checkout the [RegisterPlaceholderToContainer] function for more info.
func ProvideScopedValueToContainer[T comparable](con *Container, ctx context.Context, value T) context.Context {
func ProvideScopedValueToContainer[T any](con *Container, ctx context.Context, value T) context.Context {
return provideScopedValueToContainer(con, ctx, value, nil)
}
6 changes: 4 additions & 2 deletions creator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ func TestRegisterCreatorMultipleImplementationsKeyed(t *testing.T) {
clearAll()

RegisterKeyedCreator[interfaces.SomeCounter](registrationType, &models.SimpleCounter{}, "firas")
RegisterKeyedCreatorToContainer[interfaces.SomeCounter](DefaultContainer, registrationType, &models.SimpleCounter{}, "firas")

RegisterKeyedCreator[interfaces.SomeCounter](registrationType, &models.SimpleCounter{}, "firas")

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

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

if got := len(counters); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
if got := len(counters); got != 3 {
t.Errorf("got %v, expected %v", got, 3)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions default_container_keyed_registerers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func RegisterKeyedCreator[T any](lifetime Lifetime, creator Creator[T], key KeyS

// RegisterKeyedSingleton Registers an eagerly instantiated singleton value
// To register an eagerly instantiated scoped value use [ProvideScopedValue]
func RegisterKeyedSingleton[T comparable](impl T, key KeyStringer) {
func RegisterKeyedSingleton[T any](impl T, key KeyStringer) {
if key == nil {
panic(nilKey)
}
Expand All @@ -31,7 +31,7 @@ func RegisterKeyedFunc[T any](lifetime Lifetime, initializer Initializer[T], key
// 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) {
func RegisterKeyedPlaceholder[T any](key KeyStringer) {
if key == nil {
panic(nilKey)
}
Expand All @@ -41,7 +41,7 @@ func RegisterKeyedPlaceholder[T comparable](key KeyStringer) {
// 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 {
func ProvideKeyedScopedValue[T any](ctx context.Context, value T, key KeyStringer) context.Context {
if key == nil {
panic(nilKey)
}
Expand Down
6 changes: 3 additions & 3 deletions default_container_unkeyed_registerers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func RegisterCreator[T any](lifetime Lifetime, creator Creator[T]) {

// RegisterSingleton Registers an eagerly instantiated singleton value
// To register an eagerly instantiated scoped value use [ProvideScopedValue]
func RegisterSingleton[T comparable](impl T) {
func RegisterSingleton[T any](impl T) {
registerSingletonToContainer[T](DefaultContainer, impl, nil)
}

Expand All @@ -22,13 +22,13 @@ func RegisterFunc[T any](lifetime Lifetime, initializer Initializer[T]) {
// 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 RegisterPlaceholder[T comparable]() {
func RegisterPlaceholder[T any]() {
registerPlaceholderToContainer[T](DefaultContainer, nil)
}

// ProvideScopedValue 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 ProvideScopedValue[T comparable](ctx context.Context, value T) context.Context {
func ProvideScopedValue[T any](ctx context.Context, value T) context.Context {
return provideScopedValueToContainer(DefaultContainer, ctx, value, nil)
}
12 changes: 9 additions & 3 deletions eager_singleton_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ func TestRegisterSingleton(t *testing.T) {
clearAll()

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

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

RegisterSingletonToContainer[interfaces.SomeCounter](DefaultContainer, &models.SimpleCounter{})
c, _ = Get[interfaces.SomeCounter](context.Background())

c.AddOne()
c.AddOne()
Expand Down Expand Up @@ -49,14 +54,15 @@ func TestRegisterSingletonMultipleImplementationsKeyed(t *testing.T) {
clearAll()

RegisterKeyedSingleton[interfaces.SomeCounter](&models.SimpleCounter{}, "firas")
RegisterKeyedSingletonToContainer[interfaces.SomeCounter](DefaultContainer, &models.SimpleCounter{}, "firas")
RegisterKeyedSingleton[interfaces.SomeCounter](&models.SimpleCounter{}, "firas")

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

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

if got := len(counters); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
if got := len(counters); got != 3 {
t.Errorf("got %v, expected %v", got, 3)
}
}

Expand Down
16 changes: 8 additions & 8 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ func nilVal[T any]() error {
}

func lifetimeMisalignment(resolver resolverMetadata, depResolver resolverMetadata) error {
return fmt.Errorf("detect lifetime misalignment: %s depends on %s", resolver, depResolver)
return fmt.Errorf("detected lifetime misalignment: %s depends on %s", resolver, depResolver)
}

func cyclicDependency(resolver resolverMetadata) error {
return fmt.Errorf("detect cyclic dependency where: %s depends on itself", resolver)
return fmt.Errorf("detected cyclic dependency where: %s depends on itself", resolver)
}

func placeHolderValueNotProvided(resolver resolverMetadata) error {
return fmt.Errorf("No value has been provided for this placeholder: %s", resolver)
func placeholderValueNotProvided(resolver resolverMetadata) error {
return fmt.Errorf("no value has been provided for this placeholder: %s", resolver)
}

func typeAlreadyRegistered(typeID typeID) error {
return fmt.Errorf("The type '%s' has already been registered (as a Resolver or as a Placeholder). Cannot override it with other Placeholder", typeID)
return fmt.Errorf("the type '%s' has already been registered (as a Resolver or as a Placeholder). Cannot override it with other Placeholder", typeID)
}

var alreadyBuilt = errors.New("services container is already built")
var alreadyBuiltCannotAdd = errors.New("cannot appendToContainer, services container is already built")
var nilKey = errors.New("cannot have nil keys")
var alreadyBuilt = errors.New("services container is already sealed")
var alreadyBuiltCannotAdd = errors.New("cannot register new resolvers, container is sealed")
var nilKey = errors.New("cannot use nil key")
150 changes: 142 additions & 8 deletions get_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,151 @@ import (
"testing"
)

func TestGetList(t *testing.T) {
for _, registrationType := range types {
clearAll()
func TestGetKeyedListSingleton(t *testing.T) {
clearAll()

RegisterCreator[interfaces.SomeCounter](registrationType, &models.SimpleCounter{})
key := "ore"
RegisterKeyedCreator[interfaces.SomeCounter](Singleton, &models.SimpleCounter{}, key)

counters, _ := GetList[interfaces.SomeCounter](context.Background())
counters, _ := GetKeyedList[interfaces.SomeCounter](context.Background(), key)
counters, _ = GetKeyedList[interfaces.SomeCounter](context.Background(), key)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
}
if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
}

counters, _ = GetKeyedListFromContainer[interfaces.SomeCounter](DefaultContainer, context.Background(), key)
counters, _ = GetKeyedListFromContainer[interfaces.SomeCounter](DefaultContainer, context.Background(), key)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 4 {
t.Errorf("got %v, expected %v", got, 4)
}
}

func TestGetListSingleton(t *testing.T) {
clearAll()

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

counters, _ := GetList[interfaces.SomeCounter](context.Background())
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}
counters, _ = GetList[interfaces.SomeCounter](context.Background())
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 4 {
t.Errorf("got %v, expected %v", got, 4)
}

counters, _ = GetListFromContainer[interfaces.SomeCounter](DefaultContainer, context.Background())
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}
counters, _ = GetListFromContainer[interfaces.SomeCounter](DefaultContainer, context.Background())
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 8 {
t.Errorf("got %v, expected %v", got, 8)
}
}

func TestGetListScoped(t *testing.T) {
clearAll()

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

ctx := context.Background()
counters, ctx := GetList[interfaces.SomeCounter](ctx)
counters, ctx = GetList[interfaces.SomeCounter](ctx)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
}

counters, ctx = GetListFromContainer[interfaces.SomeCounter](DefaultContainer, ctx)
counters, ctx = GetListFromContainer[interfaces.SomeCounter](DefaultContainer, ctx)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 4 {
t.Errorf("got %v, expected %v", got, 4)
}
}

func TestGetListTransient(t *testing.T) {
clearAll()

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

ctx := context.Background()
counters, ctx := GetList[interfaces.SomeCounter](ctx)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

counters, ctx = GetList[interfaces.SomeCounter](ctx)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
}

counters, ctx = GetListFromContainer[interfaces.SomeCounter](DefaultContainer, ctx)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}
counters, ctx = GetListFromContainer[interfaces.SomeCounter](DefaultContainer, ctx)
if len(counters) > 0 {
counters[0].AddOne()
counters[0].AddOne()
}

if got := len(counters); got != 1 {
t.Errorf("got %v, expected %v", got, 1)
} else if got := counters[0].GetCount(); got != 2 {
t.Errorf("got %v, expected %v", got, 2)
}
}

Expand Down
Loading

0 comments on commit 91d373a

Please sign in to comment.