mirror of
https://github.com/NUTtech/bell.git
synced 2025-09-16 08:56:27 +02:00
Init commit
This commit is contained in:
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal 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
|
121
event.go
Normal file
121
event.go
Normal 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
102
event_test.go
Normal 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
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module events
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/stretchr/testify v1.7.0
|
11
go.sum
Normal file
11
go.sum
Normal 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=
|
Reference in New Issue
Block a user