inject

Runtime error-free dependency injection for Golang callbacks.

Use cases

Fetching values from contexts can lead to runtime errors that can only be tracked when you invoke the callback.

bar := req.Context.Value("foo").(barType)
// panic: interface conversion: interface is fooType, not barType

Maintaining huge value objects and sharing it via some package global variable can be insecure, because everyone has access to its attributes and even can change it not intentionally.

package config

var Config = ConfigVO{}

type ConfigVO struct {
    DBConn        *sql.DB
    Logger        *log.Logger
    PaymentConfig *payment.Config
    // etc.
}

Instead you declare only resources your callback needs, even if a callback has invalid arguments program panics at boot-up process decreasing amount of error-prone code:

dbConn := sql.Open("localhost:5432")
logger := log.New(os.Stderr, "", LstdFlags)

httpinject.With(handlerFoo, dbConn)
httpinject.With(handlerBar, logger, dbConn)
httpinject.With(handlerBaz, logger, dbConn) // panics, handlerBaz accepts log.Logger by value
httpinject.With(handlerBam, logger, dbConn) // panics, handlerBam doesn't accept any extra arguments

func handlerFoo(http.ResponseWriter, *http.Request, *sql.DB) {}
func handlerBar(http.ResponseWriter, *http.Request, *log.Logger, *sql.DB) {}
func handlerBaz(http.ResponseWriter, *http.Request, log.Logger, *sql.DB) {}
func handlerBam(http.ResponseWriter, *http.Request) {}

Usage

net/http
package main

import (
    "fmt"
    httpinject "github.com/amenzhinsky/inject/http"
    "net/http"
)

func main() {
    http.HandleFunc("/", httpinject.With(handler, 1, "foo"))
    http.ListenAndServe(":8080", nil)
}

func handler(w http.ResponseWriter, _ *http.Request, a int, s string) {
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "a: %d, s: %s\n", a, s)
}

Custom usage

Let's say with the echo webserver.

package main

import (
    "fmt"
    "github.com/amenzhinsky/inject"
    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    "net/http"
    "reflect"
)

var typ = reflect.TypeOf((*echo.HandlerFunc)(nil)).Elem()

func main() {
    e := echo.New()
    e.GET("/", with(handler, "Mr. President"))
    e.Run(standard.New(":8080"))
}

func handler(c echo.Context, name string) error {
    return c.String(http.StatusOK, fmt.Sprintf("Hello %s!\n", name))
}

func with(fn interface{}, v ...interface{}) echo.HandlerFunc {
    return inject.With(typ, fn, v...).Interface().(echo.HandlerFunc)
}