Documentation
¶
Overview ¶
Package goldi implements a lazy dependency injection framework for go. Goldi is MIT-Licensed
Example ¶
package main
import (
"fmt"
"net/http"
"time"
"github.com/tarokamikaze/goldi"
)
type SimpleLogger struct {
Name string
}
func (l *SimpleLogger) Log(msg string) {
fmt.Printf("LOG: %s\n", msg)
}
func main() {
// create a new container when your application loads
registry := goldi.NewTypeRegistry()
config := map[string]interface{}{
"some_parameter": "Hello World",
"timeout": 42.7,
}
// register a simple type with parameter
registry.RegisterType("logger", NewSimpleLoggerWithParam, "%some_parameter%")
// register a struct type
registry.RegisterType("httpClient", &http.Client{}, time.Second*5)
// you can also register already instantiated types
registry.InjectInstance("myInstance", &SimpleLogger{Name: "Foo"})
// create a new container with the registry and the config
container := goldi.NewContainer(registry, config)
// retrieve types from the container
logger := container.MustGet("logger").(*SimpleLogger)
fmt.Printf("logger.Name = %q\n", logger.Name)
}
func NewSimpleLoggerWithParam(name string) *SimpleLogger {
return &SimpleLogger{Name: name}
}
Output: logger.Name = "Hello World"
Example (Go124Features) ¶
Example_go124Features demonstrates the new Go 1.24 features in Goldi
package main
import (
"fmt"
"github.com/tarokamikaze/goldi"
)
func main() {
registry := goldi.NewTypeRegistry()
// Register some types without dependencies for example
registry.RegisterType("logger", func() Logger { return &SimpleLogger{} })
registry.RegisterType("database", func() Database { return &SimpleDatabase{Logger: &SimpleLogger{}} })
registry.RegisterType("service", func() Service {
return &SimpleService{DB: &SimpleDatabase{Logger: &SimpleLogger{}}, Logger: &SimpleLogger{}}
})
container := goldi.NewContainer(registry, map[string]interface{}{})
// 1. Range over func - iterate through registered types
fmt.Println("Registered types:")
for typeID := range registry.TypeIDs() {
fmt.Printf("- %s\n", typeID)
}
// 2. Improved Type Inference - no type assertions needed
logger, err := goldi.Get[Logger](container, "logger")
if err != nil {
panic(err)
}
logger.Log("Using improved type inference!")
// 3. slices.Collect - efficiently collect type IDs
allTypeIDs := registry.CollectTypeIDs()
fmt.Printf("Total registered types: %d\n", len(allTypeIDs))
// 4. Iterator-based warmup
err = container.WarmupCache()
if err != nil {
panic(err)
}
// 5. Check cached instances count
cachedCount := 0
for range container.CachedTypeIDs() {
cachedCount++
}
fmt.Printf("Cached instances count: %d\n", cachedCount)
}
// Test types for examples
type Logger interface {
Log(string)
}
type SimpleLogger struct {
Name string
}
func (l *SimpleLogger) Log(msg string) {
fmt.Printf("LOG: %s\n", msg)
}
type Database interface {
Query(string) string
}
type SimpleDatabase struct {
Logger Logger
}
func (d *SimpleDatabase) Query(query string) string {
d.Logger.Log("Executing query: " + query)
return "result"
}
type Service interface {
Process(string) string
}
type SimpleService struct {
DB Database
Logger Logger
}
func (s *SimpleService) Process(data string) string {
s.Logger.Log("Processing: " + data)
return s.DB.Query("SELECT * FROM data WHERE value = '" + data + "'")
}
Output: Registered types: - logger - database - service LOG: Using improved type inference! Total registered types: 3 Cached instances count: 3
Index ¶
- func Get[T any](c *Container, typeID string) (T, error)
- func IsParameter(p string) bool
- func IsParameterOrTypeReference(p string) bool
- func IsTypeReference(p string) bool
- func IsValid(t TypeFactory) bool
- func MustGet[T any](c *Container, typeID string) T
- type Container
- func (c *Container) AllInstances() iter.Seq2[string, interface{}]
- func (c *Container) CachedTypeIDs() iter.Seq[string]
- func (c *Container) CollectCachedInstances() map[string]interface{}
- func (c *Container) CollectCachedTypeIDs() []string
- func (c *Container) Get(typeID string) (interface{}, error)
- func (c *Container) GetAllInstances() (map[string]interface{}, error)
- func (c *Container) GetMultiple(typeIDs iter.Seq[string]) iter.Seq2[string, interface{}]
- func (c *Container) MustGet(typeID string) interface{}
- func (c *Container) WarmupCache() error
- type MemoryPool
- func (mp *MemoryPool) GetInterfaceSlice() []interface{}
- func (mp *MemoryPool) GetReflectValueSlice() []reflect.Value
- func (mp *MemoryPool) GetStringSlice() []string
- func (mp *MemoryPool) PutInterfaceSlice(slice []interface{})
- func (mp *MemoryPool) PutReflectValueSlice(slice []reflect.Value)
- func (mp *MemoryPool) PutStringSlice(slice []string)
- type ParameterResolver
- type ReflectionCache
- func (rc *ReflectionCache) GetFactoryType(factory TypeFactory) reflect.Type
- func (rc *ReflectionCache) GetMethodByName(obj interface{}, methodName string) (reflect.Method, bool)
- func (rc *ReflectionCache) GetType(obj interface{}) reflect.Type
- func (rc *ReflectionCache) GetValue(obj interface{}) reflect.Value
- type StringSet
- type TypeConfigurator
- type TypeFactory
- func NewAliasType(typeID string) TypeFactory
- func NewConfiguredType(embeddedType TypeFactory, configuratorTypeID, configuratorMethod string) TypeFactory
- func NewFuncReferenceType(typeID, functionName string) TypeFactory
- func NewFuncType(function interface{}) TypeFactory
- func NewInstanceType(instance interface{}) TypeFactory
- func NewProxyType(typeID, functionName string, args ...interface{}) TypeFactory
- func NewStructType(structT interface{}, structParameters ...interface{}) TypeFactory
- func NewType(factoryFunction interface{}, factoryParameters ...interface{}) TypeFactory
- type TypeID
- type TypeReferenceError
- type TypeRegistry
- func (r TypeRegistry) All() iter.Seq2[string, TypeFactory]
- func (r TypeRegistry) Clone() TypeRegistry
- func (r TypeRegistry) CollectAll() map[string]TypeFactory
- func (r TypeRegistry) CollectByType(targetType reflect.Type) []string
- func (r TypeRegistry) CollectFactories() []TypeFactory
- func (r TypeRegistry) CollectTypeIDs() []string
- func (r TypeRegistry) Factories() iter.Seq[TypeFactory]
- func (r TypeRegistry) FilterByType(targetType reflect.Type) iter.Seq[string]
- func (r TypeRegistry) InjectInstance(typeID string, instance interface{})
- func (r TypeRegistry) Register(typeID string, typeDef TypeFactory)
- func (r TypeRegistry) RegisterAll(factories map[string]TypeFactory)
- func (r TypeRegistry) RegisterType(typeID string, factory interface{}, arguments ...interface{})
- func (r TypeRegistry) TypeIDs() iter.Seq[string]
- type UnknownTypeReferenceError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Get ¶
Get retrieves a type with improved type inference using Go 1.24 generics This method provides compile-time type safety and eliminates the need for type assertions
func IsParameter ¶
IsParameter returns whether the given type ID represents a parameter. A goldi parameter is recognized by the leading and trailing percent sign Example: %foobar%
func IsParameterOrTypeReference ¶
IsParameterOrTypeReference is a utility function that returns whether the given string represents a parameter or a reference to a type. See IsParameter and IsTypeReference for further details
func IsTypeReference ¶
IsTypeReference returns whether the given string represents a reference to a type. A goldi type reference is recognized by the leading @ sign. Example: @foobar
func IsValid ¶
func IsValid(t TypeFactory) bool
IsValid checks if a given type factory is valid. This function can be used to check the result of functions like NewType
Types ¶
type Container ¶
type Container struct {
TypeRegistry
Config map[string]interface{}
Resolver *ParameterResolver
// contains filtered or unexported fields
}
Container is the dependency injection container that can be used by your application to define and get types.
Basically this is just a TypeRegistry with access to the application configuration and the knowledge of how to build individual services. Additionally this implements the laziness of the DI using a simple in memory type cache
You must use goldi.NewContainer to get a initialized instance of a Container!
Example ¶
registry := goldi.NewTypeRegistry()
config := map[string]interface{}{}
container := goldi.NewContainer(registry, config)
container.Register("logger", goldi.NewType(NewNullLogger))
l := container.MustGet("logger")
fmt.Printf("%T", l)
Output: *goldi_test.NullLogger
func NewContainer ¶
func NewContainer(registry TypeRegistry, config map[string]interface{}) *Container
NewContainer creates a new container instance using the provided arguments
func (*Container) AllInstances ¶
AllInstances returns an iterator over all cached instances This uses Go 1.24's range over func feature for memory-efficient iteration
func (*Container) CachedTypeIDs ¶
CachedTypeIDs returns an iterator over all cached type IDs
func (*Container) CollectCachedInstances ¶
CollectCachedInstances efficiently collects all cached instances using slices.Collect
func (*Container) CollectCachedTypeIDs ¶
CollectCachedTypeIDs efficiently collects all cached type IDs using slices.Collect
func (*Container) Get ¶
Get retrieves a previously defined type or an error. If the requested typeID has not been registered before or can not be generated Get will return an error.
For your dependency injection to work properly it is important that you do only try to assert interface types when you use Get(..). Otherwise it might be impossible to assert the correct type when you change the underlying type implementations. Also make sure your application is properly tested and defers some panic handling in case you forgot to define a service.
See also Container.MustGet
Example ¶
registry := goldi.NewTypeRegistry()
config := map[string]interface{}{}
container := goldi.NewContainer(registry, config)
container.Register("logger", goldi.NewType(NewNullLogger))
l, err := container.Get("logger")
if err != nil {
fmt.Println(err.Error())
return
}
// do stuff with the logger. usually you need a type assertion
fmt.Printf("%T", l.(*NullLogger))
Output: *goldi_test.NullLogger
func (*Container) GetAllInstances ¶
GetAllInstances retrieves all registered types and returns them as a map
func (*Container) GetMultiple ¶
GetMultiple efficiently retrieves multiple types using iterator pattern
func (*Container) MustGet ¶
MustGet behaves exactly like Get but will panic instead of returning an error Since MustGet can only return interface{} you need to add a type assertion after the call:
container.MustGet("logger").(LoggerInterface)
Example ¶
package main
import (
"fmt"
"github.com/tarokamikaze/goldi"
)
type SimpleLogger struct {
Name string
}
func (l *SimpleLogger) Log(msg string) {
fmt.Printf("LOG: %s\n", msg)
}
func main() {
registry := goldi.NewTypeRegistry()
config := map[string]interface{}{
"some_parameter": "Hello World",
}
registry.RegisterType("logger", NewSimpleLoggerWithParam, "%some_parameter%")
container := goldi.NewContainer(registry, config)
logger := container.MustGet("logger").(*SimpleLogger)
fmt.Printf("logger.Name = %q\n", logger.Name)
}
func NewSimpleLoggerWithParam(name string) *SimpleLogger {
return &SimpleLogger{Name: name}
}
Output: logger.Name = "Hello World"
func (*Container) WarmupCache ¶
WarmupCache pre-generates instances for all registered types
type MemoryPool ¶
type MemoryPool struct {
// contains filtered or unexported fields
}
MemoryPool provides object pooling to reduce memory allocations
func GetGlobalMemoryPool ¶
func GetGlobalMemoryPool() *MemoryPool
GetGlobalMemoryPool returns the global memory pool instance
func NewMemoryPool ¶
func NewMemoryPool() *MemoryPool
NewMemoryPool creates a new memory pool instance
func (*MemoryPool) GetInterfaceSlice ¶
func (mp *MemoryPool) GetInterfaceSlice() []interface{}
GetInterfaceSlice returns a pooled interface{} slice
func (*MemoryPool) GetReflectValueSlice ¶
func (mp *MemoryPool) GetReflectValueSlice() []reflect.Value
GetReflectValueSlice returns a pooled reflect.Value slice
func (*MemoryPool) GetStringSlice ¶
func (mp *MemoryPool) GetStringSlice() []string
GetStringSlice returns a pooled string slice
func (*MemoryPool) PutInterfaceSlice ¶
func (mp *MemoryPool) PutInterfaceSlice(slice []interface{})
PutInterfaceSlice returns an interface{} slice to the pool
func (*MemoryPool) PutReflectValueSlice ¶
func (mp *MemoryPool) PutReflectValueSlice(slice []reflect.Value)
PutReflectValueSlice returns a reflect.Value slice to the pool
func (*MemoryPool) PutStringSlice ¶
func (mp *MemoryPool) PutStringSlice(slice []string)
PutStringSlice returns a string slice to the pool
type ParameterResolver ¶
type ParameterResolver struct {
Container *Container
}
The ParameterResolver is used by type factories to resolve the values of the dynamic factory arguments (parameters and other type references).
func NewParameterResolver ¶
func NewParameterResolver(container *Container) *ParameterResolver
NewParameterResolver creates a new ParameterResolver and initializes it with the given Container. The container is used when resolving parameters and the type references.
func (*ParameterResolver) Resolve ¶
func (r *ParameterResolver) Resolve(parameter reflect.Value, expectedType reflect.Type) (reflect.Value, error)
Resolve takes a parameter and resolves any references to configuration parameter values or type references. If the type of `parameter` is not a parameter or type reference it is returned as is. Parameters must always have the form `%my.beautiful.param%. Type references must have the form `@my_type.bla`. It is also legal to request an optional type using the syntax `@?my_optional_type`. If this type is not registered Resolve will not return an error but instead give you the null value of the expected type.
type ReflectionCache ¶
type ReflectionCache struct {
// contains filtered or unexported fields
}
ReflectionCache provides cached reflection operations to reduce runtime overhead
func GetGlobalReflectionCache ¶
func GetGlobalReflectionCache() *ReflectionCache
GetGlobalReflectionCache returns the global reflection cache instance
func NewReflectionCache ¶
func NewReflectionCache() *ReflectionCache
NewReflectionCache creates a new reflection cache instance
func (*ReflectionCache) GetFactoryType ¶
func (rc *ReflectionCache) GetFactoryType(factory TypeFactory) reflect.Type
GetFactoryType returns the type that a TypeFactory produces
func (*ReflectionCache) GetMethodByName ¶
func (rc *ReflectionCache) GetMethodByName(obj interface{}, methodName string) (reflect.Method, bool)
GetMethodByName returns cached method or computes and caches it
func (*ReflectionCache) GetType ¶
func (rc *ReflectionCache) GetType(obj interface{}) reflect.Type
GetType returns cached reflect.Type or computes and caches it
func (*ReflectionCache) GetValue ¶
func (rc *ReflectionCache) GetValue(obj interface{}) reflect.Value
GetValue returns cached reflect.Value or computes and caches it Note: Values are not cached as they represent specific instances
type StringSet ¶
type StringSet map[string]struct{}
A StringSet represents a set of strings. Uses empty struct{} as value to minimize memory usage.
func NewStringSet ¶
NewStringSet creates a new StringSet with optional initial capacity
func (StringSet) Contains ¶
Contains returns true if the given value is contained in this string set.
type TypeConfigurator ¶
The TypeConfigurator is used to configure a type after its instantiation. You can specify a function in another type that is known to the container. The type instance is passed to the configurator type, allowing the configurator to do whatever it needs to configure the type after its creation.
A TypeConfigurator can be used, for example, when you have a type that requires complex setup based on configuration settings coming from different sources. Using an external configurator, you can decouple the setup logic from the business logic of the corresponding type to keep it DRY and easy to maintain. Also this way its easy to exchange setup logic at run time for example on different environments.
Another interesting use case is when you have multiple objects that share a common configuration or that should be configured in a similar way at runtime.
func NewTypeConfigurator ¶
func NewTypeConfigurator(configuratorTypeID, methodName string) *TypeConfigurator
NewTypeConfigurator creates a new TypeConfigurator
func (*TypeConfigurator) Configure ¶
func (c *TypeConfigurator) Configure(thing interface{}, container *Container) error
Configure will get the configurator type and ass `thing` its configuration function. The method returns an error if thing is nil, the configurator type is not defined or the configurators function does not exist.
type TypeFactory ¶
type TypeFactory interface {
// Arguments returns all arguments that are used to generate the type.
// This enables the container validator to check if all required parameters exist
// and if there are circular type dependencies.
Arguments() []interface{}
// Generate will instantiate a new instance of the according type or return an error.
Generate(parameterResolver *ParameterResolver) (interface{}, error)
}
A TypeFactory is used to instantiate a certain type.
func NewAliasType ¶
func NewAliasType(typeID string) TypeFactory
NewAliasType create a new TypeFactory which just serves as alias to the given type ID.
A call to an alias type will retrieve the aliased type as if it was requested via container.Get(typeID) This method will always return a valid type and works bot for regular type references (without leading @) and references to type functions.
Goldigen yaml syntax example:
type_that_is_aliased:
alias: "@some_type" // container.Get("type_that_is_aliased") will now return "some_type" instead
Goldigen yaml syntax example with function reference:
func_type_that_is_aliased:
alias: "@some_type::DoStuff"
Example ¶
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
container.Register("logger", goldi.NewStructType(SimpleLogger{}))
container.Register("mock", goldi.NewStructType(MockType{}))
container.Register("default_logger", goldi.NewAliasType("logger"))
container.Register("logging_func", goldi.NewAliasType("mock::DoStuff"))
fmt.Printf("logger: %T\n", container.MustGet("logger"))
fmt.Printf("default_logger: %T\n", container.MustGet("default_logger"))
fmt.Printf("logging_func: %T\n", container.MustGet("logging_func"))
}
var _ = Describe("aliasType", func() {
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewAliasType("foo")
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("Arguments()", func() {
It("should return the aliased service ID", func() {
typeDef := goldi.NewAliasType("foo")
Expect(typeDef.Arguments()).To(Equal([]interface{}{"@foo"}))
})
})
Describe("Generate()", func() {
var (
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
config := map[string]interface{}{}
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
It("should act as alias for the actual type", func() {
container.Register("foo", goldi.NewStructType(Foo{}, "I was created by @foo"))
alias := goldi.NewAliasType("foo")
generated, err := alias.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generated).To(BeAssignableToTypeOf(&Foo{}))
Expect(generated.(*Foo).Value).To(Equal("I was created by @foo"))
})
It("should work with func reference types", func() {
container.Register("foo", goldi.NewStructType(Foo{}, "I was created by @foo"))
alias := goldi.NewAliasType("foo::ReturnString")
generated, err := alias.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generated).To(BeAssignableToTypeOf(func(string) string { return "" }))
Expect(generated.(func(string) string)("TEST")).To(Equal("I was created by @foo TEST"))
})
})
})
Output: logger: *goldi_test.SimpleLogger default_logger: *goldi_test.SimpleLogger logging_func: func() string
func NewConfiguredType ¶
func NewConfiguredType(embeddedType TypeFactory, configuratorTypeID, configuratorMethod string) TypeFactory
NewConfiguredType creates a new TypeFactory that decorates a given TypeFactory. The returned configurator will use the decorated type factory first to create a type and then use the resolve the configurator by the given type ID and call the configured method with the instance.
Internally the goldi.TypeConfigurator is used.
The method removes any leading or trailing whitespace from configurator type ID and method. NewConfiguredType will return an invalid type when embeddedType is nil or the trimmed configurator typeID or method is empty.
Goldigen yaml syntax example:
my_type:
package: github.com/fgrosse/foobar
type: MyType
configurator: [ "@my_configurator", Configure ]
Example ¶
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
// this example configurator accepts a Foo type and will set its Value field to the given value
configurator := &MyConfigurator{ConfiguredValue: "success!"}
// register the configurator under a type ID
container.Register("configurator_type", goldi.NewInstanceType(configurator))
// create the type that should be configured
embeddedType := goldi.NewStructType(Foo{})
container.Register("foo", goldi.NewConfiguredType(embeddedType, "configurator_type", "Configure"))
fmt.Println(container.MustGet("foo").(*Foo).Value)
}
var _ = Describe("configuredType", func() {
var embeddedType goldi.TypeFactory
BeforeEach(func() {
embeddedType = goldi.NewStructType(Foo{})
})
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewConfiguredType(embeddedType, "configurator_type", "Configure")
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("NewConfiguredType()", func() {
Context("with invalid argument", func() {
It("should return an invalid type if the embedded type is nil", func() {
typeDef := goldi.NewConfiguredType(nil, "configurator_type", "Configure")
Expect(goldi.IsValid(typeDef)).To(BeFalse())
})
It("should return an invalid type if either the configurator ID or method is empty", func() {
Expect(goldi.IsValid(goldi.NewConfiguredType(embeddedType, "", ""))).To(BeFalse())
Expect(goldi.IsValid(goldi.NewConfiguredType(embeddedType, "configurator_type", ""))).To(BeFalse())
Expect(goldi.IsValid(goldi.NewConfiguredType(embeddedType, "", "configure"))).To(BeFalse())
})
It("should return an invalid type if the configurator method is not exported", func() {
Expect(goldi.IsValid(goldi.NewConfiguredType(embeddedType, "configurator_type", "configure"))).To(BeFalse())
})
})
Context("with valid arguments", func() {
It("should create the type", func() {
typeDef := goldi.NewConfiguredType(embeddedType, "configurator_type", "Configure")
Expect(typeDef).NotTo(BeNil())
})
})
})
Describe("Arguments()", func() {
It("should return the arguments of the embedded type and the configurator as type ID", func() {
embeddedType = goldi.NewStructType(Foo{}, "%param_of_embedded%", "another param")
typeDef := goldi.NewConfiguredType(embeddedType, "configurator_type", "Configure")
Expect(typeDef.Arguments()).NotTo(BeNil())
Expect(typeDef.Arguments()).To(HaveLen(3))
Expect(typeDef.Arguments()).To(ContainElement("%param_of_embedded%"))
Expect(typeDef.Arguments()).To(ContainElement("another param"))
Expect(typeDef.Arguments()).To(ContainElement("@configurator_type"))
})
})
Describe("Generate()", func() {
var (
config = map[string]interface{}{}
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
It("should get the embedded type and configurator and configure it", func() {
typeDef := goldi.NewConfiguredType(embeddedType, "configurator_type", "Configure")
configurator := &MyConfigurator{ConfiguredValue: "success!"}
container.Register("configurator_type", goldi.NewInstanceType(configurator))
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).NotTo(BeNil())
Expect(generatedType).To(BeAssignableToTypeOf(&Foo{}))
Expect(generatedType.(*Foo).Value).To(Equal("success!"))
})
It("should return an error if the embedded type can not be generated", func() {
invalidType := goldi.NewStructType(nil)
typeDef := goldi.NewConfiguredType(invalidType, "configurator_type", "Configure")
configurator := &MyConfigurator{ConfiguredValue: "should not happen"}
container.Register("configurator_type", goldi.NewInstanceType(configurator))
generatedType, err := typeDef.Generate(resolver)
Expect(err).To(MatchError("can not generate configured type: the given struct is nil"))
Expect(generatedType).To(BeNil())
})
It("should return an error if the configurator returns an error", func() {
typeDef := goldi.NewConfiguredType(embeddedType, "configurator_type", "Configure")
configurator := &MyConfigurator{ReturnError: true}
container.Register("configurator_type", goldi.NewInstanceType(configurator))
generatedType, err := typeDef.Generate(resolver)
Expect(err).To(MatchError("can not configure type: this is the error message from the tests.MockTypeConfigurator"))
Expect(generatedType).To(BeNil())
})
})
})
Output: success!
func NewFuncReferenceType ¶
func NewFuncReferenceType(typeID, functionName string) TypeFactory
NewFuncReferenceType returns a TypeFactory that returns a method of another type as method value (function).
Goldigen yaml syntax example:
my_func_type:
func: "@some_type::FancyAction"
Example ¶
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
mockType := &MockType{StringParameter: "Hello World"}
container.Register("mock", goldi.NewInstanceType(mockType))
container.Register("log_func", goldi.NewFuncReferenceType("mock", "DoStuff"))
f := container.MustGet("log_func").(func() string)
fmt.Println(f()) // executes mockType.DoStuff
}
var _ = Describe("funcReferenceType", func() {
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewFuncReferenceType("my_controller", "FancyAction")
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("NewFuncReferenceType()", func() {
It("should return an invalid type if the method name is not exported", func() {
t := goldi.NewFuncReferenceType("foo", "doStuff")
Expect(goldi.IsValid(t)).To(BeFalse())
Expect(t).To(MatchError(`can not use unexported method "doStuff" as second argument to NewFuncReferenceType`))
})
})
Describe("Arguments()", func() {
It("should return the referenced service ID", func() {
typeDef := goldi.NewFuncReferenceType("my_controller", "FancyAction")
Expect(typeDef.Arguments()).To(Equal([]interface{}{"@my_controller"}))
})
})
Describe("Generate()", func() {
var (
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
config := map[string]interface{}{}
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
It("should get the correct method of the referenced type", func() {
container.Register("foo", goldi.NewStructType(Foo{}, "I was created by @foo"))
typeDef := goldi.NewFuncReferenceType("foo", "ReturnString")
generated, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generated).To(BeAssignableToTypeOf(func(string) string { return "" }))
Expect(generated.(func(string) string)("TEST")).To(Equal("I was created by @foo TEST"))
})
It("should return an error if the referenced type can not be generated", func() {
container.Register("foo", goldi.NewStructType(nil))
typeDef := goldi.NewFuncReferenceType("foo", "DoStuff")
_, err := typeDef.Generate(resolver)
Expect(err).To(MatchError(`could not generate func reference type @foo::DoStuff : goldi: error while generating type "foo": the given struct is nil`))
})
It("should return an error if the referenced type has no such method", func() {
container.Register("foo", goldi.NewStructType(Foo{}))
typeDef := goldi.NewFuncReferenceType("foo", "ThisMethodDoesNotExist")
_, err := typeDef.Generate(resolver)
Expect(err).To(MatchError("could not generate func reference type @foo::ThisMethodDoesNotExist : method does not exist"))
})
})
})
Output: I did stuff
func NewFuncType ¶
func NewFuncType(function interface{}) TypeFactory
NewFuncType creates a new TypeFactory that will return a method value
Goldigen yaml syntax example:
my_func_type:
package: github.com/fgrosse/foobar
func: DoStuff
Example ¶
package main
import (
"net/http"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
// define the type
container.Register("my_func", goldi.NewFuncType(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "test" {
w.WriteHeader(http.StatusAccepted)
}
}))
// generate it
result, err := container.Get("my_func")
if err != nil {
return
}
// call it
f := result.(func(name string, age int) (bool, error))
ok, err := f("foo", 42)
if ok != true || err != nil {
panic("!!!")
}
}
var _ = Describe("funcType", func() {
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewFuncType(SomeFunctionForFuncTypeTest)
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("goldi.NewFuncType()", func() {
Context("with invalid argument", func() {
It("should return an invalid type if the argument is no function", func() {
Expect(goldi.IsValid(goldi.NewFuncType(42))).To(BeFalse())
})
})
Context("with argument beeing a function", func() {
It("should create the type", func() {
typeDef := goldi.NewFuncType(SomeFunctionForFuncTypeTest)
Expect(typeDef).NotTo(BeNil())
})
})
})
Describe("Arguments()", func() {
It("should return an empty list", func() {
typeDef := goldi.NewFuncType(SomeFunctionForFuncTypeTest)
Expect(typeDef.Arguments()).NotTo(BeNil())
Expect(typeDef.Arguments()).To(BeEmpty())
})
})
Describe("Generate()", func() {
var (
config = map[string]interface{}{}
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
It("should just return the function", func() {
typeDef := goldi.NewFuncType(SomeFunctionForFuncTypeTest)
Expect(typeDef.Generate(resolver)).To(BeAssignableToTypeOf(SomeFunctionForFuncTypeTest))
})
})
})
func SomeFunctionForFuncTypeTest(name string, age int) (bool, error) {
return true, nil
}
func NewInstanceType ¶
func NewInstanceType(instance interface{}) TypeFactory
NewInstanceType creates a new TypeFactory which will return the given instance on each call to Generate. It will return an invalid type factory if the given instance is nil
You can not generate this type using goldigen
Example ¶
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
myInstance := new(SimpleLogger)
myInstance.Name = "Foobar" // you can configure the instance in your code
// now register this instance as a type
container.Register("logger", goldi.NewInstanceType(myInstance))
// each reference to the "logger" type will now be resolved to that instance
fmt.Println(container.MustGet("logger").(*SimpleLogger).Name)
}
var _ = Describe("instanceType", func() {
var resolver *goldi.ParameterResolver
BeforeEach(func() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
resolver = goldi.NewParameterResolver(container)
})
It("should return an invalid type if NewInstanceType is called with nil", func() {
Expect(goldi.IsValid(goldi.NewInstanceType(nil))).To(BeFalse())
})
Describe("Arguments()", func() {
It("should return an empty list", func() {
typeDef := goldi.NewInstanceType(NewFoo())
Expect(typeDef.Arguments()).To(BeEmpty())
})
})
Describe("Generate", func() {
It("should always return the given instance", func() {
instance := NewFoo()
factory := goldi.NewInstanceType(instance)
for i := 0; i < 3; i++ {
generateResult, err := factory.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generateResult == instance).To(BeTrue(),
fmt.Sprintf("generateResult (%p) should point to the same instance as instance (%p)", generateResult, instance),
)
}
})
})
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewInstanceType("foo")
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
})
Output: Foobar
func NewProxyType ¶
func NewProxyType(typeID, functionName string, args ...interface{}) TypeFactory
NewProxyType returns a TypeFactory that uses a function of another type to generate a result.
Goldigen yaml syntax example:
logger:
factory: "@logger_provider::GetLogger"
args: [ "My Logger" ]
Example ¶
Let's assume that we have a LoggerProvider type that produces configured instances of a Logger each time we call LoggerProvider.GetLogger(loggerName string).
The example shows how to register a `logger` as proxy for a specific call to this LoggerProvider.
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
// LoggerProvider provides logger instances
type LoggerProvider struct{}
func (lp *LoggerProvider) GetLogger(name string) *SimpleLogger {
return &SimpleLogger{Name: name}
}
// Let's assume that we have a LoggerProvider type that produces configured instances
// of a Logger each time we call LoggerProvider.GetLogger(loggerName string).
//
// The example shows how to register a `logger` as proxy for a specific call to this LoggerProvider.
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
// register some type as always
container.Register("logger_provider", goldi.NewStructType(LoggerProvider{}))
// register a proxy type that references the method of previously defined type and append call arguments if any
container.Register("logger", goldi.NewProxyType("logger_provider", "GetLogger", "My logger"))
l := container.MustGet("logger").(*SimpleLogger)
fmt.Printf("%s: %T", l.Name, l)
}
var _ = Describe("proxyType", func() {
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewProxyType("logger_provider", "GetLogger", "My logger")
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("NewProxyType()", func() {
It("should return an invalid type if the method name is not exported", func() {
t := goldi.NewProxyType("logger_provider", "getLogger", "My logger")
Expect(goldi.IsValid(t)).To(BeFalse())
Expect(t).To(MatchError(`can not use unexported method "getLogger" as second argument to NewProxyType`))
})
})
Describe("Arguments()", func() {
It("should return the referenced service ID", func() {
typeDef := goldi.NewProxyType("logger_provider", "GetLogger", "My logger")
Expect(typeDef.Arguments()).To(Equal([]interface{}{"@logger_provider", "My logger"}))
})
})
Describe("Generate()", func() {
var (
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
config := map[string]interface{}{}
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
It("should get the correct method of the referenced type", func() {
container.Register("logger_provider", goldi.NewStructType(LoggerProvider{}))
typeDef := goldi.NewProxyType("logger_provider", "GetLogger", "My logger")
generated, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generated).To(BeAssignableToTypeOf(&SimpleLogger{}))
Expect(generated.(*SimpleLogger).Name).To(Equal("My logger"))
})
It("should return an error if the referenced type has no such method", func() {
typeDef := goldi.NewProxyType("foobar", "DoStuff")
_, err := typeDef.Generate(resolver)
Expect(err).To(MatchError("could not generate proxy type @foobar::DoStuff : type foobar does not exist"))
})
It("should return an error if the referenced type has no such method", func() {
container.Register("logger_provider", goldi.NewStructType(LoggerProvider{}))
typeDef := goldi.NewProxyType("logger_provider", "ThisMethodDoesNotExist", "foobar")
_, err := typeDef.Generate(resolver)
Expect(err).To(MatchError("could not generate proxy type @logger_provider::ThisMethodDoesNotExist : method does not exist"))
})
})
})
Output: My logger: *goldi_test.SimpleLogger
func NewStructType ¶
func NewStructType(structT interface{}, structParameters ...interface{}) TypeFactory
NewStructType creates a TypeFactory that can be used to create a new instance of some struct type.
This function will return an invalid type if:
- structT is no struct or pointer to a struct,
- the number of given structParameters exceed the number of field of structT
- the structParameters types do not match the fields of structT
Goldigen yaml syntax example:
logger:
package: github.com/fgrosse/foobar
type: MyType
Example ¶
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
// all of the following types are semantically identical
container.Register("foo_1", goldi.NewStructType(Foo{}))
container.Register("foo_2", goldi.NewStructType(&Foo{}))
container.Register("foo_3", goldi.NewStructType(new(Foo)))
// each reference to the "logger" type will now be resolved to that instance
fmt.Printf("foo_1: %T\n", container.MustGet("foo_1"))
fmt.Printf("foo_2: %T\n", container.MustGet("foo_2"))
fmt.Printf("foo_3: %T\n", container.MustGet("foo_3"))
}
var _ = Describe("structType", func() {
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewStructType(MockType{})
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("goldi.NewStructType()", func() {
Context("with invalid arguments", func() {
It("should return an invalid type if the generator is no struct or pointer to a struct", func() {
Expect(goldi.IsValid(goldi.NewStructType(42))).To(BeFalse())
})
It("should return an invalid type if the generator is a pointer to something other than a struct", func() {
something := "Hello Pointer World!"
Expect(goldi.IsValid(goldi.NewStructType(&something))).To(BeFalse())
})
})
Context("with first argument beeing a struct", func() {
It("should create the type", func() {
typeDef := goldi.NewStructType(MockType{})
Expect(typeDef).NotTo(BeNil())
})
})
Context("with first argument beeing a pointer to struct", func() {
It("should create the type", func() {
typeDef := goldi.NewStructType(&MockType{})
Expect(typeDef).NotTo(BeNil())
})
})
It("should return an invalid type if more factory arguments were provided than the struct has fields", func() {
t := goldi.NewStructType(&MockType{}, "foo", true, "bar")
Expect(goldi.IsValid(t)).To(BeFalse())
Expect(t).To(MatchError("the struct MockType has only 2 fields but 3 arguments where provided"))
})
})
Describe("Arguments()", func() {
It("should return all factory arguments", func() {
args := []interface{}{"foo", true}
typeDef := goldi.NewStructType(MockType{}, args...)
Expect(typeDef.Arguments()).To(Equal(args))
})
})
Describe("Generate()", func() {
var (
config = map[string]interface{}{}
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
Context("without struct arguments", func() {
Context("when the factory is a struct (no pointer)", func() {
It("should generate the type", func() {
typeDef := goldi.NewStructType(MockType{})
Expect(typeDef.Generate(resolver)).To(BeAssignableToTypeOf(&MockType{}))
})
})
It("should generate the type", func() {
typeDef := goldi.NewStructType(&MockType{})
Expect(typeDef.Generate(resolver)).To(BeAssignableToTypeOf(&MockType{}))
})
It("should generate a new type each time", func() {
typeDef := goldi.NewStructType(&MockType{})
t1, err1 := typeDef.Generate(resolver)
t2, err2 := typeDef.Generate(resolver)
Expect(err1).NotTo(HaveOccurred())
Expect(err2).NotTo(HaveOccurred())
Expect(t1).NotTo(BeNil())
Expect(t2).NotTo(BeNil())
Expect(t1 == t2).To(BeFalse(), fmt.Sprintf("t1 (%p) should not point to the same instance as t2 (%p)", t1, t2))
// Just to make the whole issue more explicit:
t1Mock := t1.(*MockType)
t2Mock := t2.(*MockType)
t1Mock.StringParameter = "CHANGED"
Expect(t2Mock.StringParameter).NotTo(Equal(t1Mock.StringParameter),
"Changing two indipendently generated types should not affect both at the same time",
)
})
})
Context("with one or more arguments", func() {
It("should generate the type", func() {
typeDef := goldi.NewStructType(&MockType{}, "foo", true)
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(&MockType{}))
generatedMock := generatedType.(*MockType)
Expect(generatedMock.StringParameter).To(Equal("foo"))
Expect(generatedMock.BoolParameter).To(Equal(true))
})
It("should use the given parameters", func() {
typeDef := goldi.NewStructType(&MockType{}, "%param1%", "%param2%")
config["param1"] = "TEST"
config["param2"] = true
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(&MockType{}))
generatedMock := generatedType.(*MockType)
Expect(generatedMock.StringParameter).To(Equal("TEST"))
Expect(generatedMock.BoolParameter).To(Equal(true))
})
Context("when a type reference is given", func() {
Context("and its type matches the struct field type", func() {
It("should generate the type", func() {
container.RegisterType("foo", NewMockType)
typeDef := goldi.NewStructType(TypeForServiceInjection{}, "@foo")
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(&TypeForServiceInjection{}))
generatedMock := generatedType.(*TypeForServiceInjection)
Expect(generatedMock.InjectedType).To(BeAssignableToTypeOf(&MockType{}))
})
})
Context("and its type does not match the function signature", func() {
It("should return an error", func() {
container.RegisterType("foo", NewFoo)
typeDef := goldi.NewStructType(TypeForServiceInjection{}, "@foo")
_, err := typeDef.Generate(resolver)
Expect(err).To(MatchError(`the referenced type "@foo" (type *goldi_test.Foo) can not be used as field 1 for struct type goldi_test.TypeForServiceInjection`))
})
})
})
})
})
})
Output: foo_1: *goldi_test.Foo foo_2: *goldi_test.Foo foo_3: *goldi_test.Foo
func NewType ¶
func NewType(factoryFunction interface{}, factoryParameters ...interface{}) TypeFactory
NewType creates a new TypeFactory.
This function will return an invalid type if:
- the factoryFunction is nil or no function,
- the factoryFunction returns zero or more than one parameter
- the factoryFunctions return parameter is no pointer, interface or function type.
- the number of given factoryParameters does not match the number of arguments of the factoryFunction
Goldigen yaml syntax example:
my_type:
package: github.com/fgrosse/foobar
factory: NewType
args:
- "Hello World"
- true
Example ¶
package main
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/tarokamikaze/goldi"
)
func main() {
container := goldi.NewContainer(goldi.NewTypeRegistry(), map[string]interface{}{})
// register the type using the factory function NewMockTypeWithArgs and pass two arguments
container.Register("my_type", goldi.NewType(NewMockTypeWithArgs, "Hello World", true))
t := container.MustGet("my_type").(*MockType)
fmt.Printf("%#v", t)
}
var _ = Describe("type", func() {
It("should implement the TypeFactory interface", func() {
var factory goldi.TypeFactory
factory = goldi.NewType(NewFoo)
// if this compiles the test passes (next expectation only to make compiler happy)
Expect(factory).NotTo(BeNil())
})
Describe("goldi.NewType()", func() {
Context("with invalid factory function", func() {
It("should return an invalid type if the generator is no function", func() {
Expect(goldi.IsValid(goldi.NewType(42))).To(BeFalse())
})
It("should return an invalid type if the generator has no output parameters", func() {
Expect(goldi.IsValid(goldi.NewType(func() {}))).To(BeFalse())
})
It("should return an invalid type if the generator has more than one output parameter", func() {
Expect(goldi.IsValid(goldi.NewType(func() (*MockType, *MockType) { return nil, nil }))).To(BeFalse())
})
It("should allow struct return types for flexibility", func() {
Expect(goldi.IsValid(goldi.NewType(func() MockType { return MockType{} }))).To(BeTrue())
})
It("should not return an invalid type if the return parameter is an interface", func() {
Expect(goldi.IsValid(goldi.NewType(func() interface{} { return MockType{} }))).To(BeTrue())
})
It("should not return an invalid type if the return parameter is a function", func() {
Expect(goldi.IsValid(goldi.NewType(func() func() { return func() {} }))).To(BeTrue())
})
})
Context("without factory function arguments", func() {
Context("when no factory argument is given", func() {
It("should create the type", func() {
typeDef := goldi.NewType(NewMockType)
Expect(typeDef).NotTo(BeNil())
})
})
Context("when any argument is given", func() {
It("should return an invalid type", func() {
Expect(goldi.IsValid(goldi.NewType(NewMockType, "foo"))).To(BeFalse())
})
})
})
Context("with one or more factory function arguments", func() {
Context("when an invalid number of arguments is given", func() {
It("should return an invalid type", func() {
Expect(goldi.IsValid(goldi.NewType(NewMockTypeWithArgs))).To(BeFalse())
Expect(goldi.IsValid(goldi.NewType(NewMockTypeWithArgs, "foo"))).To(BeFalse())
Expect(goldi.IsValid(goldi.NewType(NewMockTypeWithArgs, "foo", false, 42))).To(BeFalse())
})
})
Context("when the wrong argument types are given", func() {
It("should return an invalid type", func() {
Expect(goldi.IsValid(goldi.NewType(NewMockTypeWithArgs, "foo", "bar"))).To(BeFalse())
Expect(goldi.IsValid(goldi.NewType(NewMockTypeWithArgs, true, "bar"))).To(BeFalse())
})
})
Context("when the correct argument number and types are given", func() {
It("should create the type", func() {
typeDef := goldi.NewType(NewMockTypeWithArgs, "foo", true)
Expect(typeDef).NotTo(BeNil())
})
})
Context("when the arguments are variadic", func() {
It("should create the type", func() {
typeDef := goldi.NewType(NewVariadicMockType, true, "ignored", "1", "two", "drei")
Expect(typeDef).NotTo(BeNil())
})
It("should return an invalid type if not enough arguments where given", func() {
t := goldi.NewType(NewVariadicMockType, true)
Expect(goldi.IsValid(t)).To(BeFalse())
Expect(t).To(MatchError("invalid number of input parameters for variadic function: got 1 but expected at least 3"))
})
})
})
})
Describe("Arguments()", func() {
It("should return all factory arguments", func() {
args := []interface{}{"foo", true}
typeDef := goldi.NewType(NewMockTypeWithArgs, args...)
Expect(typeDef.Arguments()).To(Equal(args))
})
})
Describe("Generate()", func() {
var (
config = map[string]interface{}{}
container *goldi.Container
resolver *goldi.ParameterResolver
)
BeforeEach(func() {
container = goldi.NewContainer(goldi.NewTypeRegistry(), config)
resolver = goldi.NewParameterResolver(container)
})
Context("without factory function arguments", func() {
It("should generate the type", func() {
typeDef := goldi.NewType(NewMockType)
Expect(typeDef.Generate(resolver)).To(BeAssignableToTypeOf(&MockType{}))
})
})
Context("with one or more factory function arguments", func() {
It("should generate the type", func() {
typeDef := goldi.NewType(NewMockTypeWithArgs, "foo", true)
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(&MockType{}))
generatedMock := generatedType.(*MockType)
Expect(generatedMock.StringParameter).To(Equal("foo"))
Expect(generatedMock.BoolParameter).To(Equal(true))
})
Context("when a type reference is given", func() {
Context("and its type matches the function signature", func() {
It("should generate the type", func() {
container.RegisterType("foo", NewMockType)
typeDef := goldi.NewType(NewTypeForServiceInjection, "@foo")
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(&TypeForServiceInjection{}))
generatedMock := generatedType.(*TypeForServiceInjection)
Expect(generatedMock.InjectedType).To(BeAssignableToTypeOf(&MockType{}))
})
})
Context("and its type does not match the function signature", func() {
It("should return an error", func() {
container.RegisterType("foo", NewFoo)
typeDef := goldi.NewType(NewTypeForServiceInjectionWithArgs, "@foo", "arg1", "arg2", true)
_, err := typeDef.Generate(resolver)
Expect(err).To(MatchError(`the referenced type "@foo" (type *goldi_test.Foo) can not be passed as argument 1 to the function signature goldi_test.NewTypeForServiceInjectionWithArgs(*goldi_test.MockType, string, string, bool)`))
})
})
})
Context("when the arguments are variadic", func() {
It("should generate the type", func() {
typeDef := goldi.NewType(NewVariadicMockType, true, "ignored", "1", "two", "drei")
Expect(typeDef).NotTo(BeNil())
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(&MockType{}))
generatedMock := generatedType.(*MockType)
Expect(generatedMock.BoolParameter).To(BeTrue())
Expect(generatedMock.StringParameter).To(Equal("1, two, drei"))
})
})
Context("when a func reference type is given", func() {
It("should generate the type", func() {
foo := &MockType{StringParameter: "Success!"}
container.InjectInstance("foo", foo)
typeDef := goldi.NewType(NewMockTypeFromStringFunc, "YEAH", "@foo::ReturnString")
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(NewMockType()))
Expect(generatedType.(*MockType).StringParameter).To(Equal("Success! YEAH"))
})
})
Context("when a func reference type is given as variadic argument", func() {
It("should generate the type", func() {
foo := &MockType{StringParameter: "Success!"}
container.InjectInstance("foo", foo)
typeDef := goldi.NewType(NewVariadicMockTypeFuncs, "@foo::ReturnString", "@foo::ReturnString")
generatedType, err := typeDef.Generate(resolver)
Expect(err).NotTo(HaveOccurred())
Expect(generatedType).To(BeAssignableToTypeOf(NewMockType()))
Expect(generatedType.(*MockType).StringParameter).To(Equal("Success! Success! "))
})
})
})
})
})
Output: &goldi_test.MockType{StringParameter:"Hello World", BoolParameter:true}
type TypeID ¶
type TypeID struct {
ID, Raw string
FuncReferenceMethod string
IsOptional bool
IsFuncReference bool
}
TypeID represents a parsed type identifier and associated meta data.
type TypeReferenceError ¶
type TypeReferenceError struct {
TypeID string
TypeInstance interface{}
// contains filtered or unexported fields
}
A TypeReferenceError occurs if you tried to inject a type that does not match the function declaration of the corresponding method.
type TypeRegistry ¶
type TypeRegistry map[string]TypeFactory
The TypeRegistry is effectively a map of typeID strings to TypeFactory
func NewTypeRegistry ¶
func NewTypeRegistry() TypeRegistry
NewTypeRegistry creates a new empty TypeRegistry
func (TypeRegistry) All ¶
func (r TypeRegistry) All() iter.Seq2[string, TypeFactory]
All returns an iterator over all registered type IDs and their factories This uses Go 1.24's range over func feature for memory-efficient iteration
func (TypeRegistry) Clone ¶
func (r TypeRegistry) Clone() TypeRegistry
Clone creates a deep copy of the registry using maps.Collect
func (TypeRegistry) CollectAll ¶
func (r TypeRegistry) CollectAll() map[string]TypeFactory
CollectAll efficiently collects all type registrations into a map using maps.Collect
func (TypeRegistry) CollectByType ¶
func (r TypeRegistry) CollectByType(targetType reflect.Type) []string
CollectByType efficiently collects type IDs matching a specific type
func (TypeRegistry) CollectFactories ¶
func (r TypeRegistry) CollectFactories() []TypeFactory
CollectFactories efficiently collects all factories into a slice using slices.Collect
func (TypeRegistry) CollectTypeIDs ¶
func (r TypeRegistry) CollectTypeIDs() []string
CollectTypeIDs efficiently collects all type IDs into a slice using slices.Collect
func (TypeRegistry) Factories ¶
func (r TypeRegistry) Factories() iter.Seq[TypeFactory]
Factories returns an iterator over all registered factories
func (TypeRegistry) FilterByType ¶
FilterByType returns an iterator over type IDs that match the given type
func (TypeRegistry) InjectInstance ¶
func (r TypeRegistry) InjectInstance(typeID string, instance interface{})
InjectInstance enables you to inject type instances. If instance is nil an error is returned
func (TypeRegistry) Register ¶
func (r TypeRegistry) Register(typeID string, typeDef TypeFactory)
Register saves a type under the given symbolic typeID so it can be retrieved later. It is perfectly legal to call Register multiple times with the same typeID. In this case you overwrite existing type definitions with new once
func (TypeRegistry) RegisterAll ¶
func (r TypeRegistry) RegisterAll(factories map[string]TypeFactory)
RegisterAll will register all given type factories under the mapped type ID It uses maps.Copy for efficient bulk registration
func (TypeRegistry) RegisterType ¶
func (r TypeRegistry) RegisterType(typeID string, factory interface{}, arguments ...interface{})
RegisterType is convenience method for TypeRegistry.Register It tries to create the correct TypeFactory and passes this to TypeRegistry.Register This function panics if the given generator function and arguments can not be used to create a new type factory.
type UnknownTypeReferenceError ¶
type UnknownTypeReferenceError struct {
TypeID string
// contains filtered or unexported fields
}
The UnknownTypeReferenceError occurs if you try to get a type by an unknown type id (type has not been registered).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
The goldigen binary See https://github.com/tarokamikaze/goldi#the-goldigen-binary
|
The goldigen binary See https://github.com/tarokamikaze/goldi#the-goldigen-binary |
|
Package validation provides simple validation of goldi containers
|
Package validation provides simple validation of goldi containers |
