Skip to content

Commit 9e14a05

Browse files
committed
Phase one: Autowire
1 parent 1e2dee5 commit 9e14a05

File tree

11 files changed

+381
-0
lines changed

11 files changed

+381
-0
lines changed

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
# Dependency directories (remove the comment below to include it)
15+
# vendor/
16+
.idea/

atesting/testing.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package atesting
2+
3+
import (
4+
"autowire"
5+
"container/list"
6+
"log"
7+
"reflect"
8+
)
9+
10+
func Spy(v interface{}, dependency interface{}) {
11+
slice := []interface{}{dependency}
12+
Spies(v, slice)
13+
}
14+
15+
func Spies(v interface{}, dependencies []interface{}) {
16+
queue := list.New()
17+
queue.PushBack(v)
18+
for queue.Len() > 0 {
19+
elemQueue := queue.Front()
20+
value := reflect.ValueOf(elemQueue.Value)
21+
queue.Remove(elemQueue)
22+
log.Printf("+++ %+v", value)
23+
var elem reflect.Value
24+
switch value.Kind() {
25+
case reflect.Ptr:
26+
elem = value.Elem()
27+
case reflect.Struct:
28+
elem = value
29+
}
30+
for i := 0; i < elem.NumField(); i++ {
31+
field := elem.Type().Field(i)
32+
log.Printf("[%s] %s:%+v", field.Type.String(), field.Name, elem.Field(i))
33+
tag, ok := field.Tag.Lookup(autowire.Tag)
34+
if ok {
35+
if tag != "" {
36+
for _, dependency := range dependencies {
37+
dependValue := reflect.ValueOf(dependency)
38+
if dependValue.Type().Implements(field.Type) {
39+
t := reflect.New(dependValue.Type())
40+
log.Println("found dependency by tag " + tag + " will be used " + t.Type().String())
41+
elem.Field(i).Set(reflect.ValueOf(dependency))
42+
}
43+
}
44+
}
45+
if !elem.Field(i).IsNil() {
46+
queue.PushBack(elem.Field(i).Elem().Interface())
47+
}
48+
}
49+
}
50+
}
51+
}

autowire.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package autowire
2+
3+
import (
4+
"log"
5+
"os"
6+
"os/signal"
7+
"reflect"
8+
"regexp"
9+
"strings"
10+
)
11+
12+
var dependencies map[string]interface{}
13+
var currentProfile = getProfile()
14+
var ch = make(chan os.Signal)
15+
16+
const Tag = "autowire"
17+
18+
func init() {
19+
log.SetFlags(log.LstdFlags | log.Lshortfile)
20+
log.Println("Init Autowire Context")
21+
signal.Notify(ch, os.Interrupt, os.Kill)
22+
23+
dependencies = make(map[string]interface{})
24+
}
25+
26+
func RunOmitTest(f func()) {
27+
if currentProfile != _Testing {
28+
f()
29+
}
30+
}
31+
32+
func Autowire(v interface{}) {
33+
value := reflect.ValueOf(v)
34+
switch value.Kind() {
35+
case reflect.Invalid:
36+
log.Println("invalid reflection type")
37+
case reflect.Ptr:
38+
structType := getStructPtrFullPath(value)
39+
_, ok := dependencies[structType]
40+
if ok {
41+
log.Printf("%s already autowired. Going go overwrite it`s value!", structType)
42+
} else {
43+
autowireDependencies(value)
44+
dependencies[structType] = v
45+
}
46+
default: // reflect.Array, reflect.Struct, reflect.Interface
47+
log.Println(value.Type().String() + " value")
48+
}
49+
log.Println(dependencies)
50+
}
51+
52+
func Autowired(v interface{}) interface{} {
53+
value := reflect.ValueOf(v)
54+
var path string
55+
switch value.Kind() {
56+
case reflect.Invalid:
57+
log.Println("invalid")
58+
case reflect.Struct:
59+
path = getFullPath(value.Type().PkgPath(), value.Type().String())
60+
case reflect.Ptr:
61+
path = getStructPtrFullPath(value)
62+
default:
63+
log.Panicln("Unknown Autowired Typed!")
64+
}
65+
log.Println(path)
66+
dependency, ok := dependencies[path]
67+
if ok {
68+
return dependency
69+
}
70+
return nil
71+
}
72+
73+
func getStructPtrFullPath(value reflect.Value) string {
74+
return getFullPath(value.Elem().Type().PkgPath(), value.Type().String())
75+
}
76+
77+
func getFullPath(pkgPath string, typePath string) string {
78+
var re = regexp.MustCompile(`^\*?.+\.`)
79+
return pkgPath + re.ReplaceAllString(typePath, "/")
80+
}
81+
82+
func autowireDependencies(value reflect.Value) {
83+
elem := value.Elem()
84+
for i := 0; i < elem.NumField(); i++ {
85+
field := elem.Type().Field(i)
86+
tag, ok := field.Tag.Lookup(Tag)
87+
if ok {
88+
var t reflect.Value
89+
log.Println("......." + field.Type.String())
90+
if tag != "" {
91+
log.Printf("field type: %v\n", getFullPath(field.Type.PkgPath(), field.Type.String()))
92+
currentDep := findDependency(tag)
93+
if len(currentDep) == 0 {
94+
msg := "Unknown dependency " + tag + " found none"
95+
if currentProfile != _Testing {
96+
log.Panicln(msg)
97+
} else {
98+
log.Println(msg)
99+
}
100+
} else {
101+
v := reflect.ValueOf(currentDep[0])
102+
if v.Type().Implements(field.Type) {
103+
t = reflect.New(v.Type())
104+
log.Println("found dependency by tag " + tag + " will be used " + t.Type().String())
105+
elem.Field(i).Set(reflect.ValueOf(currentDep[0]))
106+
} else {
107+
log.Panicln(v.Type().String() + " doesnt Implements: " + field.Type.String())
108+
}
109+
}
110+
} else {
111+
t = reflect.New(elem.Type().Field(i).Type.Elem())
112+
log.Printf("field type: %v", getStructPtrFullPath(t))
113+
dependency, found := dependencies[getStructPtrFullPath(t)]
114+
if found {
115+
elem.Field(i).Set(reflect.ValueOf(dependency))
116+
}
117+
}
118+
}
119+
}
120+
}
121+
122+
func findDependency(tagDependencyType string) []interface{} {
123+
var result []interface{}
124+
for tmp, dep := range dependencies {
125+
if strings.Contains(tmp, tagDependencyType) {
126+
result = append(result, dep)
127+
}
128+
}
129+
return result
130+
}
131+
132+
type AutoClosable interface {
133+
Close()
134+
}
135+
136+
func Run(mainFunction func()) {
137+
defer close()
138+
mainFunction()
139+
}
140+
141+
func close() {
142+
println("HEREE")
143+
144+
}

example_application/autowire_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package example_application
2+
3+
import (
4+
"autowire"
5+
"autowire/atesting"
6+
"autowire/example_application/service"
7+
"log"
8+
"testing"
9+
)
10+
11+
type FilipPaymentServiceTest struct {
12+
}
13+
14+
func (FilipPaymentServiceTest) Status() {
15+
log.Println("FilipPaymentServiceTest...")
16+
}
17+
18+
type TestClient struct {
19+
T string
20+
}
21+
22+
func (TestClient) Connect() {
23+
log.Println("TestConnected")
24+
}
25+
26+
func TestAutowire(t *testing.T) {
27+
var service = autowire.Autowired(service.UserService{}).(*service.UserService)
28+
atesting.Spies(service, []interface{}{&FilipPaymentServiceTest{}, &TestClient{T: "filip"}})
29+
log.Println(service.Do())
30+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package configuration
2+
3+
import (
4+
"autowire"
5+
"log"
6+
)
7+
8+
func init() {
9+
log.Println("Initializing ApplicationConfig")
10+
autowire.Autowire(New("con"))
11+
}
12+
13+
type ApplicationConfig struct {
14+
kind string
15+
}
16+
17+
func New(kind string) *ApplicationConfig {
18+
return &ApplicationConfig{kind: kind}
19+
}
20+
21+
func (a ApplicationConfig) Kind() string {
22+
return a.kind
23+
}

example_application/service/client.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package service
2+
3+
import (
4+
"autowire"
5+
"log"
6+
)
7+
8+
func init() {
9+
autowire.Autowire(&AppClient{})
10+
}
11+
12+
type Client interface {
13+
Connect()
14+
}
15+
16+
type AppClient struct {
17+
Type string
18+
}
19+
20+
func (AppClient) Connect() {
21+
log.Println("Connected")
22+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package service
2+
3+
import (
4+
"autowire"
5+
"log"
6+
)
7+
8+
func init() {
9+
log.Println("Initializing PaymentService")
10+
autowire.RunOmitTest(func() {
11+
autowire.Autowire(&BankAccountService{})
12+
autowire.Autowire(&PaypalService{})
13+
})
14+
}
15+
16+
type PaymentService interface {
17+
Status()
18+
}
19+
20+
type BankAccountService struct {
21+
}
22+
23+
func (BankAccountService) Status() {
24+
log.Println("BankAccountService...")
25+
}
26+
27+
type PaypalService struct {
28+
}
29+
30+
func (PaypalService) Status() {
31+
log.Println("PaypalService...")
32+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package service
2+
3+
import (
4+
"autowire"
5+
"autowire/example_application/configuration"
6+
"log"
7+
)
8+
9+
func init() {
10+
log.Println("Initializing UserService")
11+
s := &UserService{}
12+
autowire.Autowire(s)
13+
}
14+
15+
type UserService struct {
16+
Config *configuration.ApplicationConfig `autowire:""`
17+
PaymentSvc PaymentService `autowire:"service/BankAccountService"`
18+
Client Client `autowire:"service/AppClient"`
19+
}
20+
21+
func (u UserService) Do() string {
22+
u.Client.Connect()
23+
u.PaymentSvc.Status()
24+
return u.Config.Kind()
25+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/go-autowire/autowire
2+
3+
go 1.15

profile.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package autowire
2+
3+
import (
4+
"os"
5+
"regexp"
6+
"strings"
7+
)
8+
9+
type Profile uint64
10+
11+
const (
12+
_Default Profile = iota
13+
_Testing
14+
)
15+
16+
func getProfile() Profile {
17+
args := os.Args
18+
programName := args[0][strings.LastIndex(args[0], "/"):]
19+
if result, _ := regexp.MatchString("/.*[Tt]est", programName); result {
20+
return _Testing
21+
}
22+
return _Default
23+
}

profile_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package autowire
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func Test_Profile(t *testing.T) {
8+
profile := getProfile()
9+
if profile != _Testing {
10+
t.Errorf("Expected profile testing found active")
11+
}
12+
}

0 commit comments

Comments
 (0)