Skip to content

Commit 3f8ae41

Browse files
committed
First commit.
0 parents  commit 3f8ae41

19 files changed

+832
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.out
2+
3+
vendor/

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 Alexander Sedelnikov
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Go-Inversify - Dependency Injection Tool for Go
2+
3+
Yet another dependency injection system for Go inspired by [InversifyJS](https://github.com/inversify/InversifyJS).
4+
5+
## Installation
6+
7+
#### Go get
8+
9+
```
10+
go get github.com/alekns/go-inversify
11+
```
12+
13+
## Examples
14+
15+
#### Values
16+
17+
```
18+
container := inversify.Container()
19+
20+
container.Bind(1).To("Hello")
21+
container.Bind(2).To(" world")
22+
container.Bind(3).ToFactory(func (word1, word2, optDep Any) Any {
23+
return word1.(string) + word2.(string), nil
24+
}, 1, 2, inversify.Optional(3))
25+
26+
// or
27+
28+
container.Bind(3).ToTypedFactory(func (word1, word2 string, optDep Any) string {
29+
// optDep == nil
30+
return word1 + word2, nil
31+
}, 1, 2, inversify.Optional(3))
32+
33+
container.IsBound(3) == true
34+
container.Get(3).(string) == "Hello world"
35+
```

auto/method.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package auto
2+

auto/reflection.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package auto
2+

auto/struct.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package auto
2+

binder.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package inversify
2+
3+
import "sync"
4+
5+
// Binding holds factory and dependencies
6+
type Binding struct {
7+
once sync.Once
8+
factory FactoryFunc
9+
dependencies NAny
10+
}
11+
12+
// To binds to any object that can converted interface{}
13+
func (b *Binding) To(obj Any) *Binding {
14+
b.factory = func() (Any, error) {
15+
return obj, nil
16+
}
17+
18+
return b
19+
}
20+
21+
// ToFactory binds to abstract function with specified dependencies
22+
func (b *Binding) ToFactory(factoryMethod Any, dependencies ...Any) *Binding {
23+
return b.toFactoryMethod(wrapAbstractApplyFuncAsSlice(factoryMethod), dependencies...)
24+
}
25+
26+
// ToTypedFactory binds to typed function with specified dependencies
27+
func (b *Binding) ToTypedFactory(factoryMethod Any, dependencies ...Any) *Binding {
28+
return b.toFactoryMethod(wrapTypedApplyFuncAsSlice(factoryMethod), dependencies...)
29+
}
30+
31+
func (b *Binding) toFactoryMethod(factoryMethod func([]Any) (Any, error), dependencies ...Any) *Binding {
32+
b.dependencies = dependencies
33+
emptyDeps := []Any{}
34+
depsCount := len(dependencies)
35+
36+
b.factory = func() (Any, error) {
37+
if depsCount == 0 {
38+
return factoryMethod(emptyDeps)
39+
}
40+
var err error
41+
resolvedDeps := make([]Any, depsCount, depsCount)
42+
for inx, dep := range b.dependencies {
43+
resolvedDeps[inx], err = dep.(FactoryFunc)()
44+
if err != nil {
45+
return nil, err
46+
}
47+
}
48+
return factoryMethod(resolvedDeps)
49+
}
50+
51+
return b
52+
}
53+
54+
// InSingletonScope declares dependency as singleton
55+
func (b *Binding) InSingletonScope() {
56+
orig := b.factory
57+
var instance Any
58+
var err error
59+
b.factory = func() (Any, error) {
60+
if instance == nil {
61+
b.once.Do(func() {
62+
instance, err = orig()
63+
})
64+
}
65+
return instance, err
66+
}
67+
}

binder_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package inversify
2+
3+
import (
4+
"errors"
5+
"sync"
6+
"testing"
7+
8+
"github.com/stretchr/testify/suite"
9+
)
10+
11+
type BindingTestSuite struct {
12+
suite.Suite
13+
}
14+
15+
func defaultDepVal(val Any) FactoryFunc { return func() (Any, error) { return val, nil } }
16+
func defaultDepError(err error) FactoryFunc { return func() (Any, error) { return nil, err } }
17+
18+
func (t *BindingTestSuite) TestBindValue() {
19+
binding := &Binding{sync.Once{}, nil, NAny{}}
20+
21+
binding.To("hello")
22+
val, err := binding.factory()
23+
24+
t.Equal("hello", val)
25+
t.NoError(err)
26+
}
27+
28+
func (t *BindingTestSuite) TestBindAbstractFactory() {
29+
binding := &Binding{sync.Once{}, nil, NAny{}}
30+
31+
binding.ToFactory(func(a, b Any) (Any, error) {
32+
return a.(string) + b.(string), nil
33+
}, defaultDepVal("1"), defaultDepVal("2"))
34+
val, err := binding.factory()
35+
36+
t.Equal("12", val)
37+
t.NoError(err)
38+
}
39+
40+
func (t *BindingTestSuite) TestBindAbstractFactoryError() {
41+
binding := &Binding{sync.Once{}, nil, NAny{}}
42+
43+
binding.ToFactory(func(a, b Any) (Any, error) {
44+
return a.(string) + b.(string), nil
45+
}, defaultDepVal("1"), defaultDepError(errors.New("error")))
46+
val, err := binding.factory()
47+
48+
t.Nil(val)
49+
t.Error(err)
50+
}
51+
52+
func (t *BindingTestSuite) TestBindTypedFactory() {
53+
binding := &Binding{sync.Once{}, nil, NAny{}}
54+
55+
counter := 0
56+
binding.ToTypedFactory(func(a, b string) (string, error) {
57+
counter++
58+
return a + b, nil
59+
}, defaultDepVal("1"), defaultDepVal("2"))
60+
val, err := binding.factory()
61+
val, err = binding.factory()
62+
63+
t.Equal("12", val)
64+
t.NoError(err)
65+
t.Equal(2, counter)
66+
}
67+
68+
func (t *BindingTestSuite) TestBindTypedFactorySingleton() {
69+
binding := &Binding{sync.Once{}, nil, NAny{}}
70+
71+
counter := 0
72+
binding.ToTypedFactory(func(a, b string) (string, error) {
73+
counter++
74+
return a + b, nil
75+
}, defaultDepVal("1"), defaultDepVal("2")).InSingletonScope()
76+
val, err := binding.factory()
77+
val, err = binding.factory()
78+
79+
t.Equal("12", val)
80+
t.NoError(err)
81+
t.Equal(1, counter)
82+
}
83+
84+
func (t *BindingTestSuite) TestBindTypedFactoryNoDeps() {
85+
binding := &Binding{sync.Once{}, nil, NAny{}}
86+
87+
binding.ToTypedFactory(func() (string, error) {
88+
return "12", nil
89+
})
90+
val, err := binding.factory()
91+
92+
t.Equal("12", val)
93+
t.NoError(err)
94+
}
95+
96+
func TestBindingSute(t *testing.T) {
97+
suite.Run(t, new(BindingTestSuite))
98+
}

builder.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package inversify
2+
3+
import "fmt"
4+
5+
func buildContainerImpl(c *containerImpl) {
6+
for _, bind := range c.factories {
7+
for inx, dep := range bind.dependencies {
8+
optdep, hasOpt := dep.(optionalBind)
9+
if hasOpt {
10+
dep = optdep.dep
11+
}
12+
13+
b, hasDep := c.findFactory(dep)
14+
if hasDep {
15+
bind.dependencies[inx] = b.factory
16+
} else {
17+
if hasOpt {
18+
bind.dependencies[inx] = FactoryFunc(func() (Any, error) {
19+
return nil, nil
20+
})
21+
} else {
22+
panic(fmt.Sprintf("depending %+v not found", dep))
23+
}
24+
}
25+
}
26+
}
27+
}

builder_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package inversify
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/suite"
7+
)
8+
9+
type FactoryTestSuite struct {
10+
suite.Suite
11+
}
12+
13+
func (t *FactoryTestSuite) TestBasic() {
14+
}
15+
16+
func TestFactorySute(t *testing.T) {
17+
suite.Run(t, new(FactoryTestSuite))
18+
}

container.go

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package inversify
2+
3+
// IContainer .
4+
type IContainer interface {
5+
// Bind .
6+
Bind(Any) *Binding
7+
8+
// Get .
9+
Get(Any) (Any, error)
10+
// IsBound .
11+
IsBound(Any) bool
12+
13+
Build()
14+
15+
// Merge with another container
16+
Merge(IContainer) IContainer
17+
// SetParent supports for hierarchical DI systems
18+
SetParent(IContainer)
19+
20+
// Snapshot .
21+
// Snapshot() IContainer
22+
}
23+
24+
type optionalBind struct {
25+
dep Any
26+
}
27+
28+
// Optional .
29+
func Optional(dep Any) Any {
30+
return optionalBind{dep}
31+
}
32+
33+
type containerImpl struct {
34+
parent *containerImpl
35+
factories map[Any]*Binding
36+
}
37+
38+
func (c *containerImpl) Bind(symbol Any) *Binding {
39+
b := &Binding{}
40+
c.factories[symbol] = b
41+
return b
42+
}
43+
44+
func (c *containerImpl) findFactory(symbol Any) (*Binding, bool) {
45+
factory, ok := c.factories[symbol]
46+
47+
if !ok {
48+
if c.parent != nil {
49+
return c.parent.findFactory(symbol)
50+
}
51+
52+
return nil, false
53+
}
54+
55+
return factory, true
56+
}
57+
58+
func (c *containerImpl) Build() {
59+
buildContainerImpl(c)
60+
}
61+
62+
func (c *containerImpl) Get(symbol Any) (Any, error) {
63+
return c.factories[symbol].factory()
64+
}
65+
66+
func (c *containerImpl) IsBound(symbol Any) bool {
67+
_, ok := c.factories[symbol]
68+
return ok
69+
}
70+
71+
func (c *containerImpl) Merge(other IContainer) IContainer {
72+
container := newContainer()
73+
74+
otherImpl, ok := other.(*containerImpl)
75+
if !ok {
76+
panic("container is not containerImpl")
77+
}
78+
79+
for symbol, factory := range c.factories {
80+
container.factories[symbol] = factory
81+
}
82+
83+
for symbol, factory := range otherImpl.factories {
84+
container.factories[symbol] = factory
85+
}
86+
87+
return container
88+
}
89+
90+
func (c *containerImpl) SetParent(parent IContainer) {
91+
parentImpl, ok := parent.(*containerImpl)
92+
if !ok {
93+
panic("container is not containerImpl")
94+
}
95+
96+
c.parent = parentImpl
97+
}
98+
99+
func newContainer() *containerImpl {
100+
return &containerImpl{
101+
factories: make(map[Any]*Binding),
102+
}
103+
}
104+
105+
func Container() IContainer {
106+
return newContainer()
107+
}

0 commit comments

Comments
 (0)