-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adds support for named service registrations * Changes comparison service registrations (Func is always treated as not equal) * Fixes a bug in the detectCircularDependency helper function, which caused an infinite loop
- Loading branch information
1 parent
5899c46
commit 76ea475
Showing
10 changed files
with
258 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package tests | ||
|
||
import ( | ||
"context" | ||
"github.com/matzefriedrich/parsley/pkg/features" | ||
"github.com/matzefriedrich/parsley/pkg/registration" | ||
"github.com/matzefriedrich/parsley/pkg/resolving" | ||
"github.com/matzefriedrich/parsley/pkg/types" | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func Test_Registry_register_named_service_resolve_factory(t *testing.T) { | ||
|
||
// Arrange | ||
registry := registration.NewServiceRegistry() | ||
|
||
// Act | ||
err := features.RegisterNamed[dataService](registry, | ||
registration.NamedServiceRegistration("remote", newRemoteDataService, types.LifetimeSingleton), | ||
registration.NamedServiceRegistration("local", newLocalDataService, types.LifetimeTransient)) | ||
|
||
resolver := resolving.NewResolver(registry) | ||
scopedContext := resolving.NewScopedContext(context.Background()) | ||
|
||
namedServiceFactory, _ := resolving.ResolveRequiredService[func(string) (dataService, error)](resolver, scopedContext) | ||
remote, _ := namedServiceFactory("remote") | ||
local, _ := namedServiceFactory("local") | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
assert.NotNil(t, namedServiceFactory) | ||
assert.NotNil(t, remote) | ||
assert.NotNil(t, local) | ||
} | ||
|
||
func Test_Registry_register_named_service_consume_factory(t *testing.T) { | ||
|
||
// Arrange | ||
registry := registration.NewServiceRegistry() | ||
_ = registration.RegisterSingleton(registry, newController) | ||
_ = features.RegisterNamed[dataService](registry, | ||
registration.NamedServiceRegistration("remote", newRemoteDataService, types.LifetimeSingleton), | ||
registration.NamedServiceRegistration("local", newLocalDataService, types.LifetimeTransient)) | ||
|
||
resolver := resolving.NewResolver(registry) | ||
scopedContext := resolving.NewScopedContext(context.Background()) | ||
|
||
// Act | ||
actual, err := resolving.ResolveRequiredService[*controller](resolver, scopedContext) | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
assert.NotNil(t, actual) | ||
assert.NotNil(t, actual.remoteDataService) | ||
assert.NotNil(t, actual.localDataService) | ||
} | ||
|
||
type dataService interface { | ||
FetchData() string | ||
} | ||
|
||
type remoteDataService struct { | ||
} | ||
|
||
func newRemoteDataService() dataService { | ||
return &remoteDataService{} | ||
} | ||
|
||
func (r *remoteDataService) FetchData() string { | ||
return "data from remote service" | ||
} | ||
|
||
var _ dataService = &remoteDataService{} | ||
|
||
type localDataService struct{} | ||
|
||
func newLocalDataService() dataService { | ||
return &localDataService{} | ||
} | ||
|
||
func (l *localDataService) FetchData() string { | ||
return "data from local service" | ||
} | ||
|
||
var _ dataService = &localDataService{} | ||
|
||
type controller struct { | ||
remoteDataService dataService | ||
localDataService dataService | ||
} | ||
|
||
func newController(dataServiceFactory func(string) (dataService, error)) *controller { | ||
remote, _ := dataServiceFactory("remote") | ||
local, _ := dataServiceFactory("local") | ||
return &controller{ | ||
remoteDataService: remote, | ||
localDataService: local, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package features | ||
|
||
import ( | ||
"github.com/matzefriedrich/parsley/pkg/registration" | ||
"github.com/matzefriedrich/parsley/pkg/resolving" | ||
"github.com/matzefriedrich/parsley/pkg/types" | ||
) | ||
|
||
type namedService struct { | ||
name string | ||
activatorFunc any | ||
} | ||
|
||
func (n namedService) ActivatorFunc() any { | ||
return n.activatorFunc | ||
} | ||
|
||
func (n namedService) Name() string { | ||
return n.name | ||
} | ||
|
||
func RegisterNamed[T any](registry types.ServiceRegistry, services ...registration.NamedServiceRegistrationFunc) error { | ||
|
||
registrationErrors := make([]error, 0) | ||
|
||
for _, service := range services { | ||
name, serviceActivatorFunc, _ := service() | ||
if len(name) == 0 || serviceActivatorFunc == nil { | ||
return types.NewRegistryError("invalid named service registration") | ||
} | ||
namedActivator := newNamedServiceFactory[T](name, serviceActivatorFunc) | ||
err := registration.RegisterInstance(registry, namedActivator) | ||
if err != nil { | ||
registrationErrors = append(registrationErrors, err) | ||
} | ||
} | ||
|
||
nameServiceResolver := resolving.CreateNamedServiceResolverActivatorFunc[T]() | ||
err := registration.RegisterTransient(registry, nameServiceResolver) | ||
if err != nil { | ||
registrationErrors = append(registrationErrors, err) | ||
} | ||
|
||
if len(registrationErrors) > 0 { | ||
return types.NewRegistryError("failed to register named services", types.WithAggregatedCause(registrationErrors...)) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func newNamedServiceFactory[T any](name string, activatorFunc any) func() types.NamedService[T] { | ||
return func() types.NamedService[T] { | ||
return &namedService{ | ||
name: name, | ||
activatorFunc: activatorFunc, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package registration | ||
|
||
import ( | ||
"github.com/matzefriedrich/parsley/pkg/types" | ||
) | ||
|
||
type NamedServiceRegistrationFunc func() (name string, activatorFunc any, scope types.LifetimeScope) | ||
|
||
func NamedServiceRegistration(name string, activatorFunc any, scope types.LifetimeScope) NamedServiceRegistrationFunc { | ||
return func() (string, any, types.LifetimeScope) { | ||
return name, activatorFunc, scope | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package resolving | ||
|
||
import ( | ||
"context" | ||
"github.com/matzefriedrich/parsley/pkg/types" | ||
) | ||
|
||
type NamedServiceResolverActivatorFunc[T any] func(types.Resolver) func(string) (T, error) | ||
|
||
func CreateNamedServiceResolverActivatorFunc[T any]() NamedServiceResolverActivatorFunc[T] { | ||
return func(resolver types.Resolver) func(string) (T, error) { | ||
var nilInstance T | ||
requiredServices, _ := ResolveRequiredServices[func() types.NamedService[T]](resolver, context.Background()) | ||
return func(name string) (T, error) { | ||
for _, service := range requiredServices { | ||
s := service() | ||
if s.Name() == name { | ||
return Activate[T](resolver, context.Background(), s.ActivatorFunc()) | ||
} | ||
} | ||
return nilInstance, types.NewResolverError("failed to resolve named service") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters