func main() { app := Application{} /* This is the first of three `inj` API calls. (The others are `inj.Assert()` and `inj.Inject`, more on those later). `inj.Provide()` is a variadic function that takes any number of `interface{}`s and inserts then into a list of globally-available objects call the application graph. After each call to `inj.Provide()`, the graph is 'connected', meaning that any structs with depdendencies (that is, structs containing fields with `inj:""` tags) have their dependent fields assigned to nodes on the graph. This how how we'll assign values to the three fields of the `Application` struct. You can call `inj.Provide()` as many times as you like, which means that it's possible to build a cascade of dependencies from imported packages using the `init()` function. Be careful with that, because `inj.Provide()` is where all the heavy lifting is done, and it's not fast enough to execute for each request or operation. Generally it should be called at startup, like we're doing here. */ inj.Provide( // A pointer to our `Application` is necessary to fulfil its depdendencies. (It must be a pointer so the values // can be assigned). &app, // Objects are stored in the graph, even if they're stored anywhere else. Here we're creating a channel. make(ExitChan), // The same with the `Config` struct; just create a new instance for the graph and move on. &Config{}, // As noted above, `Logger` happens to match the signature of `fmt.Printf`, so we use that. fmt.Printf, // This is that 'Hello, I love %s' writer. `Application` doesn't use it, but other graph-dependent functions do. WriteResponse, ) /* Once you've made all your calls to `inj.Provide()`, it's time to make sure that all the dependencies were met. In `inj`, that's fairly simple: use the `inj.Assert()` function. It returns a boolean and a slice of strings. If the boolean is false, then the slice will be populated with all the errors that occured during struct injection. */ if valid, messages := inj.Assert(); !valid { fmt.Println(messages) os.Exit(1) } /* If we get to this point, we know that `app` has had all its dependencies met, so we can call `app.run()`. */ app.run() }
func main() { // Wont work if GOPATH contains multiple DIRs path := filepath.Join(os.Getenv("GOPATH"), "src/github.com/adrianduke/configr/examples/simple-json-inj/config.json") configr.AddSource(configr.NewFile(path)) if err := configr.Parse(); err != nil { fmt.Println(err) os.Exit(1) } email := Email{} inj.Provide(&email) // Informs inj to perform DI on given instance inj.AddDatasource(configr.GetConfigr()) // Provides inj with a datasource to query if valid, errors := inj.Assert(); !valid { // Triggers the inj DI process fmt.Println(errors) os.Exit(1) } fmt.Println("> Email Address:", email.FromAddress) fmt.Println("> Subject:", email.Subject) fmt.Println("> Max Retries:", email.MaxRetries) fmt.Println("> Retry on Fail:", email.RetryOnFail) }
/* This is the main test: add all the dependencies to the global graph and run the application. We can use the http package from the standard library to make sure the responses from the server are correct, and shut down the server properly when we're done. */ func Test_Application(t *testing.T) { app := Application{} exit := make(ExitChan) // Set up the graph, much the same was as in main.go – and exactly as in the test // above, only this time globally inj.Provide( &app, // This time it is a pointer exit, // `ExitChan`, this time shared &MockConfig{}, // Use the mock config MockLogger, // `Logger`, as above MockWriteResponse, // Our specialised response writer t, // Pointer to `testing.T`, which is now going to be used ) // Make sure everything was loaded properly again if valid, messages := inj.Assert(); !valid { t.Fatalf("inj.Assert() failed: %s", messages) } // Run the application in its own goroutine. It should stay running until it's explicitly // shut down go app.run() // Here we make some requests to the application, and check the responses. Again, there are // better ways to choose test strings tests := []string{ "fulfilment", "doubt", "futility", "inetivatable narrative hijacking", } for _, input := range tests { res, err := http.Get(fmt.Sprintf("http://localhost:%d/%s", MOCK_HTTP_PORT, input)) if err != nil { t.Fatalf("http.Get(): %s", err) } body, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { log.Fatal(err) t.Fatalf("ioutil.ReadAll %s", err) } t.Logf("Testing %s...", input) if g, e := res.StatusCode, 200; g != e { t.Errorf("Expected status code %d, got %d", g, e) } if g, e := string(body), input; g != e { t.Fatalf("Got %s, expected %s") } } // Kill the app exit <- struct{}{} }