Skip to content

fgrosse/goldi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Coverage Status GoDoc license Join the chat at https://gitter.im/fgrosse/goldi

Goldi: lazy dependency injection framework for go.

This library enables you to build your applications based on a dependency injection container. It helps to make your code modular, flexible and ensures that you can reuse components easily.

If you are familiar with the Symfony dependency injection framework you should feel at home here.

The goldi API

Use go get to get the goldi API:

$ go get github.com/fgrosse/goldi

No additional dependencies are required to use the library. The full documentation is available at godoc.org. It is almost complete and includes a lot of examples on how to use goldi.

Usage

First you need to define the types you are going to use later

import (
    "github.com/fgrosse/goldi"
    "github.com/fgrosse/goldi/validation"
)

// create a new container when your application loads
registry := goldi.NewTypeRegistry()
config := map[string]interface{}{
    "some_parameter": "Hello World",
    "timeout":        42.7,
}
container := goldi.NewContainer(registry, config)

// now define the types you want to build using the di container
// you can use simple structs
container.RegisterType("logger", &SimpleLogger{})
container.RegisterType("api.geo.client", new(GeoClient), "http://example.com/geo:1234")

// you can also use factory functions and parameters
container.RegisterType("acme_corp.mailer", NewAwesomeMailer, "first argument", "%some_parameter%")

// dynamic or static parameters and references to other services can be used as arguments
container.RegisterType("renderer", NewRenderer, "@logger")

// closures and functions are also possible
container.Register("http_handler", goldi.NewFuncType(func(w http.ResponseWriter, r *http.Request) {
    // do amazing stuff
}))

// once you are done registering all your types you should probably validate the container
validator := validation.NewContainerValidator()
validator.MustValidate(container) // will panic, use validator.Validate to get the error

// whoever has access to the container can request these types now
logger := container.MustGet("logger").(LoggerInterface)
logger.DoStuff("...")

// in the tests you might want to exchange the registered types with mocks or other implementations
container.RegisterType("logger", NewNullLogger)

// if you already have an instance you want to be used you can inject it directly
myLogger := NewNullLogger()
container.InjectInstance("logger", myLogger)

The types are build lazily. This means that the logger will only be created when you ask the container for it the first time. Also all built types are singletons. This means that if you call container.Get("typeID")two times you will always get the same instance of whatever typeID stands for.

More detailed usage examples and a list of features will be available eventually.

The goldigen binary

If you are used to frameworks like Symfony you might want to define your types in an easy to maintain yaml file. You can do this using goldigen.

Use go get to install the goldigen binary:

$ go get github.com/fgrosse/goldi/goldigen

Goldigen depends on gopkg.in/yaml.v2 (LGPLv3) for the parsing of the yaml files and Kingpin (MIT licensed) for the command line flag parsing.

You then need to define your types like this:

types:
    logger:
        package: github.com/fgrosse/goldi-example/lib
        type: SimpleLogger

    my_fancy.client:
        package: github.com/fgrosse/goldi-example/lib
        type: Client
        factory: NewDefaultClient
        arguments:
            - "%client_base_url%"   # As in the API you can use parameters here
            - "@logger"             # You can also reference other types 

    time.clock:
        package: github.com/fgrosse/goldi-example/lib/mytime
        type: Clock
        factory: NewSystemClock
        
    http_handler:
        package: github.com/fgrosse/servo/example
        func:    HandleHTTP         # You can register functions as types using the "func" keyword

Now you have your type configuration file you can use goldigen like this:

$ goldigen --in config/types.yml --out lib/dependency_injection.go

This will generate the following output and write it to lib/dependency_injection.go:

//go:generate goldigen --in "../config/types.yml" --out "dependency_injection.go" --package github.com/fgrosse/goldi-example/lib --function RegisterTypes --overwrite --nointeraction
package lib

import (
	"github.com/fgrosse/goldi"
	"github.com/fgrosse/goldi-example/lib/mytime"
	"github.com/fgrosse/servo/example"
)

// RegisterTypes registers all types that have been defined in the file "../config/types.yml"
//
// DO NOT EDIT THIS FILE: it has been generated by goldigen v0.9.9.
// It is however good practice to put this file under version control.
// See https://github.com/fgrosse/goldi for what is going on here.
func RegisterTypes(types goldi.TypeRegistry) {
	types.RegisterAll(map[string]goldi.TypeFactory{
        "http_handler":    goldi.NewFuncType(example.HandleHTTP),
        "logger":          goldi.NewStructType(new(SimpleLogger)),
        "my_fancy.client": goldi.NewType(NewDefaultClient, "%client_base_url%", "@logger"),
        "time.clock":      goldi.NewType(mytime.NewSystemClock),
	})
}

As you might have noticed goldigen has created a go generate comment for you. Next time you want to update dependency_injection.go you can simply run go generate.

Goldigen tries its best to determine the output files package by looking into your GOPATH. In certain situations this might not be enough so you can set a package explicitly using the --package parameter.

For a full list of goldigens flags and parameters try:

$ goldigen --help

Now all you need to to is to create the di container as you would just using the goldi API and then somewhere in the bootstrapping of your application call.

RegisterTypes(registry)

If you have a serious error in your type registration (like returning more than one result from your type factory method) goldi defers error handling by return an invalid type. You can check for invalid types with the ContainerValidator or by using goldi.IsValid(TypeFactory) directly. Using the ContainerValidator is always the preferred option since it will check for a wide variety of bad configurations like undefined parameters or circular type dependencies.

Note that using goldigen is completely optional. If you do not like the idea of having an extra build step for your application just use goldis API directly.

License

Goldi is licensed under the the MIT license. Please see the LICENSE file for details.

Contributing

Any contributions are always welcome (use pull requests). For each pull request make sure that you covered your changes and additions with ginkgo tests. If you are unsure how to write those just drop me a message.

Please keep in mind that I might not always be able to respond immediately but I usually try to react within the week ☺.

About

Goldi: lazy dependency injection framework for go.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages