├── .idea ├── .gitignore ├── gobserve.iml ├── modules.xml └── vcs.xml ├── LICENSE.txt ├── README.md ├── dispatcher.go ├── dispatcher_test.go ├── events.go ├── examples ├── concurrentStrategy.go ├── priorityStrategy.go ├── simple.go └── unsubscriber.go ├── globals.go ├── go.mod ├── go.sum ├── subscriber.go ├── types.go ├── utils.go └── utils_test.go /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/gobserve.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Henri Larget 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gobserve 2 | ===== 3 | 4 | A simple event-dispatcher library. 5 | 6 | [![Go Reference](https://pkg.go.dev/badge/github.com/decima/gobserve.svg)](https://pkg.go.dev/github.com/decima/gobserve) 7 | 8 | ## Install 9 | 10 | to add the library to your project simply run : 11 | 12 | ```bash 13 | go get github.com/decima/gobserve 14 | ``` 15 | 16 | ## example usage : 17 | 18 | ```go 19 | package main 20 | 21 | import ( 22 | "github.com/decima/gobserve" 23 | "log" 24 | ) 25 | 26 | type helloWorld int 27 | 28 | func (s helloWorld) Name() string { 29 | return "sayHello" 30 | } 31 | 32 | func main() { 33 | gobserve.Subscribe("sayHello", func(h helloWorld) error { 34 | log.Println("hello from 1") 35 | return nil 36 | }) 37 | gobserve.Subscribe("sayHello", func(h helloWorld) error { 38 | log.Println("hello from 2") 39 | return nil 40 | }) 41 | 42 | gobserve.DispatchConcurrent(helloWorld(1)) 43 | 44 | } 45 | ``` 46 | 47 | By default you can use the Subscribe and Dispatch methods in order to use a globally defined event Subscriber. 48 | But you can create your own dispatcher for more precise and independant control. 49 | -------------------------------------------------------------------------------- /dispatcher.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type actionType[T EventInterface] func(T) error 8 | type subscriptions[T EventInterface] map[string]map[int]map[string]actionType[T] 9 | 10 | // This structure contains a map of every events registered in the event Dispatcher 11 | type EventDispatcher[T EventInterface] struct { 12 | subscriptions[T] 13 | } 14 | 15 | // NewDispatcher creates a new eventDispatcher for EventInterfaces 16 | func NewDispatcher() EventDispatcher[EventInterface] { 17 | return EventDispatcher[EventInterface]{ 18 | subscriptions: subscriptions[EventInterface]{}, 19 | } 20 | } 21 | 22 | //Dispatch sends an event through every concerned event subscribers 23 | func (e *EventDispatcher[T]) Dispatch( 24 | event T, 25 | processingStrategy ProcessingStrategy, 26 | ) []error { 27 | errorListMutex := sync.Mutex{} 28 | errorList := []error{} 29 | if _, ok := e.subscriptions[event.Name()]; !ok { 30 | return nil 31 | } 32 | 33 | actionsPerPriority := sortIntMap(e.subscriptions[event.Name()]) 34 | 35 | wg := sync.WaitGroup{} 36 | 37 | for _, actionList := range actionsPerPriority { 38 | 39 | for _, action := range actionList { 40 | wg.Add(1) 41 | 42 | go func(action func(T) error) { 43 | err := action(event) 44 | if err != nil { 45 | errorListMutex.Lock() 46 | errorList = append(errorList, err) 47 | errorListMutex.Unlock() 48 | } 49 | wg.Done() 50 | }(action) 51 | if processingStrategy == Sequential { 52 | wg.Wait() 53 | } 54 | } 55 | 56 | if processingStrategy == PerPriorityConcurrent { 57 | wg.Wait() 58 | } 59 | } 60 | if processingStrategy == Concurrent { 61 | wg.Wait() 62 | } 63 | 64 | if len(errorList) == 0 { 65 | return nil 66 | } 67 | return errorList 68 | } 69 | -------------------------------------------------------------------------------- /dispatcher_test.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | const DEMO_EVENT_NAME = "demo" 9 | 10 | type TestEvent struct { 11 | mux *sync.Mutex 12 | callStack *[]string 13 | } 14 | 15 | func (t TestEvent) Name() string { 16 | return DEMO_EVENT_NAME 17 | } 18 | 19 | func TestDispatch(t *testing.T) { 20 | 21 | dispatcher := NewDispatcher() 22 | 23 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 24 | func(event EventInterface) error { 25 | return func(evt TestEvent) error { 26 | *evt.callStack = append(*evt.callStack, "1") 27 | 28 | return nil 29 | }(event.(TestEvent)) 30 | }, 0) 31 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 32 | func(event EventInterface) error { 33 | return func(evt TestEvent) error { 34 | *evt.callStack = append(*evt.callStack, "2") 35 | 36 | return nil 37 | }(event.(TestEvent)) 38 | }, 0) 39 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 40 | func(event EventInterface) error { 41 | return func(evt TestEvent) error { 42 | *evt.callStack = append(*evt.callStack, "3") 43 | 44 | return nil 45 | }(event.(TestEvent)) 46 | }, 0) 47 | callStack := []string{} 48 | dispatcher.Dispatch(TestEvent{callStack: &callStack}, Sequential) 49 | 50 | expected := []string{"1", "2", "3"} 51 | 52 | if len(expected) != len(callStack) { 53 | t.Errorf("Expected String(%v) is not same as"+ 54 | " actual string (%v)", expected, callStack) 55 | } 56 | for i := 0; i < len(expected); i++ { 57 | 58 | if expected[i] != callStack[i] { 59 | t.Errorf("Expected slice (%v) is not same as"+ 60 | " actual slice (%v)", expected, callStack) 61 | } 62 | } 63 | 64 | } 65 | 66 | func TestDispatch2(t *testing.T) { 67 | 68 | dispatcher := NewDispatcher() 69 | 70 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 71 | func(event EventInterface) error { 72 | return func(evt TestEvent) error { 73 | *evt.callStack = append(*evt.callStack, "1") 74 | 75 | return nil 76 | }(event.(TestEvent)) 77 | }, 1) 78 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 79 | func(event EventInterface) error { 80 | return func(evt TestEvent) error { 81 | *evt.callStack = append(*evt.callStack, "2") 82 | 83 | return nil 84 | }(event.(TestEvent)) 85 | }, 2) 86 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 87 | func(event EventInterface) error { 88 | return func(evt TestEvent) error { 89 | *evt.callStack = append(*evt.callStack, "3") 90 | 91 | return nil 92 | }(event.(TestEvent)) 93 | }, 3) 94 | callStack := []string{} 95 | dispatcher.Dispatch(TestEvent{callStack: &callStack}, Sequential) 96 | 97 | expected := []string{"3", "2", "1"} 98 | 99 | if len(expected) != len(callStack) { 100 | t.Errorf("Expected String(%v) is not same as"+ 101 | " actual string (%v)", expected, callStack) 102 | } 103 | for i := 0; i < len(expected); i++ { 104 | 105 | if expected[i] != callStack[i] { 106 | t.Errorf("Expected slice (%v) is not same as"+ 107 | " actual slice (%v)", expected, callStack) 108 | } 109 | } 110 | } 111 | 112 | func TestDispatch3(t *testing.T) { 113 | 114 | dispatcher := NewDispatcher() 115 | 116 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 117 | func(event EventInterface) error { 118 | return func(evt TestEvent) error { 119 | 120 | *evt.callStack = append(*evt.callStack, "1") 121 | 122 | return nil 123 | }(event.(TestEvent)) 124 | }, 1) 125 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 126 | func(event EventInterface) error { 127 | return func(evt TestEvent) error { 128 | *evt.callStack = append(*evt.callStack, "2") 129 | 130 | return nil 131 | }(event.(TestEvent)) 132 | }, 2) 133 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 134 | func(event EventInterface) error { 135 | return func(evt TestEvent) error { 136 | *evt.callStack = append(*evt.callStack, "3") 137 | 138 | return nil 139 | }(event.(TestEvent)) 140 | }, 1) 141 | callStack := []string{} 142 | dispatcher.Dispatch(TestEvent{callStack: &callStack}, PerPriorityConcurrent) 143 | 144 | if 3 != len(callStack) { 145 | t.Errorf("Expected Len(3) is not same as"+ 146 | " actual slice (%v)", callStack) 147 | } 148 | 149 | if "2" != callStack[0] { 150 | 151 | t.Errorf("Expected 2 to be first in the list"+ 152 | " got (%v)", callStack) 153 | } 154 | 155 | } 156 | 157 | func TestDispatch4(t *testing.T) { 158 | dispatcher := NewDispatcher() 159 | 160 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 161 | func(event EventInterface) error { 162 | return func(evt TestEvent) error { 163 | evt.mux.Lock() 164 | *evt.callStack = append(*evt.callStack, "1") 165 | evt.mux.Unlock() 166 | return nil 167 | }(event.(TestEvent)) 168 | }, 1) 169 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 170 | func(event EventInterface) error { 171 | return func(evt TestEvent) error { 172 | evt.mux.Lock() 173 | 174 | *evt.callStack = append(*evt.callStack, "2") 175 | evt.mux.Unlock() 176 | return nil 177 | }(event.(TestEvent)) 178 | }, 2) 179 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME, 180 | func(event EventInterface) error { 181 | return func(evt TestEvent) error { 182 | evt.mux.Lock() 183 | *evt.callStack = append(*evt.callStack, "3") 184 | evt.mux.Unlock() 185 | return nil 186 | }(event.(TestEvent)) 187 | }, 1) 188 | callStack := []string{} 189 | mutex := sync.Mutex{} 190 | dispatcher.Dispatch(TestEvent{callStack: &callStack, mux: &mutex}, Concurrent) 191 | if 3 != len(callStack) { 192 | t.Errorf("Expected Len(3) is not same as"+ 193 | " actual slice (%v)", callStack) 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /events.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | //EventInterface This interface is the "event enveloppe" for dispatching elements through all subscribes 4 | type EventInterface interface { 5 | //Name the name of the event 6 | Name() string 7 | } 8 | -------------------------------------------------------------------------------- /examples/concurrentStrategy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/decima/gobserve" 6 | "log" 7 | ) 8 | 9 | type VoteEvent struct { 10 | Result *int 11 | } 12 | 13 | func (v VoteEvent) Name() string { 14 | return "vote" 15 | } 16 | 17 | func main() { 18 | for i := 0; i < 50; i++ { 19 | func(i int) { 20 | gobserve.Subscribe("vote", func(v VoteEvent) error { 21 | if i%3 == 0 { 22 | *v.Result-- 23 | fmt.Printf("I(%v) voted NO : %v\n", i, *v.Result) 24 | 25 | } else { 26 | *v.Result++ 27 | fmt.Printf("I(%v) voted YES: %v\n", i, *v.Result) 28 | } 29 | return nil 30 | }) 31 | }(i) 32 | } 33 | 34 | result := 0 35 | v := VoteEvent{ 36 | Result: &result, 37 | } 38 | gobserve.DispatchConcurrent(v) 39 | 40 | log.Println(*v.Result) 41 | } 42 | -------------------------------------------------------------------------------- /examples/priorityStrategy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/decima/gobserve" 5 | "log" 6 | "time" 7 | ) 8 | 9 | type RacePodium struct{} 10 | 11 | func (r RacePodium) Name() string { 12 | return "podium" 13 | } 14 | 15 | func main() { 16 | 17 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error { 18 | log.Println("LAURA : I finished ex-ego with SARAH") 19 | time.Sleep(2 * time.Second) 20 | 21 | return nil 22 | }, 600) 23 | 24 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error { 25 | log.Println("BOB : I finished Last") 26 | time.Sleep(2 * time.Second) 27 | return nil 28 | }, 400) 29 | 30 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error { 31 | log.Println("JOHN : Almost first!") 32 | time.Sleep(2 * time.Second) 33 | 34 | return nil 35 | 36 | }, 800) 37 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error { 38 | log.Println("KEN : C'mon barbie, let's go party, i'm first!") 39 | time.Sleep(2 * time.Second) 40 | 41 | return nil 42 | 43 | }, 1000) 44 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error { 45 | log.Println("SARAH : I finished at the same time as LAURA") 46 | time.Sleep(2 * time.Second) 47 | 48 | return nil 49 | 50 | }, 600) 51 | 52 | gobserve.DispatchConcurrentPerPriority(RacePodium{}) 53 | 54 | } 55 | -------------------------------------------------------------------------------- /examples/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/decima/gobserve" 5 | "log" 6 | "time" 7 | ) 8 | 9 | type helloMessage struct { 10 | From string 11 | } 12 | 13 | func (s helloMessage) Name() string { 14 | return "sayHello" 15 | } 16 | 17 | func (s helloMessage) Source() string { 18 | return s.From 19 | } 20 | 21 | func main() { 22 | 23 | gobserve.SubscribeWithPriority("sayHello", func(h helloMessage) error { 24 | log.Println("I lately received Hello from " + h.Source()) 25 | time.Sleep(2 * time.Second) 26 | 27 | return nil 28 | }, -100) 29 | 30 | gobserve.SubscribeWithPriority("sayHello", func(h helloMessage) error { 31 | log.Println("I first received Hello from " + h.Source()) 32 | time.Sleep(2 * time.Second) 33 | return nil 34 | }, 200) 35 | 36 | gobserve.SubscribeWithPriority("sayHello", func(h helloMessage) error { 37 | log.Println("I received Hello from " + h.Source()) 38 | time.Sleep(2 * time.Second) 39 | 40 | return nil 41 | }, 100) 42 | 43 | gobserve.DispatchSequential(helloMessage{From: "Henri"}) 44 | } 45 | -------------------------------------------------------------------------------- /examples/unsubscriber.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/decima/gobserve" 5 | "log" 6 | ) 7 | 8 | type hello2Message struct { 9 | } 10 | 11 | func (s hello2Message) Name() string { 12 | return "sayHello2" 13 | } 14 | 15 | func main() { 16 | 17 | gobserve.SubscribeWithPriority("sayHello2", func(h hello2Message) error { 18 | log.Println("hello from subscriber with prio 3") 19 | return nil 20 | }, 3) 21 | 22 | subscription := gobserve.SubscribeWithPriority("sayHello2", func(h hello2Message) error { 23 | log.Println("hello from subscriber with prio 2") 24 | 25 | return nil 26 | }, 2) 27 | 28 | gobserve.SubscribeWithPriority("sayHello2", func(h hello2Message) error { 29 | log.Println("hello from subscriber with prio 1") 30 | 31 | return nil 32 | }, 1) 33 | 34 | gobserve.DispatchSequential(hello2Message{}) 35 | gobserve.Unsubscribe(subscription) 36 | gobserve.DispatchSequential(hello2Message{}) 37 | } 38 | -------------------------------------------------------------------------------- /globals.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | //globalEventDispatcher is an instance of the EventDispatch. 4 | //its purpose is to simplify the usage of the event dispatcher. 5 | var globalEventDispatcher = NewDispatcher() 6 | 7 | //Subscribe attach an action to an event name given. 8 | //Internally, this function, will attach the action to a normalPriority (0) 9 | func Subscribe[T EventInterface](name string, action func(T) error) interface{} { 10 | return SubscribeWithPriority(name, action, NormalPriority) 11 | } 12 | 13 | //SubscribeWithPriority attach an action to an event name given and weights the priority 14 | //The higher the priority the first will the action be trigger. 15 | func SubscribeWithPriority[T EventInterface](name string, action func(T) error, priority int) interface{} { 16 | return globalEventDispatcher.SubscribeWithPriority(name, func(event EventInterface) error { 17 | return action(event.(T)) 18 | }, priority) 19 | } 20 | 21 | //Unsubscribe removes the subscription previously made. 22 | func Unsubscribe(sub interface{}) { 23 | globalEventDispatcher.Unsubscribe(sub) 24 | } 25 | 26 | //Dispatch sends an event to every same name events. 27 | //Processing Strategies are defined for concurrency/sequential behaviors. 28 | func Dispatch[T EventInterface](event T, strategy ProcessingStrategy) []error { 29 | return globalEventDispatcher.Dispatch(event, strategy) 30 | } 31 | 32 | //DispatchSequential sends an event through every action in a sequential mode. 33 | func DispatchSequential[T EventInterface](event T) []error { 34 | return Dispatch(event, Sequential) 35 | } 36 | 37 | //DispatchConcurrentPerPriority processes every priority sequentially, but with concurrent working in the tasks. 38 | func DispatchConcurrentPerPriority[T EventInterface](event T) []error { 39 | return Dispatch(event, PerPriorityConcurrent) 40 | } 41 | 42 | func DispatchConcurrent[T EventInterface](event T) []error { 43 | return Dispatch(event, Concurrent) 44 | } 45 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/decima/gobserve 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 // indirect 7 | github.com/yuin/goldmark v1.4.1 // indirect 8 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect 9 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect 10 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect 11 | golang.org/x/tools v0.1.11 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 2 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 3 | github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM= 4 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 5 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 6 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 7 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= 8 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 9 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= 10 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 11 | golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= 12 | golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= 13 | -------------------------------------------------------------------------------- /subscriber.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | import "github.com/google/uuid" 4 | 5 | type Subscriber[T EventInterface] struct { 6 | eventName string 7 | priority int 8 | uniqueId string 9 | dispatcher *EventDispatcher[T] 10 | } 11 | 12 | //Subscribe attach an action to an event name given. 13 | //Internally, this function, will attach the action to a normalPriority (0) 14 | func (e *EventDispatcher[T]) Subscribe(name string, action func(T) error) Subscriber[T] { 15 | return e.SubscribeWithPriority(name, action, NormalPriority).(Subscriber[T]) 16 | } 17 | 18 | //SubscribeWithPriority attach an action to an event name given and weights the priority 19 | //The higher the priority the first will the action be trigger. 20 | func (e *EventDispatcher[T]) SubscribeWithPriority(name string, action func(T) error, priority int) interface{} { 21 | if _, ok := e.subscriptions[name]; !ok { 22 | e.subscriptions[name] = map[int]map[string]actionType[T]{} 23 | } 24 | if _, ok := e.subscriptions[name][priority]; !ok { 25 | e.subscriptions[name][priority] = map[string]actionType[T]{} 26 | } 27 | 28 | uniqueId := uuid.NewString() 29 | e.subscriptions[name][priority][uniqueId] = action 30 | 31 | return Subscriber[T]{ 32 | eventName: name, 33 | priority: priority, 34 | uniqueId: uniqueId, 35 | } 36 | } 37 | 38 | //Unsubscribe removes the subscription previously made. 39 | func (e *EventDispatcher[T]) Unsubscribe(rawSub interface{}) { 40 | sub := rawSub.(Subscriber[T]) 41 | if _, ok := e.subscriptions[sub.eventName][sub.priority][sub.uniqueId]; ok { 42 | delete(e.subscriptions[sub.eventName][sub.priority], sub.uniqueId) 43 | } 44 | if len(e.subscriptions[sub.eventName][sub.priority]) == 0 { 45 | delete(e.subscriptions[sub.eventName], sub.priority) 46 | } 47 | if len(e.subscriptions[sub.eventName]) == 0 { 48 | delete(e.subscriptions, sub.eventName) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | type ( 4 | //ProcessingStrategy the type of the 5 | ProcessingStrategy int 6 | ) 7 | 8 | const ( 9 | HighPriority = 100 10 | //NormalPriority default priority 11 | NormalPriority = 0 12 | LowPriority = -100 13 | 14 | //Sequential runs every action sequentially. 15 | Sequential ProcessingStrategy = iota 16 | //Concurrent runs every action at once, regardless priority 17 | Concurrent 18 | //PerPriorityConcurrent runs every actions concurrently regarding every priority. The priority processing is sequential. 19 | PerPriorityConcurrent 20 | ) 21 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | import "sort" 4 | 5 | //sortIntMap converts a int map of any type to a reverse-ordered slice of the same elements. 6 | func sortIntMap[T any](items map[int]T) []T { 7 | listOfKeys := []int{} 8 | 9 | for k, _ := range items { 10 | listOfKeys = append(listOfKeys, k) 11 | } 12 | sort.Sort(sort.Reverse(sort.IntSlice(listOfKeys))) 13 | 14 | returnValue := []T{} 15 | 16 | for _, val := range listOfKeys { 17 | returnValue = append(returnValue, items[val]) 18 | } 19 | return returnValue 20 | } 21 | -------------------------------------------------------------------------------- /utils_test.go: -------------------------------------------------------------------------------- 1 | package gobserve 2 | 3 | import "testing" 4 | 5 | func TestSortIntMap(t *testing.T) { 6 | input := map[int]int{0: 0, 10: 10, 7: 7, 8: 8} 7 | expectedOutput := []int{10, 8, 7, 0} 8 | 9 | output := sortIntMap(input) 10 | 11 | if len(expectedOutput) != len(output) { 12 | t.Errorf("Expected String(%v) is not same as"+ 13 | " actual string (%v)", expectedOutput, output) 14 | } 15 | for i := 0; i < len(output); i++ { 16 | 17 | if expectedOutput[i] != output[i] { 18 | t.Errorf("Expected slice (%v) is not same as"+ 19 | " actual slice (%v)", expectedOutput, output) 20 | } 21 | } 22 | 23 | } 24 | --------------------------------------------------------------------------------