├── .travis.yml ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── README.md ├── anagent.go ├── anagent_test.go ├── util.go └── vendor └── github.com ├── chuckpreslar └── emission │ ├── .gitignore │ ├── README.md │ └── emitter.go └── codegangsta └── inject ├── .gitignore ├── LICENSE ├── README.md └── inject.go /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | 4 | go: 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | - 1.9 9 | 10 | before_install: 11 | - go get github.com/mattn/goveralls 12 | - go get github.com/chuckpreslar/emission 13 | - go get github.com/go-macaron/inject 14 | 15 | script: 16 | - go test -v -race -coverprofile=coverage.txt -covermode=atomic 17 | 18 | after_success: 19 | - bash <(curl -s https://codecov.io/bash) 20 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | name = "github.com/chuckpreslar/emission" 7 | packages = ["."] 8 | revision = "a7ddd980baf9ec8ad3f2e6826ef9eacfe55b3f02" 9 | 10 | [[projects]] 11 | name = "github.com/codegangsta/inject" 12 | packages = ["."] 13 | revision = "37d7f8432a3e684eef9b2edece76bdfa6ac85b39" 14 | version = "v1.0-rc1" 15 | 16 | [solve-meta] 17 | analyzer-name = "dep" 18 | analyzer-version = 1 19 | inputs-digest = "efa642ad2da5ba167070b9c8ee48e9a2e9afcfe04583809ce7ce14e558d8f464" 20 | solver-name = "gps-cdcl" 21 | solver-version = 1 22 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | branch = "master" 30 | name = "github.com/chuckpreslar/emission" 31 | 32 | [[constraint]] 33 | name = "github.com/codegangsta/inject" 34 | version = "1.0.0-rc1" 35 | 36 | [prune] 37 | go-tests = true 38 | unused-packages = true 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2017-2018 Ettore Di Giacinto 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 15 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 18 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 19 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Anagent [![Build Status](https://travis-ci.org/mudler/anagent.svg?branch=master)](https://travis-ci.org/mudler/anagent) [![codecov](https://codecov.io/gh/mudler/anagent/branch/master/graph/badge.svg)](https://codecov.io/gh/mudler/anagent) [![Go Report Card](https://goreportcard.com/badge/github.com/mudler/anagent)](https://goreportcard.com/report/github.com/mudler/anagent) [![godoc](https://godoc.org/github.com/mudler/anagent?status.svg)](http://godoc.org/github.com/mudler/anagent) 2 | 3 | Minimalistic, pluggable Golang evloop/timer handler with dependency-injection - based on [codegangsta/inject](github.com/codegangsta/inject) - [go-macaron/inject](github.com/go-macaron/inject) and [chuckpreslar/emission](https://github.com/chuckpreslar/emission). 4 | 5 | *Anagent* is a lightweight library that allows you to plug inside to other event loops, or allows you to handle and create your own within your application - leaving the control to you. 6 | 7 | It comes with dependency-injection from codegangsta/inject, and it's also a soft-wrapper to chuckpreslar/emission, adding to it dependency injection capabilities and timer handlers. 8 | 9 | ## Usage 10 | 11 | ### Event Emitter with Dependency injection 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "log" 18 | "github.com/mudler/anagent" 19 | ) 20 | 21 | type TestTest struct { 22 | Test string 23 | } 24 | 25 | func main() { 26 | agent := anagent.New() 27 | mytest := &TestTest{Test: "PONG!"} 28 | agent.Map(mytest) 29 | 30 | agent.Once("test", func(te *TestTest, l *log.Logger) { 31 | if te.Test == "PONG!" { 32 | l.Println("It just works!") 33 | } 34 | }) 35 | 36 | agent.Emit("test") 37 | } 38 | ``` 39 | 40 | What happened here? we mapped our structure instance (```TestTest```) inside the agent with (```agent.Map()```), and all fired events can access to them. 41 | 42 | ### Timer / Reactor 43 | 44 | ```go 45 | package main 46 | 47 | import "github.com/mudler/anagent" 48 | import "fmt" 49 | 50 | type TestTest struct { 51 | Test string 52 | } 53 | 54 | func main() { 55 | agent := anagent.New() 56 | mytest := &TestTest{Test: "PONG!"} 57 | agent.Map(mytest) 58 | 59 | agent.Emitter().On("test", func(s string) { fmt.Println("Received: " + s) }) 60 | 61 | // Not recurring timer 62 | agent.TimerSeconds(int64(3), false, func(a *anagent.Anagent, te *TestTest) { 63 | a.Emitter().Emit("test", te.Test) 64 | go a.Stop() 65 | }) 66 | 67 | agent.Start() // Loops here and never returns 68 | } 69 | ``` 70 | 71 | The code portion will start and wait for 3 seconds, then it will execute the callback (not recurring, that's why the ```false```) that will fire a custom event defined before (note, it's not using the dependency-injection capabilities, thus it's accessing the emitter handler directly with ```agent.Emitter()```). 72 | 73 | The difference is that when we access to ```On()``` provided by ```agent.On()```, we access to the agent dependencies, that have been mapped with ```agent.Map()``` - otherwise, with ```agent.Emitter().On()``` we are free to bind any arguments to the event callback. 74 | 75 | 76 | After the event is fired, the timer stops the eventloop (```a.Stop()```), so the program returns. 77 | 78 | ### Hook into other loops 79 | 80 | It is often in other framework to use loop patterns, as example in framework for game development, network agents, and such. 81 | We can hook into other loops, and run the agent Step function, so we can still leverage the evloop functionalities. 82 | 83 | ```go 84 | package main 85 | 86 | import "github.com/mudler/anagent" 87 | import "fmt" 88 | 89 | type TestTest struct { 90 | Test string 91 | } 92 | 93 | func main() { 94 | agent := anagent.New() 95 | mytest := &TestTest{Test: "PONG!"} 96 | agent.Map(mytest) 97 | 98 | // Reply with a delay of 2s 99 | agent.Emitter().On("test", func(s string) { 100 | agent.TimerSeconds(int64(2), false, func() { 101 | fmt.Println("Received: " + s) 102 | }) 103 | }) 104 | 105 | // Recurring timer 106 | agent.TimerSeconds(int64(3), true, func(a *anagent.Anagent, te *TestTest) { 107 | fmt.Println("PING!") 108 | a.Emitter().Emit("test", te.Test) 109 | }) 110 | 111 | for { // Infinite loop 112 | agent.Step() 113 | } 114 | } 115 | ``` 116 | -------------------------------------------------------------------------------- /anagent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2018 Ettore Di Giacinto 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining 4 | // a copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 15 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 18 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 19 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | package anagent 22 | 23 | import ( 24 | "reflect" 25 | "sync" 26 | "time" 27 | 28 | "github.com/chuckpreslar/emission" 29 | "github.com/codegangsta/inject" 30 | ) 31 | 32 | // VERSION contains the Anagent version number 33 | const VERSION = "0.1" 34 | 35 | // Handler can be any callable function. 36 | // Anagent attempts to inject services into the handler's argument list, 37 | // and panics if an argument could not be fulfilled via dependency injection. 38 | type Handler interface{} 39 | 40 | // TimerID is a string that represent the timers ID, 41 | // it is used so we can access to them later or modify them 42 | // during the agent execution. 43 | type TimerID string 44 | 45 | // Timer represent the structure that holds the 46 | // informations of the Timer 47 | // timer it's a time.Time structure that defines when the timer 48 | // should be fired, after contains the time.Duration of the 49 | // recurring timer. 50 | type Timer struct { 51 | time time.Time 52 | after time.Duration 53 | handler Handler 54 | recurring bool 55 | } 56 | 57 | // After receives a time.Duration as arguments, and sets the 58 | // timer recurring time. 59 | func (t *Timer) After(ti time.Duration) { 60 | t.after = ti 61 | } 62 | 63 | // Anagent represents the top level application. 64 | // inject.Injector methods can be invoked to map services on a global level. 65 | type Anagent struct { 66 | inject.Injector 67 | sync.Mutex 68 | 69 | handlers []Handler 70 | timers map[TimerID]*Timer 71 | 72 | ee *emission.Emitter 73 | 74 | // Fatal bool 75 | Started bool 76 | BusyLoop bool 77 | StartedAccess *sync.Mutex 78 | } 79 | 80 | // On Binds a callback to an event, mapping the arguments on a global level 81 | func (a *Anagent) On(event, listener interface{}) *Anagent { 82 | a.Emitter().On(event, func() { a.Invoke(listener) }) 83 | return a 84 | } 85 | 86 | // Emit Emits an event, it does accept only the event as argument, since 87 | // the callback will have access to the service mapped by the injector 88 | func (a *Anagent) Emit(event interface{}) *Anagent { 89 | a.Emitter().Emit(event) 90 | return a 91 | } 92 | 93 | // Once Binds a callback to an event, mapping the arguments on a global level 94 | // It is fired only once. 95 | func (a *Anagent) Once(event, listener interface{}) *Anagent { 96 | a.Emitter().Once(event, func() { a.Invoke(listener) }) 97 | return a 98 | } 99 | 100 | // EmitSync Emits an event in a syncronized manner, 101 | // it does accept only the event as argument, since 102 | // the callback will have access to the service mapped by the injector 103 | func (a *Anagent) EmitSync(event interface{}) *Anagent { 104 | a.Emitter().EmitSync(event) 105 | return a 106 | } 107 | 108 | // Handlers sets the entire middleware stack with the given Handlers. 109 | // This will clear any current middleware handlers, 110 | // and panics if any of the handlers is not a callable function 111 | func (a *Anagent) Handlers(handlers ...Handler) { 112 | a.handlers = make([]Handler, 0) 113 | for _, handler := range handlers { 114 | a.Use(handler) 115 | } 116 | } 117 | 118 | // Emitter returns the internal *emission.Emitter used structure 119 | // use this to access directly to the Emitter, and override 120 | // the dependency-injection features 121 | func (a *Anagent) Emitter() *emission.Emitter { 122 | return a.ee 123 | } 124 | 125 | // validateAndWrapHandler makes sure a handler is a callable function, it panics if not. 126 | // When the handler is also potential to be any built-in inject.FastInvoker, 127 | // it wraps the handler automatically to have some performance gain. 128 | func validateAndWrapHandler(h Handler) Handler { 129 | if reflect.TypeOf(h).Kind() != reflect.Func { 130 | panic("Anagent handler must be a callable function") 131 | } 132 | return h 133 | } 134 | 135 | // Next adds a middleware Handler to the next tick, 136 | // and removes it once executed. 137 | func (a *Anagent) Next(handler Handler) { 138 | a.AddTimerSeconds(0, handler) 139 | } 140 | 141 | // Use adds a middleware Handler to the stack, 142 | // and panics if the handler is not a callable func. 143 | // Middleware Handlers are invoked in the order that they are added. 144 | func (a *Anagent) Use(handler Handler) { 145 | a.Lock() 146 | defer a.Unlock() 147 | handler = validateAndWrapHandler(handler) 148 | a.handlers = append(a.handlers, handler) 149 | } 150 | 151 | // TimerSeconds is used to set a timer, that will fire after the seconds supplied. 152 | // It requires seconds supplied as int64 153 | // a bool indicating if it is recurring or not, 154 | // and at the end the callback to be fired at the desired time. 155 | func (a *Anagent) TimerSeconds(seconds int64, recurring bool, handler Handler) TimerID { 156 | handler = validateAndWrapHandler(handler) 157 | dt := time.Duration(seconds) * time.Second 158 | 159 | return a.Timer(TimerID(""), time.Now().Add(dt), dt, recurring, handler) 160 | } 161 | 162 | // Timer is used to set a generic timer. 163 | // You have to supply by yourself all the options that normally are 164 | // exposed with other functions. 165 | // It requires a TimerID (if empty is supplied, it is created for you), 166 | // a time.Time indicating when the timer have to be fired, 167 | // a time.Duration indicating the recurring span 168 | // a boolean to set it as recurring or not 169 | // and at the end the callback to be fired at the desired time. 170 | func (a *Anagent) Timer(tid TimerID, ti time.Time, after time.Duration, recurring bool, handler Handler) TimerID { 171 | var id TimerID 172 | if tid != "" { 173 | id = tid 174 | } else { 175 | id = TimerID(GetMD5Hash(time.Now().String())) 176 | } 177 | 178 | handler = validateAndWrapHandler(handler) 179 | t := &Timer{handler: handler, time: ti, after: after, recurring: recurring} 180 | a.timers[id] = t 181 | 182 | return id 183 | } 184 | 185 | // RemoveTimer is used to set a remove a timer from the loop. 186 | // It requires a TimerID 187 | func (a *Anagent) RemoveTimer(id TimerID) { 188 | delete(a.timers, id) 189 | } 190 | 191 | // GetTimer is used to set a get a timer from the loop. 192 | // It requires a TimerID 193 | func (a *Anagent) GetTimer(id TimerID) *Timer { 194 | return a.timers[id] 195 | } 196 | 197 | // SetDuration is used to change the duration of a timer. 198 | // It requires a TimerID and a time.Duration 199 | func (a *Anagent) SetDuration(id TimerID, after time.Duration) TimerID { 200 | a.timers[id].after = after 201 | return id 202 | } 203 | 204 | // AddTimerSeconds is used to set a non recurring timer, 205 | // that will fire after the seconds supplied. 206 | // It requires seconds supplied as int64 207 | // and at the end the callback to be fired at the desired time. 208 | func (a *Anagent) AddTimerSeconds(seconds int64, handler Handler) TimerID { 209 | return a.TimerSeconds(seconds, false, handler) 210 | } 211 | 212 | // AddRecurringTimerSeconds is used to set a recurring timer, 213 | // that will fire after the seconds supplied. 214 | // It requires seconds supplied as int64 215 | // and at the end the callback to be fired at the desired time. 216 | func (a *Anagent) AddRecurringTimerSeconds(seconds int64, handler Handler) TimerID { 217 | return a.TimerSeconds(seconds, true, handler) 218 | } 219 | 220 | // New creates a bare bones Anagent instance. 221 | // Use this method if you want to have full control over the middleware that is used. 222 | func New() *Anagent { 223 | ts := make(map[TimerID]*Timer) 224 | a := &Anagent{ 225 | BusyLoop: false, 226 | Injector: inject.New(), 227 | ee: emission.NewEmitter(), 228 | timers: ts, 229 | StartedAccess: &sync.Mutex{}, 230 | } 231 | 232 | a.Map(a) 233 | a.Map(a.ee) 234 | 235 | return a 236 | } 237 | 238 | func (a *Anagent) runAll() { 239 | a.Lock() 240 | defer a.Unlock() 241 | var i = 0 242 | 243 | for i < len(a.handlers) { 244 | //var err error 245 | 246 | //_, err = a.Invoke(a.handlers[i]) // was vals 247 | 248 | //if err != nil && a.Fatal { 249 | // panic(err) 250 | //} 251 | a.Invoke(a.handlers[i]) 252 | 253 | i++ 254 | } 255 | } 256 | 257 | // RunLoop starts a loop that never returns 258 | func (a *Anagent) RunLoop() { 259 | for { 260 | a.Step() 261 | } 262 | } 263 | 264 | // IsStarted returns a boolean indicating if we started the loop with Start() 265 | func (a *Anagent) IsStarted() bool { 266 | a.StartedAccess.Lock() 267 | defer a.StartedAccess.Unlock() 268 | return a.Started 269 | } 270 | 271 | // Start starts the agent loop and never returns. ( unless you call Stop() ) 272 | func (a *Anagent) Start() { 273 | 274 | if a.Started == true { 275 | return 276 | } 277 | a.Started = true 278 | 279 | for a.IsStarted() { 280 | a.Step() 281 | } 282 | } 283 | 284 | // Stop stops the agent loop, in case Start() was called. 285 | func (a *Anagent) Stop() { 286 | a.StartedAccess.Lock() 287 | defer a.StartedAccess.Unlock() 288 | a.Started = false 289 | } 290 | 291 | // Step executes an agent step. 292 | // It is idempotent, and even if there are delays in timings 293 | // events gets executed in order as a best-effort in 294 | // respecting setted timers. 295 | func (a *Anagent) Step() { 296 | a.runAll() 297 | 298 | if len(a.timers) == 0 { 299 | return 300 | } 301 | 302 | a.consumeTimer(a.bestTimer()) 303 | } 304 | 305 | func (a *Anagent) consumeTimer(mintimeid *TimerID, mintime *time.Time) { 306 | now := time.Now() 307 | 308 | if mintime.After(now) { 309 | if !a.BusyLoop { 310 | time.Sleep(mintime.Sub(now)) 311 | } else { 312 | return 313 | } 314 | } 315 | 316 | a.Invoke(a.timers[*mintimeid].handler) 317 | a.Lock() 318 | defer a.Unlock() 319 | if a.timers[*mintimeid].recurring == true { 320 | a.timers[*mintimeid].time = time.Now().Add(a.timers[*mintimeid].after) 321 | } else { 322 | delete(a.timers, *mintimeid) 323 | } 324 | } 325 | 326 | func (a *Anagent) bestTimer() (*TimerID, *time.Time) { 327 | mintimeid, timer := RandTimer(a.timers) 328 | mintime := timer.time 329 | 330 | a.Lock() 331 | defer a.Unlock() 332 | 333 | for timerid, t := range a.timers { 334 | if t.time.Before(mintime) { 335 | mintime = t.time 336 | mintimeid = timerid 337 | } 338 | } 339 | 340 | return &mintimeid, &mintime 341 | } 342 | -------------------------------------------------------------------------------- /anagent_test.go: -------------------------------------------------------------------------------- 1 | package anagent 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestNew(t *testing.T) { 12 | agent := New() 13 | if reflect.TypeOf(agent).String() != "*anagent.Anagent" { 14 | t.Errorf("New() should return an anangent pointer: %v returned", agent) 15 | } 16 | } 17 | 18 | func TestEmitter(t *testing.T) { 19 | agent := New() 20 | 21 | fired := false 22 | agent.Emitter().On("test", func(v bool) { fired = v }) 23 | agent.Emitter().Emit("test", true) 24 | if fired == false { 25 | t.Errorf("Expected event not fired") 26 | } 27 | } 28 | 29 | func TestUse(t *testing.T) { 30 | agent := New() 31 | 32 | fired := false 33 | agent.Use(func(a *Anagent) { 34 | fired = true 35 | a.Start() 36 | a.Stop() 37 | }) 38 | 39 | agent.Start() 40 | if fired == false { 41 | t.Errorf("Agent middlewares are working and can stop the loop") 42 | } 43 | 44 | agent.Handlers() 45 | agent.Use(func(a *Anagent) { 46 | panic("OMG") 47 | }) 48 | 49 | assertPanic(t, func() { 50 | agent.Start() 51 | }) 52 | 53 | assertPanic(t, func() { 54 | agent.RunLoop() 55 | }) 56 | 57 | fired = false 58 | agent.Handlers(func(a *Anagent) { 59 | fired = true 60 | }) 61 | 62 | agent.Step() 63 | if fired == false { 64 | t.Errorf("Agent middlewares are working and can stop the loop") 65 | } 66 | 67 | assertPanic(t, func() { 68 | agent.Use("test") 69 | }) 70 | } 71 | 72 | func TestAfter(t *testing.T) { 73 | agent := New() 74 | triggered := 0 75 | 76 | tid := agent.AddRecurringTimerSeconds(int64(1), func() { 77 | triggered++ 78 | }) 79 | 80 | agent.AddRecurringTimerSeconds(int64(3), func(a *Anagent) { 81 | timer := a.GetTimer(tid) 82 | timer.After(time.Duration(2 * time.Second)) 83 | }) 84 | 85 | agent.AddRecurringTimerSeconds(int64(6), func(a *Anagent) { 86 | timer := a.GetTimer(tid) 87 | if timer.after != time.Duration(2*time.Second) { 88 | t.Errorf("Timer was not set by the previous timer") 89 | } 90 | if triggered != 4 { 91 | t.Errorf("Timer was fired in not expected order! " + strconv.Itoa(triggered)) 92 | } 93 | a.Stop() 94 | }) 95 | 96 | agent.Start() 97 | } 98 | 99 | func TestTimerSeconds(t *testing.T) { 100 | agent := New() 101 | 102 | fired := false 103 | agent.AddTimerSeconds(int64(1), func(a *Anagent) { 104 | fired = true 105 | a.Stop() 106 | }) 107 | 108 | agent.Start() 109 | if fired == false { 110 | t.Errorf("Agent middlewares are working and can stop the loop") 111 | } 112 | 113 | fired = false 114 | agent.TimerSeconds(int64(3), false, func(a *Anagent) { 115 | fired = true 116 | go a.Stop() 117 | }) 118 | 119 | agent.Start() 120 | if fired == false { 121 | t.Errorf("Agent middlewares are working and can stop the loop") 122 | } 123 | } 124 | 125 | func TestRecurringTimer(t *testing.T) { 126 | agent := New() 127 | fired := 0 128 | agent.Emitter().On("Ping", func() { fmt.Println("PING") }) 129 | tid := agent.AddRecurringTimerSeconds(int64(1), func(a *Anagent) { 130 | fired++ 131 | go func() { 132 | a.Lock() 133 | defer a.Unlock() 134 | a.Emitter().Emit("Ping") 135 | }() 136 | if fired > 4 { 137 | a.Stop() 138 | } 139 | }) 140 | 141 | agent.SetDuration(tid, time.Second) 142 | assertSleep(5.0, t, func() { 143 | agent.Start() 144 | }) 145 | 146 | if fired != 5 { 147 | t.Errorf("Agent middlewares are working and can stop the loop") 148 | } 149 | 150 | agent.RemoveTimer(tid) 151 | fired = 0 152 | agent.AddRecurringTimerSeconds(int64(1), func(a *Anagent) { 153 | fired++ 154 | if fired > 4 { 155 | a.Stop() 156 | } 157 | }) 158 | 159 | assertSleep(5.0, t, func() { 160 | agent.Start() 161 | }) 162 | if fired != 5 { 163 | t.Errorf("Agent middlewares are working and can stop the loop") 164 | } 165 | 166 | } 167 | 168 | type TestTest struct { 169 | Test string 170 | } 171 | 172 | func TestOn(t *testing.T) { 173 | agent := New() 174 | varr := &TestTest{Test: "W0h00"} 175 | fired := 0 176 | agent.Map(varr) 177 | agent.On("test", func(te *TestTest) { 178 | if te.Test != "W0h00" { 179 | t.Errorf("Cannot access to injections :(") 180 | } 181 | fired++ 182 | }) 183 | 184 | agent.Emit("test") 185 | agent.Emit("test") 186 | agent.Emit("test") 187 | agent.Emit("test") 188 | 189 | if fired != 4 { 190 | t.Errorf("Event not fired :(") 191 | } 192 | } 193 | 194 | func TestOnce(t *testing.T) { 195 | agent := New() 196 | varr := &TestTest{Test: "Just Once!"} 197 | fired := 0 198 | agent.Map(varr) 199 | agent.Once("test", func(te *TestTest) { 200 | if te.Test != "Just Once!" { 201 | t.Errorf("Cannot access to injections :(") 202 | } 203 | fired++ 204 | }) 205 | 206 | agent.Emit("test") 207 | agent.Emit("test") 208 | agent.Emit("test") 209 | agent.Emit("test") 210 | 211 | if fired != 1 { 212 | t.Errorf("Event not fired once :(") 213 | } 214 | } 215 | 216 | func TestEmitSync(t *testing.T) { 217 | agent := New() 218 | varr := &TestTest{Test: "Just Once?"} 219 | fired := 0 220 | agent.Map(varr) 221 | agent.Once("test", func(te *TestTest) { 222 | if te.Test != "Just Once?" { 223 | t.Errorf("Cannot access to injections :(") 224 | } 225 | fired++ 226 | }) 227 | agent.Once("test", func(te *TestTest) { 228 | if te.Test != "Just Once?" { 229 | t.Errorf("Cannot access to injections :(") 230 | } 231 | fired++ 232 | }) 233 | agent.Once("test", func(te *TestTest) { 234 | if te.Test != "Just Once?" { 235 | t.Errorf("Cannot access to injections :(") 236 | } 237 | fired++ 238 | }) 239 | agent.Once("test", func(te *TestTest) { 240 | if te.Test != "Just Once?" { 241 | t.Errorf("Cannot access to injections :(") 242 | } 243 | fired++ 244 | }) 245 | 246 | agent.EmitSync("test") 247 | 248 | if fired != 4 { 249 | t.Errorf("Event not fired once :(") 250 | } 251 | } 252 | 253 | func TestTimer(t *testing.T) { 254 | agent := New() 255 | var tid TimerID = "test" 256 | fired := 0 257 | agent.Timer(tid, time.Now(), time.Duration(5), true, func(a *Anagent) { 258 | fired++ 259 | if fired > 4 { 260 | a.Stop() 261 | } 262 | }) 263 | 264 | timer := agent.GetTimer(tid) 265 | 266 | if timer.recurring == false { 267 | t.Errorf("Timer should be recurring") 268 | } 269 | 270 | agent.Start() 271 | if fired != 5 { 272 | t.Errorf("Agent middlewares are working and can stop the loop") 273 | } 274 | } 275 | 276 | func TestNext(t *testing.T) { 277 | 278 | agent := New() 279 | agent.BusyLoop = true 280 | fired := 0 281 | loop := 0 282 | 283 | agent.Use(func(a *Anagent) { 284 | loop++ 285 | }) 286 | 287 | agent.Next(func(a *Anagent) { 288 | fired++ 289 | }) 290 | 291 | agent.AddTimerSeconds(int64(3), func(a *Anagent) { 292 | a.Stop() 293 | }) 294 | 295 | agent.Start() 296 | 297 | if fired != 1 { 298 | t.Error("Next is removed from the loop") 299 | } 300 | 301 | if fired >= loop { 302 | t.Error("Loop had to run for long ", loop) 303 | } 304 | } 305 | 306 | func assertPanic(t *testing.T, f func()) { 307 | defer func() { 308 | if r := recover(); r == nil { 309 | t.Errorf("The code did not panic") 310 | } 311 | }() 312 | f() 313 | } 314 | 315 | func assertSleep(secSleep float64, t *testing.T, f func()) { 316 | start := time.Now() 317 | f() 318 | sec := time.Since(start).Seconds() 319 | 320 | if sec < secSleep || sec > secSleep*1.05 { 321 | t.Error("Timer wasn't fired in the specified time") 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2018 Ettore Di Giacinto 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining 4 | // a copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included 11 | // in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 15 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 18 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 19 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | package anagent 22 | 23 | import ( 24 | "crypto/md5" 25 | "encoding/hex" 26 | "math/rand" 27 | ) 28 | 29 | // GetMD5Hash is a utility function to get MD5 digest 30 | // of the supplied string. 31 | func GetMD5Hash(text string) string { 32 | hasher := md5.New() 33 | hasher.Write([]byte(text)) 34 | return hex.EncodeToString(hasher.Sum(nil)) 35 | } 36 | 37 | func RandTimer(m map[TimerID]*Timer) (TimerID, *Timer) { 38 | i := rand.Intn(len(m)) 39 | var tid TimerID 40 | var timer Timer 41 | 42 | for k := range m { 43 | if i == 0 { 44 | tid = k 45 | timer = *m[k] 46 | break 47 | } 48 | i-- 49 | } 50 | 51 | return tid, &timer 52 | } 53 | -------------------------------------------------------------------------------- /vendor/github.com/chuckpreslar/emission/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /vendor/github.com/chuckpreslar/emission/README.md: -------------------------------------------------------------------------------- 1 | emission 2 | -------- 3 | 4 | A simple event emitter for Go. 5 | 6 | [![Build Status](https://drone.io/github.com/chuckpreslar/emission/status.png)](https://drone.io/github.com/chuckpreslar/emission/latest) 7 | 8 | ## Installation 9 | 10 | With Google's [Go](http://www.golang.org) installed on your machine: 11 | 12 | $ go get -u github.com/chuckpreslar/emission 13 | 14 | ## Usage 15 | 16 | If you've ever used an event emitter before, using Emission should be very familiar. 17 | 18 | ```go 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | ) 24 | 25 | import ( 26 | "github.com/chuckpreslar/emission" 27 | ) 28 | 29 | func main() { 30 | emitter := emission.NewEmitter() 31 | 32 | hello := func(to string) { 33 | fmt.Printf("Hello %s!\n", to) 34 | } 35 | 36 | count := func(count int) { 37 | for i := 0; i < count; i++ { 38 | fmt.Println(i) 39 | } 40 | } 41 | 42 | emitter.On("hello", hello). 43 | On("count", count). 44 | Emit("hello", "world"). 45 | Emit("count", 5) 46 | } 47 | 48 | ``` 49 | 50 | ## About 51 | 52 | The `emission` package provides an event emitter making use of the reflect packages ability to call functions. Using the `Call` method on the value of a function allows passing any type of function to the event emiiter, regardless of the functions parameters. 53 | 54 | ## Documentation 55 | 56 | View godoc's or visit [godoc.org](http://godoc.org/github.com/chuckpreslar/emission). 57 | 58 | $ godoc emission 59 | 60 | ## License 61 | 62 | > The MIT License (MIT) 63 | 64 | > Copyright (c) 2013 - 2015 Chuck Preslar 65 | 66 | > Permission is hereby granted, free of charge, to any person obtaining a copy 67 | > of this software and associated documentation files (the "Software"), to deal 68 | > in the Software without restriction, including without limitation the rights 69 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 70 | > copies of the Software, and to permit persons to whom the Software is 71 | > furnished to do so, subject to the following conditions: 72 | 73 | > The above copyright notice and this permission notice shall be included in 74 | > all copies or substantial portions of the Software. 75 | 76 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 77 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 78 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 79 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 80 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 81 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 82 | > THE SOFTWARE. 83 | -------------------------------------------------------------------------------- /vendor/github.com/chuckpreslar/emission/emitter.go: -------------------------------------------------------------------------------- 1 | // Package emission provides an event emitter. 2 | package emission 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "os" 8 | "reflect" 9 | "sync" 10 | ) 11 | 12 | // Default number of maximum listeners for an event. 13 | const DefaultMaxListeners = 10 14 | 15 | // Error presented when an invalid argument is provided as a listener function 16 | var ErrNoneFunction = errors.New("Kind of Value for listener is not Func.") 17 | 18 | // RecoveryListener ... 19 | type RecoveryListener func(interface{}, interface{}, error) 20 | 21 | // Emitter ... 22 | type Emitter struct { 23 | // Mutex to prevent race conditions within the Emitter. 24 | *sync.Mutex 25 | // Map of event to a slice of listener function's reflect Values. 26 | events map[interface{}][]reflect.Value 27 | // Optional RecoveryListener to call when a panic occurs. 28 | recoverer RecoveryListener 29 | // Maximum listeners for debugging potential memory leaks. 30 | maxListeners int 31 | // Map used to remove Listeners wrapped in a Once func 32 | onces map[reflect.Value]reflect.Value 33 | } 34 | 35 | // AddListener appends the listener argument to the event arguments slice 36 | // in the Emitter's events map. If the number of listeners for an event 37 | // is greater than the Emitter's maximum listeners then a warning is printed. 38 | // If the relect Value of the listener does not have a Kind of Func then 39 | // AddListener panics. If a RecoveryListener has been set then it is called 40 | // recovering from the panic. 41 | func (emitter *Emitter) AddListener(event, listener interface{}) *Emitter { 42 | emitter.Lock() 43 | defer emitter.Unlock() 44 | 45 | fn := reflect.ValueOf(listener) 46 | 47 | if reflect.Func != fn.Kind() { 48 | if nil == emitter.recoverer { 49 | panic(ErrNoneFunction) 50 | } else { 51 | emitter.recoverer(event, listener, ErrNoneFunction) 52 | } 53 | } 54 | 55 | if emitter.maxListeners != -1 && emitter.maxListeners < len(emitter.events[event])+1 { 56 | fmt.Fprintf(os.Stdout, "Warning: event `%v` has exceeded the maximum "+ 57 | "number of listeners of %d.\n", event, emitter.maxListeners) 58 | } 59 | 60 | emitter.events[event] = append(emitter.events[event], fn) 61 | 62 | return emitter 63 | } 64 | 65 | // On is an alias for AddListener. 66 | func (emitter *Emitter) On(event, listener interface{}) *Emitter { 67 | return emitter.AddListener(event, listener) 68 | } 69 | 70 | // RemoveListener removes the listener argument from the event arguments slice 71 | // in the Emitter's events map. If the reflect Value of the listener does not 72 | // have a Kind of Func then RemoveListener panics. If a RecoveryListener has 73 | // been set then it is called after recovering from the panic. 74 | func (emitter *Emitter) RemoveListener(event, listener interface{}) *Emitter { 75 | emitter.Lock() 76 | defer emitter.Unlock() 77 | 78 | fn := reflect.ValueOf(listener) 79 | 80 | if reflect.Func != fn.Kind() { 81 | if nil == emitter.recoverer { 82 | panic(ErrNoneFunction) 83 | } else { 84 | emitter.recoverer(event, listener, ErrNoneFunction) 85 | } 86 | } 87 | 88 | if events, ok := emitter.events[event]; ok { 89 | if _, ok = emitter.onces[fn]; ok { 90 | fn = emitter.onces[fn] 91 | } 92 | 93 | newEvents := []reflect.Value{} 94 | 95 | for _, listener := range events { 96 | if fn.Pointer() != listener.Pointer() { 97 | newEvents = append(newEvents, listener) 98 | } 99 | } 100 | 101 | emitter.events[event] = newEvents 102 | } 103 | 104 | return emitter 105 | } 106 | 107 | // Off is an alias for RemoveListener. 108 | func (emitter *Emitter) Off(event, listener interface{}) *Emitter { 109 | return emitter.RemoveListener(event, listener) 110 | } 111 | 112 | // Once generates a new function which invokes the supplied listener 113 | // only once before removing itself from the event's listener slice 114 | // in the Emitter's events map. If the reflect Value of the listener 115 | // does not have a Kind of Func then Once panics. If a RecoveryListener 116 | // has been set then it is called after recovering from the panic. 117 | func (emitter *Emitter) Once(event, listener interface{}) *Emitter { 118 | fn := reflect.ValueOf(listener) 119 | 120 | if reflect.Func != fn.Kind() { 121 | if nil == emitter.recoverer { 122 | panic(ErrNoneFunction) 123 | } else { 124 | emitter.recoverer(event, listener, ErrNoneFunction) 125 | } 126 | } 127 | 128 | var run func(...interface{}) 129 | 130 | run = func(arguments ...interface{}) { 131 | defer emitter.RemoveListener(event, run) 132 | 133 | var values []reflect.Value 134 | 135 | for i := 0; i < len(arguments); i++ { 136 | values = append(values, reflect.ValueOf(arguments[i])) 137 | } 138 | 139 | fn.Call(values) 140 | } 141 | 142 | // Lock before changing onces 143 | emitter.Lock() 144 | emitter.onces[fn] = reflect.ValueOf(run) 145 | emitter.Unlock() 146 | 147 | emitter.AddListener(event, run) 148 | return emitter 149 | } 150 | 151 | // Emit attempts to use the reflect package to Call each listener stored 152 | // in the Emitter's events map with the supplied arguments. Each listener 153 | // is called within its own go routine. The reflect package will panic if 154 | // the agruments supplied do not align the parameters of a listener function. 155 | // If a RecoveryListener has been set then it is called after recovering from 156 | // the panic. 157 | func (emitter *Emitter) Emit(event interface{}, arguments ...interface{}) *Emitter { 158 | var ( 159 | listeners []reflect.Value 160 | ok bool 161 | ) 162 | 163 | // Lock the mutex when reading from the Emitter's 164 | // events map. 165 | emitter.Lock() 166 | 167 | if listeners, ok = emitter.events[event]; !ok { 168 | // If the Emitter does not include the event in its 169 | // event map, it has no listeners to Call yet. 170 | emitter.Unlock() 171 | return emitter 172 | } 173 | 174 | // Unlock the mutex immediately following the read 175 | // instead of deferring so that listeners registered 176 | // with Once can aquire the mutex for removal. 177 | emitter.Unlock() 178 | 179 | var wg sync.WaitGroup 180 | 181 | wg.Add(len(listeners)) 182 | 183 | for _, fn := range listeners { 184 | go func(fn reflect.Value) { 185 | defer wg.Done() 186 | 187 | // Recover from potential panics, supplying them to a 188 | // RecoveryListener if one has been set, else allowing 189 | // the panic to occur. 190 | if nil != emitter.recoverer { 191 | defer func() { 192 | if r := recover(); nil != r { 193 | err := fmt.Errorf("%v", r) 194 | emitter.recoverer(event, fn.Interface(), err) 195 | } 196 | }() 197 | } 198 | 199 | var values []reflect.Value 200 | 201 | for i := 0; i < len(arguments); i++ { 202 | if arguments[i] == nil { 203 | values = append(values, reflect.New(fn.Type().In(i)).Elem()) 204 | } else { 205 | values = append(values, reflect.ValueOf(arguments[i])) 206 | } 207 | } 208 | 209 | fn.Call(values) 210 | }(fn) 211 | } 212 | 213 | wg.Wait() 214 | return emitter 215 | } 216 | 217 | // EmitSync attempts to use the reflect package to Call each listener stored 218 | // in the Emitter's events map with the supplied arguments. Each listener 219 | // is called synchronously. The reflect package will panic if 220 | // the agruments supplied do not align the parameters of a listener function. 221 | // If a RecoveryListener has been set then it is called after recovering from 222 | // the panic. 223 | func (emitter *Emitter) EmitSync(event interface{}, arguments ...interface{}) *Emitter { 224 | var ( 225 | listeners []reflect.Value 226 | ok bool 227 | ) 228 | 229 | // Lock the mutex when reading from the Emitter's 230 | // events map. 231 | emitter.Lock() 232 | 233 | if listeners, ok = emitter.events[event]; !ok { 234 | // If the Emitter does not include the event in its 235 | // event map, it has no listeners to Call yet. 236 | emitter.Unlock() 237 | return emitter 238 | } 239 | 240 | // Unlock the mutex immediately following the read 241 | // instead of deferring so that listeners registered 242 | // with Once can aquire the mutex for removal. 243 | emitter.Unlock() 244 | 245 | for _, fn := range listeners { 246 | // Recover from potential panics, supplying them to a 247 | // RecoveryListener if one has been set, else allowing 248 | // the panic to occur. 249 | if nil != emitter.recoverer { 250 | defer func() { 251 | if r := recover(); nil != r { 252 | err := fmt.Errorf("%v", r) 253 | emitter.recoverer(event, fn.Interface(), err) 254 | } 255 | }() 256 | } 257 | 258 | var values []reflect.Value 259 | 260 | for i := 0; i < len(arguments); i++ { 261 | if arguments[i] == nil { 262 | values = append(values, reflect.New(fn.Type().In(i)).Elem()) 263 | } else { 264 | values = append(values, reflect.ValueOf(arguments[i])) 265 | } 266 | } 267 | 268 | fn.Call(values) 269 | } 270 | 271 | return emitter 272 | } 273 | 274 | // RecoverWith sets the listener to call when a panic occurs, recovering from 275 | // panics and attempting to keep the application from crashing. 276 | func (emitter *Emitter) RecoverWith(listener RecoveryListener) *Emitter { 277 | emitter.recoverer = listener 278 | return emitter 279 | } 280 | 281 | // SetMaxListeners sets the maximum number of listeners per 282 | // event for the Emitter. If -1 is passed as the maximum, 283 | // all events may have unlimited listeners. By default, each 284 | // event can have a maximum number of 10 listeners which is 285 | // useful for finding memory leaks. 286 | func (emitter *Emitter) SetMaxListeners(max int) *Emitter { 287 | emitter.Lock() 288 | defer emitter.Unlock() 289 | 290 | emitter.maxListeners = max 291 | return emitter 292 | } 293 | 294 | // GetListenerCount gets count of listeners for a given event. 295 | func (emitter *Emitter) GetListenerCount(event interface{}) (count int) { 296 | emitter.Lock() 297 | if listeners, ok := emitter.events[event]; ok { 298 | count = len(listeners) 299 | } 300 | emitter.Unlock() 301 | return 302 | } 303 | 304 | // NewEmitter returns a new Emitter object, defaulting the 305 | // number of maximum listeners per event to the DefaultMaxListeners 306 | // constant and initializing its events map. 307 | func NewEmitter() (emitter *Emitter) { 308 | emitter = new(Emitter) 309 | emitter.Mutex = new(sync.Mutex) 310 | emitter.events = make(map[interface{}][]reflect.Value) 311 | emitter.maxListeners = DefaultMaxListeners 312 | emitter.onces = make(map[reflect.Value]reflect.Value) 313 | return 314 | } 315 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/.gitignore: -------------------------------------------------------------------------------- 1 | inject 2 | inject.test 3 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/README.md: -------------------------------------------------------------------------------- 1 | inject 2 | ====== 3 | 4 | Dependency injection for go 5 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/inject.go: -------------------------------------------------------------------------------- 1 | // Package inject provides utilities for mapping and injecting dependencies in various ways. 2 | package inject 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | // Injector represents an interface for mapping and injecting dependencies into structs 10 | // and function arguments. 11 | type Injector interface { 12 | Applicator 13 | Invoker 14 | TypeMapper 15 | // SetParent sets the parent of the injector. If the injector cannot find a 16 | // dependency in its Type map it will check its parent before returning an 17 | // error. 18 | SetParent(Injector) 19 | } 20 | 21 | // Applicator represents an interface for mapping dependencies to a struct. 22 | type Applicator interface { 23 | // Maps dependencies in the Type map to each field in the struct 24 | // that is tagged with 'inject'. Returns an error if the injection 25 | // fails. 26 | Apply(interface{}) error 27 | } 28 | 29 | // Invoker represents an interface for calling functions via reflection. 30 | type Invoker interface { 31 | // Invoke attempts to call the interface{} provided as a function, 32 | // providing dependencies for function arguments based on Type. Returns 33 | // a slice of reflect.Value representing the returned values of the function. 34 | // Returns an error if the injection fails. 35 | Invoke(interface{}) ([]reflect.Value, error) 36 | } 37 | 38 | // TypeMapper represents an interface for mapping interface{} values based on type. 39 | type TypeMapper interface { 40 | // Maps the interface{} value based on its immediate type from reflect.TypeOf. 41 | Map(interface{}) TypeMapper 42 | // Maps the interface{} value based on the pointer of an Interface provided. 43 | // This is really only useful for mapping a value as an interface, as interfaces 44 | // cannot at this time be referenced directly without a pointer. 45 | MapTo(interface{}, interface{}) TypeMapper 46 | // Provides a possibility to directly insert a mapping based on type and value. 47 | // This makes it possible to directly map type arguments not possible to instantiate 48 | // with reflect like unidirectional channels. 49 | Set(reflect.Type, reflect.Value) TypeMapper 50 | // Returns the Value that is mapped to the current type. Returns a zeroed Value if 51 | // the Type has not been mapped. 52 | Get(reflect.Type) reflect.Value 53 | } 54 | 55 | type injector struct { 56 | values map[reflect.Type]reflect.Value 57 | parent Injector 58 | } 59 | 60 | // InterfaceOf dereferences a pointer to an Interface type. 61 | // It panics if value is not an pointer to an interface. 62 | func InterfaceOf(value interface{}) reflect.Type { 63 | t := reflect.TypeOf(value) 64 | 65 | for t.Kind() == reflect.Ptr { 66 | t = t.Elem() 67 | } 68 | 69 | if t.Kind() != reflect.Interface { 70 | panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") 71 | } 72 | 73 | return t 74 | } 75 | 76 | // New returns a new Injector. 77 | func New() Injector { 78 | return &injector{ 79 | values: make(map[reflect.Type]reflect.Value), 80 | } 81 | } 82 | 83 | // Invoke attempts to call the interface{} provided as a function, 84 | // providing dependencies for function arguments based on Type. 85 | // Returns a slice of reflect.Value representing the returned values of the function. 86 | // Returns an error if the injection fails. 87 | // It panics if f is not a function 88 | func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { 89 | t := reflect.TypeOf(f) 90 | 91 | var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func 92 | for i := 0; i < t.NumIn(); i++ { 93 | argType := t.In(i) 94 | val := inj.Get(argType) 95 | if !val.IsValid() { 96 | return nil, fmt.Errorf("Value not found for type %v", argType) 97 | } 98 | 99 | in[i] = val 100 | } 101 | 102 | return reflect.ValueOf(f).Call(in), nil 103 | } 104 | 105 | // Maps dependencies in the Type map to each field in the struct 106 | // that is tagged with 'inject'. 107 | // Returns an error if the injection fails. 108 | func (inj *injector) Apply(val interface{}) error { 109 | v := reflect.ValueOf(val) 110 | 111 | for v.Kind() == reflect.Ptr { 112 | v = v.Elem() 113 | } 114 | 115 | if v.Kind() != reflect.Struct { 116 | return nil // Should not panic here ? 117 | } 118 | 119 | t := v.Type() 120 | 121 | for i := 0; i < v.NumField(); i++ { 122 | f := v.Field(i) 123 | structField := t.Field(i) 124 | if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { 125 | ft := f.Type() 126 | v := inj.Get(ft) 127 | if !v.IsValid() { 128 | return fmt.Errorf("Value not found for type %v", ft) 129 | } 130 | 131 | f.Set(v) 132 | } 133 | 134 | } 135 | 136 | return nil 137 | } 138 | 139 | // Maps the concrete value of val to its dynamic type using reflect.TypeOf, 140 | // It returns the TypeMapper registered in. 141 | func (i *injector) Map(val interface{}) TypeMapper { 142 | i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) 143 | return i 144 | } 145 | 146 | func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { 147 | i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) 148 | return i 149 | } 150 | 151 | // Maps the given reflect.Type to the given reflect.Value and returns 152 | // the Typemapper the mapping has been registered in. 153 | func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { 154 | i.values[typ] = val 155 | return i 156 | } 157 | 158 | func (i *injector) Get(t reflect.Type) reflect.Value { 159 | val := i.values[t] 160 | 161 | if val.IsValid() { 162 | return val 163 | } 164 | 165 | // no concrete types found, try to find implementors 166 | // if t is an interface 167 | if t.Kind() == reflect.Interface { 168 | for k, v := range i.values { 169 | if k.Implements(t) { 170 | val = v 171 | break 172 | } 173 | } 174 | } 175 | 176 | // Still no type found, try to look it up on the parent 177 | if !val.IsValid() && i.parent != nil { 178 | val = i.parent.Get(t) 179 | } 180 | 181 | return val 182 | 183 | } 184 | 185 | func (i *injector) SetParent(parent Injector) { 186 | i.parent = parent 187 | } 188 | --------------------------------------------------------------------------------