Skip to content

Commit ccc19b3

Browse files
committed
Prepare Godoc
1 parent 9e14a05 commit ccc19b3

File tree

15 files changed

+344
-146
lines changed

15 files changed

+344
-146
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020 go-autowire
3+
Copyright (c) 2021 go-autowire
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

atesting/testing.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package atesting
22

33
import (
4-
"autowire"
54
"container/list"
5+
"github.com/go-autowire/autowire"
66
"log"
77
"reflect"
8+
"unicode"
89
)
910

1011
func Spy(v interface{}, dependency interface{}) {
@@ -19,7 +20,6 @@ func Spies(v interface{}, dependencies []interface{}) {
1920
elemQueue := queue.Front()
2021
value := reflect.ValueOf(elemQueue.Value)
2122
queue.Remove(elemQueue)
22-
log.Printf("+++ %+v", value)
2323
var elem reflect.Value
2424
switch value.Kind() {
2525
case reflect.Ptr:
@@ -29,23 +29,51 @@ func Spies(v interface{}, dependencies []interface{}) {
2929
}
3030
for i := 0; i < elem.NumField(); i++ {
3131
field := elem.Type().Field(i)
32-
log.Printf("[%s] %s:%+v", field.Type.String(), field.Name, elem.Field(i))
3332
tag, ok := field.Tag.Lookup(autowire.Tag)
3433
if ok {
3534
if tag != "" {
3635
for _, dependency := range dependencies {
3736
dependValue := reflect.ValueOf(dependency)
3837
if dependValue.Type().Implements(field.Type) {
3938
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))
39+
log.Println("Injecting Spy on dependency by tag " + tag + " will be used " + t.Type().String())
40+
autowire.Autowire(dependency)
41+
setValue(value, elem, i, dependency)
4242
}
4343
}
4444
}
4545
if !elem.Field(i).IsNil() {
46-
queue.PushBack(elem.Field(i).Elem().Interface())
46+
if elem.Field(i).Elem().CanInterface() {
47+
queue.PushBack(autowire.Autowired(elem.Field(i).Elem().Interface()))
48+
} else {
49+
runeName := []rune(elem.Type().Field(i).Name)
50+
runeName[0] = unicode.ToUpper(runeName[0])
51+
methodName := string(runeName)
52+
method := value.MethodByName(methodName)
53+
if method.IsValid() {
54+
result := method.Call([]reflect.Value{})[0]
55+
queue.PushBack(autowire.Autowired(result.Interface()))
56+
}
57+
}
4758
}
4859
}
4960
}
5061
}
5162
}
63+
64+
func setValue(value reflect.Value, elem reflect.Value, i int, dependency interface{}) bool {
65+
runeName := []rune(elem.Type().Field(i).Name)
66+
exported := unicode.IsUpper(runeName[0])
67+
if exported {
68+
elem.Field(i).Set(reflect.ValueOf(dependency))
69+
} else {
70+
runeName[0] = unicode.ToUpper(runeName[0])
71+
methodName := "Set" + string(runeName)
72+
method := value.MethodByName(methodName)
73+
if method.IsValid() {
74+
method.Call([]reflect.Value{reflect.ValueOf(dependency)})
75+
return true
76+
}
77+
}
78+
return false
79+
}

autowire.go

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package autowire
22

33
import (
4+
"io"
45
"log"
56
"os"
67
"os/signal"
78
"reflect"
89
"regexp"
910
"strings"
11+
"unicode"
1012
)
1113

1214
var dependencies map[string]interface{}
@@ -19,16 +21,50 @@ func init() {
1921
log.SetFlags(log.LstdFlags | log.Lshortfile)
2022
log.Println("Init Autowire Context")
2123
signal.Notify(ch, os.Interrupt, os.Kill)
22-
2324
dependencies = make(map[string]interface{})
2425
}
2526

26-
func RunOmitTest(f func()) {
27-
if currentProfile != _Testing {
28-
f()
27+
// InitProd executes function only in production so it is preventing execution in go tests.
28+
// This flexibility could help if we want to skip autowiring struct in our tests.
29+
func InitProd(initFunc func()) {
30+
if currentProfile == _Production {
31+
initFunc()
2932
}
3033
}
3134

35+
// Autowire function injects all dependencies for the given structure v.
36+
// In order dependencies to be injected the desired struct fields should be marked with
37+
// autowire tag.
38+
//
39+
// Injection of concrete type
40+
//
41+
// Currently both type of fields exported and unexported are supported.
42+
// Following snippet shows injecting dependencies inside private structure fields using `autowire:""` tag:
43+
// type Application struct {
44+
// config *configuration.ApplicationConfig `autowire:""`
45+
// }
46+
// func (a *Application) SetConfig(config *configuration.ApplicationConfig) {
47+
// a.config = config
48+
// }
49+
// If we need dependency to be injected into unexported field Set<FieldName> function is required, as show above.
50+
// type Application struct {
51+
// Config *configuration.ApplicationConfig `autowire:""`
52+
// }
53+
// Injection of dependency into exported is supported also and there is no need to provide additional Setter.
54+
//
55+
// Injection of interface
56+
//
57+
// Often it`s better to rely on interface instead of concrete type, in order to accomplish this decoupling we specify
58+
// interfaces as a type of struct fields. The following snippet demonstrate that
59+
// type UserService struct {
60+
// userRoleRepository UserRoleRepository `autowire:"service/InMemoryUserRoleRepository"`
61+
// }
62+
// func (u *UserService) SetUserRoleRepository(userRoleRepository UserRoleRepository) {
63+
// u.userRoleRepository = userRoleRepository
64+
// }
65+
// UserRoleRepository is simply an interface and InMemoryUserRoleRepository is a struct, which implements this interface.
66+
// As it`s done dependency injection on unexported field, providing Setter is required. Just to highlight unexported fields
67+
// needs Setter while exported not. For more information take a look at example package.
3268
func Autowire(v interface{}) {
3369
value := reflect.ValueOf(v)
3470
switch value.Kind() {
@@ -38,17 +74,23 @@ func Autowire(v interface{}) {
3874
structType := getStructPtrFullPath(value)
3975
_, ok := dependencies[structType]
4076
if ok {
41-
log.Printf("%s already autowired. Going go overwrite it`s value!", structType)
77+
log.Printf("%s already autowired... ignored", structType)
4278
} else {
79+
log.Printf("Autowiring %s", structType)
4380
autowireDependencies(value)
4481
dependencies[structType] = v
4582
}
4683
default: // reflect.Array, reflect.Struct, reflect.Interface
4784
log.Println(value.Type().String() + " value")
4885
}
49-
log.Println(dependencies)
86+
//log.Println(dependencies)
5087
}
5188

89+
// Autowired function returns fully initialized with all dependencies instance, which is ready to be used.
90+
//As the result is empty interface, type assertions is required before using the instance.
91+
//Take a look at https://golang.org/ref/spec#Type_assertions for more information.
92+
// The following snippet demonstrate how could be done :
93+
// app := autowire.Autowired(app.Application{}).(*app.Application)
5294
func Autowired(v interface{}) interface{} {
5395
value := reflect.ValueOf(v)
5496
var path string
@@ -62,7 +104,6 @@ func Autowired(v interface{}) interface{} {
62104
default:
63105
log.Panicln("Unknown Autowired Typed!")
64106
}
65-
log.Println(path)
66107
dependency, ok := dependencies[path]
67108
if ok {
68109
return dependency
@@ -86,39 +127,62 @@ func autowireDependencies(value reflect.Value) {
86127
tag, ok := field.Tag.Lookup(Tag)
87128
if ok {
88129
var t reflect.Value
89-
log.Println("......." + field.Type.String())
90130
if tag != "" {
91-
log.Printf("field type: %v\n", getFullPath(field.Type.PkgPath(), field.Type.String()))
92131
currentDep := findDependency(tag)
93132
if len(currentDep) == 0 {
94133
msg := "Unknown dependency " + tag + " found none"
95134
if currentProfile != _Testing {
96135
log.Panicln(msg)
97136
} else {
98-
log.Println(msg)
137+
log.Println(msg + ", ready for spy")
99138
}
100139
} else {
101140
v := reflect.ValueOf(currentDep[0])
102141
if v.Type().Implements(field.Type) {
103142
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]))
143+
dependency := currentDep[0]
144+
setValue(value, elem, i, dependency)
106145
} else {
107146
log.Panicln(v.Type().String() + " doesnt Implements: " + field.Type.String())
108147
}
109148
}
110149
} else {
111150
t = reflect.New(elem.Type().Field(i).Type.Elem())
112-
log.Printf("field type: %v", getStructPtrFullPath(t))
113151
dependency, found := dependencies[getStructPtrFullPath(t)]
114152
if found {
115-
elem.Field(i).Set(reflect.ValueOf(dependency))
153+
runeName := []rune(elem.Type().Field(i).Name)
154+
exported := unicode.IsUpper(runeName[0])
155+
if exported {
156+
elem.Field(i).Set(reflect.ValueOf(dependency))
157+
} else {
158+
runeName[0] = unicode.ToUpper(runeName[0])
159+
methodName := "Set" + string(runeName)
160+
method := value.MethodByName(methodName)
161+
if method.IsValid() {
162+
method.Call([]reflect.Value{reflect.ValueOf(dependency)})
163+
}
164+
}
116165
}
117166
}
118167
}
119168
}
120169
}
121170

171+
func setValue(value reflect.Value, elem reflect.Value, i int, dependency interface{}) {
172+
runeName := []rune(elem.Type().Field(i).Name)
173+
exported := unicode.IsUpper(runeName[0])
174+
if exported {
175+
elem.Field(i).Set(reflect.ValueOf(dependency))
176+
} else {
177+
runeName[0] = unicode.ToUpper(runeName[0])
178+
methodName := "Set" + string(runeName)
179+
method := value.MethodByName(methodName)
180+
if method.IsValid() {
181+
method.Call([]reflect.Value{reflect.ValueOf(dependency)})
182+
}
183+
}
184+
}
185+
122186
func findDependency(tagDependencyType string) []interface{} {
123187
var result []interface{}
124188
for tmp, dep := range dependencies {
@@ -129,16 +193,24 @@ func findDependency(tagDependencyType string) []interface{} {
129193
return result
130194
}
131195

132-
type AutoClosable interface {
133-
Close()
134-
}
135-
136-
func Run(mainFunction func()) {
196+
// Run executes function function after the function execution completes for each autowired struct
197+
// which implements io.Closer interface will be invoked Call() function, so currently active
198+
//occupied resources(connections, channels, etc.) could be released.
199+
func Run(function func()) {
137200
defer close()
138-
mainFunction()
201+
function()
139202
}
140203

141204
func close() {
142-
println("HEREE")
143-
205+
log.Println("Closing...")
206+
for _, dependency := range dependencies {
207+
valueDepend := reflect.ValueOf(dependency)
208+
closerType := reflect.TypeOf((*io.Closer)(nil)).Elem()
209+
if valueDepend.Type().Implements(closerType) {
210+
err := dependency.(io.Closer).Close()
211+
if err != nil {
212+
log.Println(err.Error())
213+
}
214+
}
215+
}
144216
}

example/app/application.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
"github.com/go-autowire/autowire"
6+
"github.com/go-autowire/autowire/example/configuration"
7+
"github.com/go-autowire/autowire/example/service"
8+
"log"
9+
)
10+
11+
func init() {
12+
autowire.Autowire(&Application{})
13+
}
14+
15+
type Application struct {
16+
config *configuration.ApplicationConfig `autowire:""`
17+
userSvc *service.UserService `autowire:""`
18+
}
19+
20+
func (a Application) Start() {
21+
log.Println("Config Kind : " + a.config.Kind())
22+
userId := "serviceaccount@demo.com"
23+
balance, err := a.userSvc.Balance(userId)
24+
if err != nil {
25+
log.Fatalln(err.Error())
26+
}
27+
fmt.Println("Current balance is " + balance.String())
28+
}
29+
30+
func (a *Application) SetConfig(config *configuration.ApplicationConfig) {
31+
a.config = config
32+
}
33+
34+
func (a *Application) SetUserSvc(userSvc *service.UserService) {
35+
a.userSvc = userSvc
36+
}
37+
38+
func (a *Application) UserSvc() *service.UserService {
39+
return a.userSvc
40+
}

example/autowire_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package example
2+
3+
import (
4+
"github.com/go-autowire/autowire"
5+
"github.com/go-autowire/autowire/atesting"
6+
"github.com/go-autowire/autowire/example/app"
7+
"log"
8+
"math/big"
9+
"testing"
10+
)
11+
12+
type TestPaymentServiceTest struct {
13+
}
14+
15+
func (TestPaymentServiceTest) Balance() *big.Float {
16+
log.Println("Mocked object...TestPaymentServiceTest...")
17+
balance, _ := new(big.Float).SetString("300.10")
18+
return balance
19+
}
20+
21+
type TestAuditClient struct {
22+
}
23+
24+
func (TestAuditClient) Send(event string) {
25+
log.Printf("Test event delivered")
26+
}
27+
28+
func TestAutowire(t *testing.T) {
29+
app := autowire.Autowired(app.Application{}).(*app.Application)
30+
app.UserSvc().PaymentSvc = &TestPaymentServiceTest{}
31+
atesting.Spies(app, []interface{}{&TestPaymentServiceTest{}, &TestAuditClient{}})
32+
app.Start()
33+
}

example_application/configuration/application_config.go renamed to example/configuration/application_config.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package configuration
22

33
import (
4-
"autowire"
5-
"log"
4+
"github.com/go-autowire/autowire"
65
)
76

87
func init() {
9-
log.Println("Initializing ApplicationConfig")
10-
autowire.Autowire(New("con"))
8+
autowire.Autowire(New("default"))
119
}
1210

1311
type ApplicationConfig struct {

0 commit comments

Comments
 (0)