1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

eventbus: add generic support for event name (#18805)

This commit is contained in:
kbkpbot 2023-07-08 03:33:57 +08:00 committed by GitHub
parent 97a726b188
commit b3a6b73306
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 59 deletions

View File

@ -3,7 +3,7 @@ module some_module
import eventbus import eventbus
const ( const (
eb = eventbus.new() eb = eventbus.new[string]()
) )
pub struct Duration { pub struct Duration {
@ -29,6 +29,6 @@ pub fn do_work() {
some_module.eb.publish('event_baz', &Duration{42}, &EventMetadata{'Additional data at the end.'}) some_module.eb.publish('event_baz', &Duration{42}, &EventMetadata{'Additional data at the end.'})
} }
pub fn get_subscriber() eventbus.Subscriber { pub fn get_subscriber() eventbus.Subscriber[string] {
return *some_module.eb.subscriber return *some_module.eb.subscriber
} }

View File

@ -4,26 +4,27 @@ A module to provide eventing capabilities using pub/sub.
## API ## API
1. `new()` - create a new `EventBus` 1. `new[T]()` - create a new `EventBus`
2. `EventBus.new[T]()` - create a new `EventBus`
### Structs: ### Structs:
**EventBus:** **EventBus:**
1. `publish(name string, sender voidptr, args voidptr)` - publish an event with provided 1. `publish(name T, sender voidptr, args voidptr)` - publish an event with provided
Params & name Params & name
2. `clear_all()` - clear all subscribers 2. `clear_all()` - clear all subscribers
3. `has_subscriber(name string)` - check if a subscriber to an event exists 3. `has_subscriber(name T)` - check if a subscriber to an event exists
**Subscriber:** **Subscriber:**
1. `subscribe(name string, handler EventHandlerFn)` - subscribe to an event 1. `subscribe(name T, handler EventHandlerFn)` - subscribe to an event
2. `subscribe_once(name string, handler EventHandlerFn)` - subscribe only once to an event 2. `subscribe_once(name T, handler EventHandlerFn)` - subscribe only once to an event
3. `subscribe_method(name string, handler EventHandlerFn, receiver voidptr)` - subscribe to 3. `subscribe_method(name T, handler EventHandlerFn, receiver voidptr)` - subscribe to
an event and also set the `receiver` as a parameter. an event and also set the `receiver` as a parameter.
Since it's not yet possible to send methods as parameters, this is a workaround. Since it's not yet possible to send methods as parameters, this is a workaround.
4. `is_subscribed(name string)` - check if we are subscribed to an event 4. `is_subscribed(name T)` - check if we are subscribed to an event
5. `unsubscribe(name string)` - unsubscribe from an event 5. `unsubscribe(name T)` - unsubscribe from an event
**Event Handler Signature:** **Event Handler Signature:**
@ -63,7 +64,7 @@ import eventbus
// initialize it globally // initialize it globally
const ( const (
eb = eventbus.new() eb = eventbus.new[string]()
) )
fn main() { fn main() {
@ -88,7 +89,7 @@ module main
import eventbus import eventbus
const eb = eventbus.new() const eb = eventbus.new[string]()
struct Work { struct Work {
hours int hours int

View File

@ -2,59 +2,70 @@ module eventbus
pub type EventHandlerFn = fn (receiver voidptr, args voidptr, sender voidptr) pub type EventHandlerFn = fn (receiver voidptr, args voidptr, sender voidptr)
pub struct Publisher { pub struct Publisher[T] {
mut: mut:
registry &Registry = unsafe { nil } registry &Registry[T] = unsafe { nil }
} }
pub struct Subscriber { pub struct Subscriber[T] {
mut: mut:
registry &Registry = unsafe { nil } registry &Registry[T] = unsafe { nil }
} }
struct Registry { struct Registry[T] {
mut: mut:
events []EventHandler events []EventHandler[T]
} }
struct EventHandler { struct EventHandler[T] {
name string name T
handler EventHandlerFn handler EventHandlerFn
receiver voidptr = unsafe { nil } receiver voidptr = unsafe { nil }
once bool once bool
} }
pub struct EventBus { pub struct EventBus[T] {
pub mut: pub mut:
registry &Registry = unsafe { nil } registry &Registry[T] = unsafe { nil }
publisher &Publisher = unsafe { nil } publisher &Publisher[T] = unsafe { nil }
subscriber &Subscriber = unsafe { nil } subscriber &Subscriber[T] = unsafe { nil }
} }
pub fn new() &EventBus { // EventBus.new[T] create a new eventbus with event type T.
registry := &Registry{ pub fn EventBus.new[T]() &EventBus[T] {
registry := &Registry[T]{
events: [] events: []
} }
return &EventBus{registry, &Publisher{registry}, &Subscriber{registry}} return &EventBus[T]{registry, &Publisher[T]{registry}, &Subscriber[T]{registry}}
} }
// EventBus Methods // new[T] create a new eventbus with event type T.
pub fn (eb &EventBus) publish(name string, sender voidptr, args voidptr) { pub fn new[T]() &EventBus[T] {
registry := &Registry[T]{
events: []
}
return &EventBus[T]{registry, &Publisher[T]{registry}, &Subscriber[T]{registry}}
}
// publish publish an event with provided Params & name.
pub fn (eb &EventBus[T]) publish(name T, sender voidptr, args voidptr) {
mut publisher := eb.publisher mut publisher := eb.publisher
publisher.publish(name, sender, args) publisher.publish(name, sender, args)
} }
pub fn (eb &EventBus) clear_all() { // clear_all clear all subscribers.
pub fn (eb &EventBus[T]) clear_all() {
mut publisher := eb.publisher mut publisher := eb.publisher
publisher.clear_all() publisher.clear_all()
} }
pub fn (eb &EventBus) has_subscriber(name string) bool { // has_subscriber check if a subscriber to an event exists.
pub fn (eb &EventBus[T]) has_subscriber(name T) bool {
return eb.registry.check_subscriber(name) return eb.registry.check_subscriber(name)
} }
// Publisher Methods // publish publish an event with provided Params & name.
fn (mut pb Publisher) publish(name string, sender voidptr, args voidptr) { fn (mut pb Publisher[T]) publish(name T, sender voidptr, args voidptr) {
for event in pb.registry.events { for event in pb.registry.events {
if event.name == name { if event.name == name {
event.handler(event.receiver, args, sender) event.handler(event.receiver, args, sender)
@ -63,59 +74,64 @@ fn (mut pb Publisher) publish(name string, sender voidptr, args voidptr) {
pb.registry.events = pb.registry.events.filter(!(it.name == name && it.once)) pb.registry.events = pb.registry.events.filter(!(it.name == name && it.once))
} }
fn (mut p Publisher) clear_all() { // clear_all clear all subscribers.
fn (mut p Publisher[T]) clear_all() {
p.registry.events.clear() p.registry.events.clear()
} }
// Subscriber Methods // subscribe subscribe to an event `name`.
pub fn (mut s Subscriber) subscribe(name string, handler EventHandlerFn) { pub fn (mut s Subscriber[T]) subscribe(name T, handler EventHandlerFn) {
s.registry.events << EventHandler{ s.registry.events << EventHandler[T]{
name: name name: name
handler: handler handler: handler
} }
} }
pub fn (mut s Subscriber) subscribe_method(name string, handler EventHandlerFn, receiver voidptr) { // subscribe_method subscribe to an event `name` and also set the `receiver` as a parameter.
s.registry.events << EventHandler{ pub fn (mut s Subscriber[T]) subscribe_method(name T, handler EventHandlerFn, receiver voidptr) {
s.registry.events << EventHandler[T]{
name: name name: name
handler: handler handler: handler
receiver: receiver receiver: receiver
} }
} }
// unsubscribe_method unsubscribe a receiver for only one method // unsubscribe_method unsubscribe a receiver for only one method.
pub fn (mut s Subscriber) unsubscribe_method(name string, receiver voidptr) { pub fn (mut s Subscriber[T]) unsubscribe_method(name T, receiver voidptr) {
s.registry.events = s.registry.events.filter(!(it.name == name && it.receiver == receiver)) s.registry.events = s.registry.events.filter(!(it.name == name && it.receiver == receiver))
} }
// unsubscribe_receiver unsubscribes a receiver from all events // unsubscribe_receiver unsubscribes a receiver from all events.
pub fn (mut s Subscriber) unsubscribe_receiver(receiver voidptr) { pub fn (mut s Subscriber[T]) unsubscribe_receiver(receiver voidptr) {
s.registry.events = s.registry.events.filter(it.receiver != receiver) s.registry.events = s.registry.events.filter(it.receiver != receiver)
} }
pub fn (mut s Subscriber) subscribe_once(name string, handler EventHandlerFn) { // subscribe_once subscribe only once to an event `name`.
s.registry.events << EventHandler{ pub fn (mut s Subscriber[T]) subscribe_once(name T, handler EventHandlerFn) {
s.registry.events << EventHandler[T]{
name: name name: name
handler: handler handler: handler
once: true once: true
} }
} }
pub fn (s &Subscriber) is_subscribed(name string) bool { // is_subscribed check if we are subscribed to an event `name`.
pub fn (s &Subscriber[T]) is_subscribed(name T) bool {
return s.registry.check_subscriber(name) return s.registry.check_subscriber(name)
} }
// is_subscribed_method checks whether a receiver was already subscribed for any events // is_subscribed_method checks whether a receiver was already subscribed for any events.
pub fn (s &Subscriber) is_subscribed_method(name string, receiver voidptr) bool { pub fn (s &Subscriber[T]) is_subscribed_method(name T, receiver voidptr) bool {
return s.registry.events.any(it.name == name && it.receiver == receiver) return s.registry.events.any(it.name == name && it.receiver == receiver)
} }
pub fn (mut s Subscriber) unsubscribe(name string, handler EventHandlerFn) { // unsubscribe unsubscribe from an event `name`.
pub fn (mut s Subscriber[T]) unsubscribe(name T, handler EventHandlerFn) {
// v := voidptr(handler) // v := voidptr(handler)
s.registry.events = s.registry.events.filter(!(it.name == name && it.handler == handler)) s.registry.events = s.registry.events.filter(!(it.name == name && it.handler == handler))
} }
// Registry Methods // Registry Methods
fn (r &Registry) check_subscriber(name string) bool { fn (r &Registry[T]) check_subscriber(name T) bool {
return r.events.any(it.name == name) return r.events.any(it.name == name)
} }

View File

@ -8,11 +8,12 @@ struct FakeReceiver {
ok bool ok bool
} }
fn test_eventbus() { fn test_eventbus_string() {
ev_data := &EventData{'hello'} ev_data := &EventData{'hello'}
mut eb := eventbus.new() mut eb := eventbus.new[string]()
eb.subscriber.subscribe_once('on_test', on_test) eb.subscriber.subscribe_once('on_test', on_test)
assert eb.has_subscriber('on_test') assert eb.has_subscriber('on_test')
assert !eb.has_subscriber('not_exist')
assert eb.subscriber.is_subscribed('on_test') assert eb.subscriber.is_subscribed('on_test')
eb.publish('on_test', eb, ev_data) eb.publish('on_test', eb, ev_data)
assert !eb.has_subscriber('on_test') assert !eb.has_subscriber('on_test')
@ -25,10 +26,52 @@ fn test_eventbus() {
assert !eb.subscriber.is_subscribed('on_test') assert !eb.subscriber.is_subscribed('on_test')
} }
enum Events {
event_1
event_2
event_3
}
fn test_eventbus_enum() {
ev_data := &EventData{'hello'}
mut eb := eventbus.EventBus.new[Events]()
eb.subscriber.subscribe_once(Events.event_1, on_test)
assert eb.has_subscriber(Events.event_1)
assert !eb.has_subscriber(Events.event_2)
assert eb.subscriber.is_subscribed(Events.event_1)
eb.publish(Events.event_1, eb, ev_data)
assert !eb.has_subscriber(Events.event_1)
assert !eb.subscriber.is_subscribed(Events.event_1)
eb.subscriber.subscribe(Events.event_1, on_test)
assert eb.has_subscriber(Events.event_1)
assert eb.subscriber.is_subscribed(Events.event_1)
eb.clear_all()
assert !eb.has_subscriber(Events.event_1)
assert !eb.subscriber.is_subscribed(Events.event_1)
}
fn test_eventbus_int() {
ev_data := &EventData{'hello'}
mut eb := eventbus.EventBus.new[int]()
eb.subscriber.subscribe_once(9999, on_test)
assert eb.has_subscriber(9999)
assert !eb.has_subscriber(1111)
assert eb.subscriber.is_subscribed(9999)
eb.publish(9999, eb, ev_data)
assert !eb.has_subscriber(9999)
assert !eb.subscriber.is_subscribed(9999)
eb.subscriber.subscribe(9999, on_test)
assert eb.has_subscriber(9999)
assert eb.subscriber.is_subscribed(9999)
eb.clear_all()
assert !eb.has_subscriber(9999)
assert !eb.subscriber.is_subscribed(9999)
}
fn test_subscribe_method() { fn test_subscribe_method() {
// Does not really test subscribe_method idinvidually though // Does not really test subscribe_method idinvidually though
// given // given
mut eb := eventbus.new() mut eb := eventbus.new[string]()
r := FakeReceiver{} r := FakeReceiver{}
assert !eb.subscriber.is_subscribed_method('on_test_with_receiver', r) assert !eb.subscriber.is_subscribed_method('on_test_with_receiver', r)
@ -41,7 +84,7 @@ fn test_subscribe_method() {
fn test_unsubscribe_method() { fn test_unsubscribe_method() {
// given // given
mut eb := eventbus.new() mut eb := eventbus.new[string]()
r := FakeReceiver{} r := FakeReceiver{}
r2 := FakeReceiver{} r2 := FakeReceiver{}
@ -58,7 +101,7 @@ fn test_unsubscribe_method() {
fn test_publish() { fn test_publish() {
// given // given
ev_data := &EventData{'hello'} ev_data := &EventData{'hello'}
mut eb := eventbus.new() mut eb := eventbus.new[string]()
// when // when
eb.subscriber.subscribe_once('on_test', on_test) eb.subscriber.subscribe_once('on_test', on_test)
@ -71,7 +114,7 @@ fn test_publish() {
fn test_publish_with_receiver() { fn test_publish_with_receiver() {
// given // given
mut eb := eventbus.new() mut eb := eventbus.new[string]()
ev_data := &EventData{'hello'} ev_data := &EventData{'hello'}
r := FakeReceiver{} r := FakeReceiver{}
@ -85,7 +128,7 @@ fn test_publish_with_receiver() {
fn test_unsubscribe_reveiver() { fn test_unsubscribe_reveiver() {
// given // given
mut eb := eventbus.new() mut eb := eventbus.new[string]()
r := &FakeReceiver{} r := &FakeReceiver{}
// when // when

View File

@ -5,7 +5,7 @@ struct MyMessage {
} }
fn test_fn_call_with_nonpointer_rvalue() { fn test_fn_call_with_nonpointer_rvalue() {
eb := eventbus.new() eb := eventbus.new[string]()
mut subscriber := eb.subscriber mut subscriber := eb.subscriber
subscriber.subscribe('my_publish', subscriber_method) subscriber.subscribe('my_publish', subscriber_method)
@ -18,6 +18,6 @@ fn subscriber_method(receiver voidptr, ev &MyMessage, sender voidptr) {
println(ev) println(ev)
} }
fn do_something(eb &eventbus.EventBus) { fn do_something[T](eb &eventbus.EventBus[T]) {
eb.publish('my_publish', eb, MyMessage{ msg: 'this is my message' }) eb.publish('my_publish', eb, MyMessage{ msg: 'this is my message' })
} }