Skip to content

Commit

Permalink
Merge branch 'release/1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul M Fox committed Jan 3, 2016
2 parents 52d6a42 + a55b1e8 commit b5ea4d8
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 10 deletions.
28 changes: 22 additions & 6 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ type Grapher interface {
// A default grapher to use in the public API
var graph Grapher = NewGraph()

// Fetch the current grapher instance
// Fetch the current grapher instance (in other words, get the global graph)
func GetGrapher() Grapher {
return graph
}

// Set a specific grapher instance
// Set a specific grapher instance, which will replace the global graph.
func SetGrapher(g Grapher) {
graph = g
}
Expand All @@ -35,27 +35,43 @@ func SetGrapher(g Grapher) {
// Public API
//////////////////////////////////////////////

// Insert a set of arbitrary objects into the
// application graph
// Insert zero or more objected into the graph, and then attempt to wire up any unmet
// dependencies in the graph.
//
// As explained in the main documentation (https://godoc.org/github.com/yourheropaul/inj),
// a graph consists of what is essentially a map of types to values. If the same type is
// provided twice with different values, the *last* value will be stored in the graph.
func Provide(inputs ...interface{}) error {
return graph.Provide(inputs...)
}

// Given a function, call it with arguments assigned
// from the graph. Additional arguments can be provided
// for the sake of utility.
//
// Inject() will panic if the provided argument isn't a function,
// or if the provided function accepts variadic arguments (because
// that's not currently supported in the scope of inj).
func Inject(fn interface{}, args ...interface{}) {
graph.Inject(fn, args...)
}

// Make sure that all provided dependencies have their
// requirements met, and return a list of errors if they
// don't.
// haven't. A graph is never really finalised, so Provide() and
// Assert() can be called any number of times.
func Assert() (valid bool, errors []string) {
return graph.Assert()
}

// Add zero or more datasources to the global graph
// Add any number of Datasources, DatasourceReaders or DatasourceWriters
// to the graph. Returns an error if any of the supplied arguments aren't
// one of the accepted types.
//
// Once added, the datasources will be active immediately, and the graph
// will automatically re-Provide itself, so that any depdendencies that
// can only be met by an external datasource will be wired up automatically.
//
func AddDatasource(ds ...interface{}) error {
return graph.AddDatasource(ds...)
}
79 changes: 79 additions & 0 deletions documentation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Package inj provides reflection-based dependency injection for structs and functions. Some parts of it will be familiar to
// anyone who's ever used https://github.com/facebookgo/inject; others bear a passing similarity to depdendency injection in
// Angular.js. It's designed for medium to large applications, but it works just fine for small apps too. It's especially
// useful if your project is is BDD/TDD-orientated.
//
// The essential premise of the package is that of object graphs. An object graph is just an index of objects organised by
// their explicitly nominated relationships. Depending on the application, a graph can be very simple - struct A depends on
// struct B - or extremely complicated. Package inj aims to simplify the process of creating and maintaining a graph of any
// complexity using out-of-the-box features of the Go language, and to povide easy access to the objects in the graph.
//
// A simple and unrealistically trivial example is this:
//
// package main
//
// import (
// "fmt"
// "github.com/yourheropaul/inj"
// )
//
// type ServerConfig struct {
// Port int `inj:""`
// Host string `inj:""`
// }
//
// func main() {
// config := ServerConfig{}
// inj.Provide(&config, 6060, "localhost")
//
// // The struct fields have now been set by the graph, and
// // this will print "localhost:6060"
// fmt.Printf("%s:%d",config.Host,config.Port)
// }
//
// To understand what's happening there, you have to perform a bit of counter-intuitive reasoning. Inj has a global graph
// (which is optional, and can be disabled with the noglobals build tag) that is accessed through the three main API functions –
// inj.Provide(), inj.Assert() and inj.Inject(). The first and most fundamental of those functions - inj.Provide() - inserts anything
// passed to it into the graph, and then tries to wire up any dependency requirements.
//
// Before we get on to what dependency requirements are, it's important to know how a graph works. It is, in its simplest form, a map
// of types to values. In the example above, the graph - after inj.Provide() is called - will have three entries:
//
// [ServerConfig] => (a pointer to the config variable)
// [int] => 6060
// [string] => "localhost"
//
// There can only be one entry for each type in a graph, but since Go allows new types to be created arbitrarly, that's not very much of
// a problem for inj. For example, if I wanted a second string-like type to be stored in the graph, I could simply type one:
//
// package main
//
// import (
// "github.com/yourheropaul/inj"
// )
//
// type MyString string
//
// func main() {
// var basicString string = "this is a 'normal' string"
// var myString MyString = "this is a typed string"
// inj.Provide(basicString,myString)
// }
//
// In that example, the graph would now contain two separate, stringer entities:
//
// [string] => "this is a 'normal' string"
// [MyString] => "this is a typed string"
//
// Back to depdendency requirements. The ServerConfig struct above has two, indicated by the inj:"" struct field tags (advanced usage of the
// package makes use of values in the tags, but we can ignore that for now). At the end of the inj.Provide() call, the graph is wired up –
// which essentially means finding values for all of the dependency requirements by type. The Port field of the ServerConfig struct requires
// and int, and the graph has one, so it's assigned; the Host field requires as string, and that can be assigned from the graph too.
//
// Obviously these examples are trivial in the extreme, and you'd probably never use the inj package in that way. The easiest way to understand
// the package for real-world applications is to refer to the example application: https://github.com/yourheropaul/inj/tree/master/example.
//
// For more general information, see the Wikipedia article for a technical breakdown of dependency injection:
// https://en.wikipedia.org/wiki/Dependency_injection.
//
package inj
4 changes: 4 additions & 0 deletions graph_assert.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package inj

// Make sure that all provided dependencies have their
// requirements met, and return a list of errors if they
// haven't. A graph is never really finalised, so Provide() and
// Assert() can be called any number of times.
func (g *Graph) Assert() (valid bool, errors []string) {

valid = true
Expand Down
2 changes: 1 addition & 1 deletion graph_inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// Given a function, call it with arguments from the graph.
// Throws a runtime erro,r in the form of a panic, on failure.
// Throws a runtime error in the form of a panic on failure.
func (g *Graph) Inject(fn interface{}, args ...interface{}) {

// Reflect the input
Expand Down
6 changes: 6 additions & 0 deletions graph_provide.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package inj

import "reflect"

// Insert zero or more objected into the graph, and then attempt to wire up any unmet
// dependencies in the graph.
//
// As explained in the main documentation (https://godoc.org/github.com/yourheropaul/inj),
// a graph consists of what is essentially a map of types to values. If the same type is
// provided twice with different values, the *last* value will be stored in the graph.
func (g *Graph) Provide(inputs ...interface{}) error {

for _, input := range inputs {
Expand Down
39 changes: 36 additions & 3 deletions readme.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

Are you troubled by dependency configuration issues in the middle of the night? Do you experience feelings of dread at the code boundaries of your application or during functional testing? Have you or your team ever used a global variable, exported package-level object or a build constraint hack? If the answer is *yes*, then don't wait another minute. Pick up your terminal and `go get github.com/yourheropaul/inj` today.

Come on and inject yourself before you wreck yourself.
Inject yourself before you wreck yourself.

### What *is* this thing?

Expand All @@ -15,7 +15,39 @@ Come on and inject yourself before you wreck yourself.

### How do I use it?

Check out the [example application](https://github.com/yourheropaul/inj/tree/master/example) in this repository. The API is small, and everything is demonstrated there. Technical documentation is also available on [godoc.org](https://godoc.org/github.com/yourheropaul/inj).
A simple and unrealistically trivial example is this:

```
package main
import (
"fmt"
"github.com/yourheropaul/inj"
)
type ServerConfig struct {
Port int `inj:""`
Host string `inj:""`
}
func main() {
config := ServerConfig{}
inj.Provide(&config, 6060, "localhost")
// The struct fields have now been set by the graph, and
// this will print "localhost:6060"
fmt.Printf("%s:%d", config.Host, config.Port)
}
```
There's a full explanation for this basic example in the [Godoc](http://localhost:6061/pkg/github.com/yourheropaul/inj/#Overview).
Obviously this example is trivial in the extreme, and you'd probably never use the the package in that way. The easiest way to understand
`inj` for real-world applications is to refer to the [example application](https://github.com/yourheropaul/inj/tree/master/example) in this repository. The API is small, and everything in the core API is demonstrated there.
### Dependency injection is great and everything, but I really want to be able to pull data directly from external services, not just the object graph.

You mean you want to read from a JSON or TOML config file, and inject the values into Go objects directly? Maybe you'd like to pull values from a DynamoDB instance and insert them into Go struct instances with almost zero code overhead?

That's what's `inj` is designed for! And what's more, intrepid programmer Adrian Duke has already done the leg work for you in his fantastic [configr](https://github.com/adrianduke/configr) package – see his readme for brief instructions.

### I want absolutely, positively no globals in my application. None. Can I do that with this package?

Expand All @@ -35,4 +67,5 @@ Finally, `inj.Provide()` is fairly slow, but it's designed to executed at runtim

### But how do I use it?

Seriously? I just explained that a minute ago.
Seriously? I just explained that a minute ago. Maybe look at the [example application](https://github.com/yourheropaul/inj/tree/master/example) or the [Godoc](http://localhost:6061/pkg/github.com/yourheropaul/inj/#Overview).

0 comments on commit b5ea4d8

Please sign in to comment.