1
0
mirror of https://github.com/NUTtech/bell.git synced 2025-09-16 08:56:27 +02:00

Init commit

This commit is contained in:
Litvinenko Andrey
2021-10-07 09:53:23 +03:00
commit 2d5f3deb96
6 changed files with 253 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
.idea/
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

1
README.md Normal file
View File

@@ -0,0 +1 @@
# Events

121
event.go Normal file
View File

@@ -0,0 +1,121 @@
// Package event реализует простую систему событиый
//
// На каждое событие (event) можно добавить несколько обработчиков (handlerFunc)
// Вызов обработчиков события происходит в отдельной горутине через установленный канал
// При вызове определенного события происходит последовательная передача сообщения (Message)
// каждому обработчику события.
//
// Если канал закрывается, горутина для этого события прекращает свою работу.
//
// Пример использования:
// On("event_name", func(message Message) { fmt.PrintLn(message) }) - добавляем обработчик события event_name
// Call("event_name", "some_data") - Вызоваем события "event_name", тем самым запуская обработчики установленные ранее
package event
import (
"fmt"
"sync"
"time"
)
// Хранилище состояний обработчиков событий
var eventMap = &events{channels: map[string][]chan Message{}}
// Message Сообщение которое передается в обработчик события
type Message struct {
Event string
Timestamp time.Time
Value interface{}
}
type events struct {
sync.RWMutex
channels map[string][]chan Message
}
// On Добавление обработчика события
// event - название/код события
// handlerFunc - функция-обработчик события. На вход принимает структуру Message
func On(event string, handlerFunc func(message Message)) {
eventMap.Lock()
defer eventMap.Unlock()
channel := make(chan Message)
go func(c chan Message) {
for {
message, ok := <-c
if !ok {
break
}
handlerFunc(message)
}
}(channel)
eventMap.channels[event] = append(eventMap.channels[event], channel)
}
// Call Вызывает событие
// event - название/код события
// value - данные, которые будут переданы в функции-обработчики события внутри Message
func Call(event string, value interface{}) error {
eventMap.RLock()
defer eventMap.RUnlock()
if _, ok := eventMap.channels[event]; !ok {
return fmt.Errorf("channel %s not found", event)
}
for _, c := range eventMap.channels[event] {
c <- Message{Event: event, Timestamp: time.Now(), Value: value}
}
return nil
}
// Has Возвращает true если существуют обработчики переданного события
func Has(event string) bool {
eventMap.RLock()
defer eventMap.RUnlock()
_, ok := eventMap.channels[event]
return ok
}
// List Возвращает список событий, на которые установлены обработчики
func List() []string {
eventMap.RLock()
defer eventMap.RUnlock()
var list []string
for event := range eventMap.channels {
list = append(list, event)
}
return list
}
// Remove Удаляет обработчики события или событий
// При удалении обработчиков закрываются каналы и прекращают работу горутины
//
// Если вызвать функцию без параметра names - будут удалены все обработчики всех событий
func Remove(names ...string) {
eventMap.Lock()
defer eventMap.Unlock()
if len(names) == 0 {
keys := make([]string, 0, len(eventMap.channels))
for k := range eventMap.channels {
keys = append(keys, k)
}
names = keys
}
for _, name := range names {
for _, channel := range eventMap.channels[name] {
close(channel)
}
delete(eventMap.channels, name)
}
}

102
event_test.go Normal file
View File

@@ -0,0 +1,102 @@
package event
import (
"github.com/stretchr/testify/assert"
"sort"
"testing"
)
// resetSystem Очистка хранилища состояний обработчиков событий
func resetSystem() {
for k := range eventMap.channels {
for _, channel := range eventMap.channels[k] {
close(channel)
}
}
eventMap = &events{channels: map[string][]chan Message{}}
}
// TestOn Проверка работы функции добавления обработчика событий
func TestOn(t *testing.T) {
resetSystem()
defer resetSystem()
expMessageEvent := "test_event"
expMessageValue := "value"
On(expMessageEvent, func(message Message) {
assert.Equal(t, expMessageEvent, message.Event)
assert.Equal(t, expMessageValue, message.Value)
})
assert.Equal(t, 1, len(eventMap.channels))
assert.Equal(t, 1, len(eventMap.channels[expMessageEvent]))
assert.NotPanics(t, func() {
err := Call(expMessageEvent, expMessageValue)
assert.NoError(t, err)
})
assert.NotPanics(t, func() {
resetSystem()
})
}
// TestCall_Fail Проверка корректности ошибки при ошибочном вызове события
func TestCall_Fail(t *testing.T) {
resetSystem()
defer resetSystem()
err := Call("undefined_event", func() {})
assert.EqualError(t, err, "channel undefined_event not found")
}
// TestRemove проверка удаления обработчиков события из хранилища
func TestRemove(t *testing.T) {
resetSystem()
defer resetSystem()
eventMap.channels["test"] = append(eventMap.channels["test"], make(chan Message), make(chan Message))
eventMap.channels["test2"] = append(eventMap.channels["test2"], make(chan Message))
Remove("test")
assert.Equal(t, 1, len(eventMap.channels))
eventMap.channels["test3"] = append(eventMap.channels["test3"], make(chan Message))
eventMap.channels["test4"] = append(eventMap.channels["test4"], make(chan Message))
Remove("test2")
assert.Equal(t, 2, len(eventMap.channels))
eventMap.channels["test3"] = append(eventMap.channels["test3"], make(chan Message))
eventMap.channels["test4"] = append(eventMap.channels["test4"], make(chan Message))
Remove()
assert.Equal(t, 0, len(eventMap.channels))
}
// TestHas Проверка корректности определения существования обработчиков события
func TestHas(t *testing.T) {
resetSystem()
defer resetSystem()
assert.False(t, Has("test"))
eventMap.channels["test"] = append(eventMap.channels["test"], make(chan Message))
assert.True(t, Has("test"))
}
// TestList Проверка корректного получения списка событий, на которые установлены обработчики
func TestList(t *testing.T) {
resetSystem()
defer resetSystem()
assert.Empty(t, List())
eventMap.channels["test"] = append(eventMap.channels["test"], make(chan Message), make(chan Message))
eventMap.channels["test2"] = append(eventMap.channels["test2"], make(chan Message))
actualList := List()
sort.Strings(actualList)
assert.Equal(t, 2, len(actualList))
assert.Equal(t, []string{"test", "test2"}, actualList)
}

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module events
go 1.16
require github.com/stretchr/testify v1.7.0

11
go.sum Normal file
View File

@@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=