modules

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2017 License: MIT Imports: 13 Imported by: 1

README

Go Modules GoDoc Build Status Go Report Card

A dependency injection library using struct tags.

Overview

This library simplifies the wiring of an application by injecting dependencies between modules.

A module is a go struct containing fields tagged with 'inject' or 'provide' keys. When a set of modules are bound, fields tagged 'inject' are set with values from corresponding fields tagged 'provide', respecting type and name. Provided fields may be set normally prior to binding, during binding from a module's Provide method, or from Injectors triggered by additional tag keys.

How to Use

Modules

A module is any tagged struct. The 'inject' and 'provide' tag keys are treated specially. Other tag keys may be registered with a Binder to trigger Injectors. Unexported fields, and fields without recognized tags will be ignored during binding.

type simpleModule struct {
  // Requires a string value named 'injectMe' to be injected.
  FieldA string 'inject:"injectMe"'
  // Provides a string value named 'provideMe'.
  FieldB int 'provide:"provideMe"'
  // These fields are ignored by the Binder.
  FieldC bool
  fieldD string
}

When simpleModule is bound, it provides the string value named 'provideMe' via FieldB to the Binder, and expects the string dependency named 'injectMe' to be provided by another module and injected into FieldA. FieldC and fieldD will be ignored by the Binder.

Providers

There are a few different ways for a module to provide values.

Fields may be set normally prior to binding.

module := struct {
  FieldA string 'provide:"provideMe"'
} {
  FieldA: "value"
}
// or
module.FieldA = "value"

Modules implementing the Provider interface may set fields from the Provide method.

type module struct {
  Field string 'inject:"injectMe"'
  Connection func() Connection 'provide:"provideMe"'
}
// Implements modules.Provider
func (m *Module) Provide() error {
  m.ConnectionProvider = func() Connection {
    return NewConnection(m.Field)
  }
  return nil
}

The Provide method is called during binding. Injected fields have not yet necessarily been set when Provide is called, so they may not be accessed directly, but they may be closed over.

Additionally, a Binder may be configured to recognize certain tag keys and call an Injector to set a value. The 'literal' tag key is built-in, and parses string tag values into standard supported types.

type module struct {
  FieldA string 'provide:"stringField" literal:"someString"'
  FieldB int    'provide:"intField" literal:"10"'
  FieldC complex128 'provide:"complexField" literal:"-1,1"'
}

Other built-in tag keys include:

  • 'env' for environment variables
  • 'file' for os.File handles, and decoding of txt, json, xml, and gob
  • 'flag' for command line arguments
Binders

Modules are bound using a Binder. Binders are created with the NewBinder function, which optionally accepts functional option arguments.

binder := modules.NewBinder(modules.LogWriter(os.Stdout))

This binder logs information to stdout.

The Bind method binds a set of modules. All binding and injection occurs during this call. Modules implementing Provider will have their Provide method called. Exported fields are inspected for 'provide', 'inject' or other registered tag keys.

_ := binder.Bind(appModule, dataModule, serviceModule)

This call binds 3 modules. Each module's provided fields are available for injection into any module.

Tags and Injectors

The functional option Injectors can be used to map tag keys (anything besides "provide" and "inject") to custom or third party Injectors.

injectors := modules.Injectors(map[string]Injector{
  "customTag": customInjector,
})
binder := modules.NewBinder(injectors)
module := struct{
  FieldA CustomType 'provide:"someField" customTag:"tagValueArgument"'
}
_ := binder.Bind(module)

When this module is bound, customInjector may set the value of FieldA based on the tag value "tagValueArgument".

The Injector interface is defined in the inject package.

// An Injector sets a value based on a string.
type Injector interface {
	// May set a value based on string.
	// Returns (true, nil) when a value has been set, or (false, nil) when a value has not been set (e.g. environment
	// variable not set, file not found, etc.).
	Inject(reflect.Value, string) (bool, error)
}

If a field is tagged with multiple keys, Inject will be called for each Injector until one sets the value.

module := struct{
  Field string `provide:"setting" flag:"setting" env:"SETTING" literal:"defaultValue"`
}

This module provides a string value named 'setting', which may be set via a command-line flag or environment variable, and which falls back to the default literal 'defaultValue'.

See the GoDoc for more api documentation, and a working example.

Documentation

Overview

The modules package implements binders for dependency injection of tagged struct fields.

Example
package main

import (
	"fmt"
)

// The KVClient interface models a simple key/value store.
type KVClient interface {
	Get(key string) string
	Put(key, value string)
}

// A MapDBClient is a simple mock KVClient implementation backed by a map and configured with a default value for missing keys.
type MapDBClient struct {
	defaultValue string
	db           map[string]string
}

func (client *MapDBClient) Get(key string) string {
	if value, ok := client.db[key]; ok {
		return value
	} else {
		return client.defaultValue
	}
}

func (client *MapDBClient) Put(key, value string) {
	client.db[key] = value
}

// A service module has a 'GetData' service which utilizes an injected DBClient.
type ServiceModule struct {
	KVClient KVClient `inject:""`
}

func (service *ServiceModule) GetData(key string) string {
	return service.KVClient.Get(key)
}

func (service *ServiceModule) StoreData(key, value string) {
	service.KVClient.Put(key, value)
}

type defaultValue string

// This data module provides a KVClient.
type DataModule struct {
	DefaultValue defaultValue
	KVClient     KVClient `provide:""`
}

func (data *DataModule) Provide() error {
	data.KVClient = &MapDBClient{defaultValue: string(data.DefaultValue), db: make(map[string]string)}
	return nil
}

func main() {
	serviceModule := &ServiceModule{}

	dataModule := &DataModule{DefaultValue: "default"}

	binder := NewBinder()
	if err := binder.Bind(serviceModule, dataModule); err != nil {
		panic(err)
	}

	fmt.Println(serviceModule.GetData("key"))

	serviceModule.StoreData("key", "value")
	fmt.Println(serviceModule.GetData("key"))

}
Output:

default
value

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AnnotatedError

type AnnotatedError struct {
	// contains filtered or unexported fields
}

An AnnotatedError holds a message and wraps another error.

func (*AnnotatedError) Error

func (e *AnnotatedError) Error() string

type Binder

type Binder struct {
	// contains filtered or unexported fields
}

A Binder holds a configuration for module binding.

func NewBinder

func NewBinder(options ...BinderOption) *Binder

NewBinder initializes a new Binder instance, and applies options.

func (*Binder) Bind

func (b *Binder) Bind(modules ...interface{}) error

Bind binds modules. Calls Provide() on modules implementing Provider, calls inject.Injectors for tagged fields, and injects provided fields.

type BinderOption

type BinderOption interface {
	// contains filtered or unexported methods
}

A functional option for configuring a Binder.

type BindingError

type BindingError struct {
	// contains filtered or unexported fields
}

A BindingError indicates failure during binding. Holds one or more errors which prevented binding.

func (*BindingError) Error

func (e *BindingError) Error() string

type Injectors

type Injectors map[string]inject.Injector

Injectors is a functional option that adds mapped Injectors to a Binder.

type Logger

type Logger struct {
	io.Writer
}

Logger is a functional option that sets a Binder's logger.

type Provider

type Provider interface {
	// Provide is called once to set provided fields.
	// Returns nil for success, or an error in the case of failed binding.
	// This method is called prior to field injection, so injected fields may not be directly referenced, but may be closed over.
	Provide() error
}

A Provider is a binding module that implements the Provide() method. When a Provider is bound, Provide() will be called (prior to having fields injected).

Directories

Path Synopsis
Package inject contains code for working with Injectors.
Package inject contains code for working with Injectors.
env
Package env provides a inject.Injector to set values from environment variables.
Package env provides a inject.Injector to set values from environment variables.
file
Package file provides an inject.Injector for file input.
Package file provides an inject.Injector for file input.
flag
Package flag provides an inject.Injector to set values from command line flags.
Package flag provides an inject.Injector to set values from command line flags.
literal
Package literal provides an inject.Injector that parses string literals into values.
Package literal provides an inject.Injector that parses string literals into values.
Package tags contains code for parsing struct tags.
Package tags contains code for parsing struct tags.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL