gimple

package module
v0.0.0-...-adea959 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2017 License: MIT Imports: 4 Imported by: 1

README

Gimple

Build Status Code Coverage

WARNING - 12/07/2017: This project has been refactored to be present a more Golang API (without panic on Get() and with a MustGet() method, for example). Because of this, you may need to update your code to follow the new API. Sorry for the inconvenience. :(

This project is a "port" of Pimple Dependency Injection container to Go.

The code of this project may not be in his state-of-art, but seems to be a great start to learn a few more about Go, and to avoid initializing a bunch of things in your application manually.

All the code is tested using Ginkgo and seems to be stable. Below is the documentation for the project:

Features

Good projects have good features. And because this here's the list of features that Gimple supports:

  • Define services;
  • Define factories;
  • Define parameters easily;
  • Defining services/parameters/factories from another files - because you should be able to split your configuration easily;
  • Simple API;
  • Allows extending services easily;
  • Allow to get the raw service creator easily;
  • Pure Go, no C code envolved;
  • Fully tested on each commit;
  • I already said that it have a really Simple API? :)

Installation

The installation of this package is very simple: In fact, it can be installed by just running:

    go get github.com/fjorgemota/gimple

Usage

Creating a Gimple Container is just a matter of creating a Gimple instance:

import "gimple"
container := gimple.NewGimple()

Gimple, as Pimple and many other dependency injections containers, manage two different kind of data: services and parameters.

Defining services

As Pimple describes, a service is an object that does something as part of a larger system. Examples of services: a database connection, a templating engine, or a mailer. Almost any global object can be a service.

Services in Gimple (and in Pimple too!) are defined by anonymous functions that return an instance of an object. Different from Pimple, however, here we need to call the method Set() on Gimple container, as Go offers no way to simulate map like behavior as in Pimple:

// define some services
container.Set('session_storage', func (c gimple.GimpleContainer) interface{} {
    return SessionStorage{'SESSION_ID'};
});

container.Set('session', func (c gimple.GimpleContainer) interface{} {
    session_storage := c.MustGet('session_storage').(SessionStorage)
    return Session{};
});

Notice that the anonymous function that define a service has access to the current container instance, allowing references to other services or parameters.

The objects are created on demand, just when you get them. The order of the definitions does not matter.

Using the defined services is very easy, too:

// get the session object
session := container.MustGet('session').(Session);

// the above call is roughly equivalent to the following code:
// storage = SessionStorage{'SESSION_ID'};
// session = Session{storage};

Defining factory services

By default, when you get a service, Gimple automatically cache it's value, returning always the same instance of it. If you want a different instance to be returned for all calls, wrap your anonymous function with the Factory() method:

container.Set('session', container.Factory(func (c gimple.GimpleContainer) interface{} {
    session_storage := c.MustGet('session_storage').(SessionStorage)
    return Session{session_storage};
}));

Now, each time you call container.MustGet('session'), a new instance of Session is returned for you.

Defining parameters

Defining a parameter allows to ease the configuration of your container from the outside and to store global values. In Gimple, parameters are defined as anything that it's not a function:

// define a parameter called cookie_name
container.Set('cookie_name', 'SESSION_ID');

If you change the session_storage service definition like below:

container.Set('session_storage', func (c gimple.GimpleContainer) interface{} {
    cookie_value := c.MustGet('cookie_name').(string)
    return SessionStorage{cookie_name};
});

You can now easily change the cookie name by overriding the cookie_name parameter instead of redefining the service definition.

Defining parameters based on environment variables

Do you wanna do define parameters in the container based on environment variables? It's okay! You can define it easily like that:

import "os" // At the top of the file
//define parameter based on environment variable
container.Set('cookie_name', os.Getenv(COOKIE_NAME));

Protecting parameters

Because Gimple see anything that is a function as a service, you need to wrap anonymous functions with the Protect() method to store them as parameters:

import "math/rand" // At the top of the file
container.Set('random_func', container.Protect(func (_ gimple.GimpleContainer) interface{} {
    return rand.Int();
}));

Modifying Services after Definition

In some cases you may want to modify a service definition after it has been defined. You can use the Extend() method to define additional code to be run on your service just after it is created:

container.Set('session_storage', func (c gimple.GimpleContainer) interface{} {
    cookie_name := c.MustGet('cookie_name').(string)
    return SessionStorage{cookie_name};
});

container.Extend('session_storage', func (old interface{}, c gimple.GimpleContainer) interface{} {
    storage := old.(SessionStorage)
    storage.SomeMethod();

    return storage;
});

The first argument is the name of the service to extend, the second a function that gets access to the object instance and the container.

Extending a Container

If you use the same libraries over and over, you might want to reuse some services from one project to the next one; package your services into a provider by implementing the following object structure by duck-typing:

type provider struct{}

func (p *provider) Register(c gimple.GimpleContainer) {
	// Define your services and parameters here
}

After creating a object with that structure, you can register it in the container:

container.Register(provider{});

Fetching the Service Creation Function

When you access an object, Gimple automatically calls the anonymous function that you defined, which creates the service object for you. If you want to get raw access to this function, but don't want to protect() that service, you can use the MustRaw() method to access the function directly:

container.Set("session", func (c gimple.GimpleContainer) interface{} {
    storage := c.MustGet('session_storage').(SessionStorage)
    return Session{storage};
});

sessionFunction := container.MustRaw("session").(func(gimple.Container) interface{});

Avoiding panics

When you use MustGet(), if there is no parameter or service with that key the library emits a panic() (because you must get that parameter or service).

Sometimes, however, you may decide to avoid that behavior and manage the error gracefully, as Go encourages. To do that, you can use the method Get, which returns the specified value AND an error (if any). So, instead of doing something like:

session := container.MustGet("session").(Session)

You can do something like:

session_value, err := container.MustGet("session")
if err == nil {
    session := session_value.(Session)
}

The same apply to MustRaw, so, to avoid panics, you can use:

session_value, err := container.Raw("session")
if err == nil {
    session_function := session_value.(func(gimple.Container) interface{});
}

Instead of just:

sessionFunction := container.MustRaw("session").(func(gimple.Container) interface{});

Last, but not least important: Customization

Do you wanna to customize Gimple's functionally? You can! Just extend it using ES6's class syntax:

type ABigContainer struct{
    *Gimple
}

// Overwrite any of the Gimple's methods here

container := ABigContainer{}; 

Good customization. :)

Documentation

Overview

Package gimple is a Dependency Injection Container developed in Golang with features highly inspired on Pimple, a micro dependency injection container for PHP.

Some of it's features is:

- Define services; - Define factories; - Define parameters easily; - Allow services to depend directly on interfaces, and not on concrete struct; - Defining services/parameters/factories from another files - because you should be able to split your configuration easily; - Simple API; - Allows extending services easily; - Allow to get the raw service creator easily; - Pure Go, no C code envolved; - Fully tested on each commit; - I already said that it have a really Simple API? :)

Index

Constants

This section is empty.

Variables

View Source
var (
	Undefined = errors.New("Not defined")
)

Functions

This section is empty.

Types

type Container

type Container interface {
	Get(key string) (interface{}, error)
	MustGet(key string) interface{}
	Set(key string, val interface{})
	Has(key string) bool
	Keys() []string
	Factory(fn func(c Container) interface{}) func(c Container) interface{}
	Protect(fn func(c Container) interface{}) func(c Container) interface{}
	Extend(key string, e Extender) error
	MustExtend(key string, e Extender)
	ExtendFunc(key string, f ExtenderFunc) error
	MustExtendFunc(key string, f ExtenderFunc)
	Register(provider Registerer)
	RegisterFunc(fn RegisterFunc)
	Raw(key string) (interface{}, error)
	MustRaw(key string) interface{}
}

func New

func New(values ...map[string]interface{}) Container

func NewGimple

func NewGimple() Container

func NewGimpleWithValues

func NewGimpleWithValues(values map[string]interface{}) Container

func NewWithValues

func NewWithValues(values map[string]interface{}) Container

type Error

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

func (Error) Error

func (self Error) Error() string

type Extender

type Extender interface {
	Extend(old interface{}, c Container) interface{}
}

type ExtenderFunc

type ExtenderFunc func(old interface{}, c Container) interface{}

func (ExtenderFunc) Extend

func (f ExtenderFunc) Extend(old interface{}, c Container) interface{}

type Gimple

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

func (*Gimple) Extend

func (container *Gimple) Extend(key string, e Extender) error

func (*Gimple) ExtendFunc

func (container *Gimple) ExtendFunc(key string, f ExtenderFunc) error

func (*Gimple) Factory

func (container *Gimple) Factory(fn func(c Container) interface{}) func(c Container) interface{}

func (*Gimple) Get

func (container *Gimple) Get(key string) (interface{}, error)

func (*Gimple) Has

func (container *Gimple) Has(key string) bool

func (*Gimple) Keys

func (container *Gimple) Keys() []string

func (*Gimple) MustExtend

func (container *Gimple) MustExtend(key string, e Extender)

func (*Gimple) MustExtendFunc

func (container *Gimple) MustExtendFunc(key string, f ExtenderFunc)

func (*Gimple) MustGet

func (container *Gimple) MustGet(key string) interface{}

func (*Gimple) MustRaw

func (container *Gimple) MustRaw(key string) interface{}

func (*Gimple) Protect

func (container *Gimple) Protect(fn func(c Container) interface{}) func(c Container) interface{}

func (*Gimple) Raw

func (container *Gimple) Raw(key string) (interface{}, error)

func (*Gimple) Register

func (container *Gimple) Register(provider Registerer)

func (*Gimple) RegisterFunc

func (container *Gimple) RegisterFunc(fn RegisterFunc)

func (*Gimple) Set

func (container *Gimple) Set(key string, val interface{})

type RegisterFunc

type RegisterFunc func(c Container)

func (RegisterFunc) Register

func (fn RegisterFunc) Register(c Container)

type Registerer

type Registerer interface {
	Register(container Container)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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