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

improve eventbus

This commit is contained in:
Abdullah Atta 2020-01-22 21:41:08 +05:00 committed by Alexander Medvednikov
parent 136c469ef7
commit 43ba6766ba
6 changed files with 115 additions and 274 deletions

View File

@ -2,7 +2,6 @@ module main
import (
some_module
eventbus
)
fn main(){
@ -11,8 +10,6 @@ fn main(){
some_module.do_work()
}
fn on_error(sender voidptr, p eventbus.Params) {
work := *(*some_module.Work(sender))
println(work.hours)
println(p.get_string("error"))
fn on_error(sender voidptr, e &some_module.Error) {
println(e.message)
}

View File

@ -13,19 +13,22 @@ pub struct Work {
hours int
}
pub struct Error {
pub:
message string
}
pub fn do_work(){
work := Work{20}
mut params := eventbus.Params{}
for i in 0..20 {
println("working...")
if i == 15 {
params.put_string("error", "CRASH!!")
eb.publish("error", work, params)
eb.publish("error", work, params)
error := &Error{"There was an error."}
eb.publish("error", work, error)
eb.publish("error", work, error)
return
}
}
}
pub fn get_subscriber() eventbus.Subscriber {

View File

@ -10,27 +10,36 @@ A module to provide eventing capabilities using pub/sub.
**EventBus:**
1. `publish(string, voidptr, Params)` - publish an event with provided Params & name
1. `publish(name string, sender voidptr, args voidptr)` - publish an event with provided Params & name
2. `clear_all()` - clear all subscribers
3. `has_subscriber(string)` - check if a subscriber to an event exists
3. `has_subscriber(name string)` - check if a subscriber to an event exists
**Subscriber:**
1. `subscribe(string, fn(voidptr, Params))` - subscribe to an event
2. `subscribe_once(string, fn(voidptr, Params))` - subscribe only once to an event
3. `is_subscribed(string)` - check if we are subscribed to an event
4. `unsubscribe(string)` - unsubscribe from an event
1. `subscribe(name string, handler EventHandlerFn)` - subscribe to an event
2. `subscribe_once(name string, handler EventHandlerFn)` - subscribe only once to an event
3. `subscribe_method(name string, handler EventHandlerFn, reciever voidptr)` - subscribe to an event and also recieve the `reciever` as a parameter. Since it's not yet possible to send methods as parameters, this is the workaround.
4. `is_subscribed(name string)` - check if we are subscribed to an event
5. `unsubscribe(name string)` - unsubscribe from an event
**Event Handler Signature:**
The function given to `subscribe` and `subscribe_once` must match this:
The function given to `subscribe`, `subscribe_method` and `subscribe_once` must match this:
```v
fn(voidptr, Params){
fn(voidptr, voidptr, voidptr){
}
// Example
fn onPress(sender voidptr, p Params){
// Since V can map structs to voidptr, this also works
struct ClickEvent {
x int
y int
}
// Example case where publisher sends ClickEvent as args.
fn onPress(sender voidptr, e &ClickEvent){
println(e.x)
//your code here...
}
```
@ -62,13 +71,8 @@ fn main(){
}
// the event handler
fn on_error(sender voidptr, p eventbus.Params) {
//cast the sender to the real type
//you can also make this mutable if required.
work := *(*Work(sender)) //a little verbose but works
error := p.get_string("error")
println('error occured on ${work.hours}. Error: ${error}')
fn on_error(work &Work, e &Error) {
println('error occured on ${work.hours}. Error: ${e.message}')
}
```
@ -77,55 +81,23 @@ fn on_error(sender voidptr, p eventbus.Params) {
```v
module main
import (
eventbus
)
struct Work{
hours int
}
struct Error {
message string
}
fn do_work(){
work := Work{20}
// get a mutable Params instance & put some data into it
mut params := eventbus.Params{}
params.put_string("error", "Error: no internet connection.")
error := &Error{"Error: no internet connection."}
// publish the event
eb.publish("error", work, params)
eb.publish("error", work, error)
}
```
### How to use `Params`:
```v
mut params := eventbus.Params{}
params.put_string("string", "some_string")
params.put_int("int", 20)
params.put_bool("bool", true)
// add maps and arrays of any type like this
arr := [1,2,3]
params.put_array("array", arr)
mp := {"hello": "world"}
params.put_map("map", mp)
//get and use the params like this
assert params.get_string("string") == "some_string"
assert params.get_int("int") == 20
assert params.get_bool("bool") == true
g_arr := params.get_array("array", 0)
assert g_arr[0] == 1
g_m := params.get_map("map", "")
assert g_m["hello"] == "world"
```
#### Caution when putting arrays:
Currently putting arrays and maps directly as parameters in `put_array` doesn't work, so make a variable first and use that.
### Notes:
1. Each `EventBus` instance has it's own registry (i.e. there is no global event registry so you can't just subscribe to an event wherever you are.

View File

@ -1,55 +1,55 @@
module eventbus
pub type EventHandlerFn fn(voidptr, voidptr, voidptr)
pub struct Publisher {
mut:
mut:
registry &Registry
}
pub struct Subscriber {
mut:
mut:
registry &Registry
}
struct Registry{
mut:
names []string
events []voidptr
once []string
struct Registry {
mut:
events []EventHandler
}
struct EventHandler {
func fn(voidptr, Params)
name string
handler EventHandlerFn
receiver voidptr
once bool
}
pub struct EventBus{
pub mut:
registry &Registry
publisher &Publisher
pub:
pub struct EventBus {
pub mut:
registry &Registry
publisher &Publisher
pub:
subscriber &Subscriber
}
pub fn new() &EventBus{
pub fn new() &EventBus {
registry := &Registry{
names: []
events: []
once: []
}
return &EventBus{
registry,
&Publisher{registry},
&Subscriber{registry}
registry,&Publisher{
registry},&Subscriber{
registry}
}
}
// EventBus Methods
pub fn (eb &EventBus) publish(name string, sender voidptr, p Params) {
pub fn (eb &EventBus) publish(name string, sender voidptr, args voidptr) {
mut publisher := eb.publisher
publisher.publish(name, sender, p)
publisher.publish(name, sender, args)
}
pub fn (eb &EventBus) clear_all(){
pub fn (eb &EventBus) clear_all() {
mut publisher := eb.publisher
publisher.clear_all()
}
@ -59,80 +59,77 @@ pub fn (eb &EventBus) has_subscriber(name string) bool {
}
// Publisher Methods
fn (pb mut Publisher) publish(name string, sender voidptr, p Params){
//p.put_custom("sender", "any", sender) //add sender to params
for i, n in pb.registry.names {
if name == n {
eh := pb.registry.events[i]
once_index := pb.registry.once.index(pb.registry.names[i])
if once_index > -1 {
fn (pb mut Publisher) publish(name string, sender voidptr, args voidptr) {
for i, event in pb.registry.events {
if event.name == name {
if event.once {
pb.registry.events.delete(i)
pb.registry.names.delete(i)
pb.registry.once.delete(once_index)
}
invoke(eh, sender, p)
if event.receiver != voidptr(0) {
event.handler(event.receiver, args, sender)
} else {
event.handler(sender, args, voidptr(0))
}
}
}
}
fn (p mut Publisher) clear_all(){
if p.registry.names.len == 0 {return}
for i := p.registry.names.len - 1; i >= 0; i-- {
p.registry.delete_entry(i)
fn (p mut Publisher) clear_all() {
if p.registry.events.len == 0 {
return
}
for i := p.registry.events.len - 1; i >= 0; i-- {
p.registry.events.delete(i)
}
}
// Subscriber Methods
pub fn (s mut Subscriber) subscribe(name string, handler fn(voidptr, Params)){
s.registry.names << name
v := voidptr(handler)
s.registry.events << v
pub fn (s mut Subscriber) subscribe(name string, handler EventHandlerFn) {
s.registry.events << EventHandler {
name: name
handler: handler
receiver: voidptr(0)
}
}
pub fn (s mut Subscriber) subscribe_once(name string, handler fn(voidptr, Params)){
s.subscribe(name, handler)
s.registry.once << name
pub fn (s mut Subscriber) subscribe_method(name string, handler EventHandlerFn, receiver voidptr) {
s.registry.events << EventHandler {
name: name
handler: handler
receiver: receiver
}
}
pub fn (s mut Subscriber) subscribe_once(name string, handler EventHandlerFn) {
s.registry.events << EventHandler {
name: name
handler: handler
receiver: voidptr(0)
once: true
}
}
pub fn (s &Subscriber) is_subscribed(name string) bool {
return s.registry.check_subscriber(name)
}
pub fn (s mut Subscriber) unsubscribe(name string, handler fn(voidptr, Params)){
v := voidptr(handler)
for i, n in s.registry.names {
if name == n {
eh := s.registry.events[i]
if eh == v {
s.registry.delete_entry(i)
pub fn (s mut Subscriber) unsubscribe(name string, handler EventHandlerFn) {
// v := voidptr(handler)
for i, event in s.registry.events {
if event.name == name {
if event.handler == handler {
s.registry.events.delete(i)
}
}
}
}
// Registry Methods
fn (r &Registry) check_subscriber(name string) bool {
for n in r.names {
if name == n {return true}
for event in r.events {
if event.name == name {
return true
}
}
return false
}
fn (r mut Registry) delete_entry(index int) {
once_index := r.once.index(r.names[index])
if once_index > -1 {
r.once.delete(once_index)
}
r.events.delete(index)
r.names.delete(index)
}
// Helper Functions
fn invoke(p, sender voidptr, arr Params){
handler := EventHandler{p}.func
handler(sender, arr)
}

View File

@ -2,17 +2,19 @@ import (
eventbus
)
struct EventData {
data string
}
fn test_eventbus(){
ev_data := &EventData{'hello'}
mut eb := eventbus.new()
eb.subscriber.subscribe_once("on_test", on_test)
assert eb.has_subscriber("on_test")
assert eb.subscriber.is_subscribed("on_test")
mut params := eventbus.Params{}
params.put_string("eventbus", "vevent")
eb.publish("on_test", eb, params)
eb.publish("on_test", eb, ev_data)
assert !eb.has_subscriber("on_test")
assert !eb.subscriber.is_subscribed("on_test")
@ -28,37 +30,6 @@ fn test_eventbus(){
assert !eb.subscriber.is_subscribed("on_test")
}
fn test_params(){
mut params := eventbus.Params{}
params.put_string("string", "some_string")
params.put_int("int", 20)
params.put_bool("bool", true)
arr := [1,2,3]
params.put_array("array", arr)
mp := {"hello": "world"}
params.put_map("map", mp)
assert params.get_string("string") == "some_string"
assert params.get_int("int") == 20
assert params.get_bool("bool") == true
g_arr := params.get_array("array", 0)
assert g_arr[0] == 1
g_m := params.get_map("map", "")
assert g_m["hello"] == "world"
}
fn on_test(sender voidptr, p eventbus.Params) {
mut eb := *(*eventbus.EventBus(sender))
eb.subscriber.subscribe("on_test_2", on_test_2)
eb.clear_all()
assert !eb.has_subscriber("on_test_2")
assert !eb.subscriber.is_subscribed("on_test_2")
assert p.get_string("eventbus") == "vevent"
}
fn on_test_2(sender voidptr, p eventbus.Params){}
fn on_test(sender voidptr, ev &EventData) {
assert ev.data == "hello"
}

View File

@ -1,99 +0,0 @@
module eventbus
/*
NOTE: All these non-generic methods are temporary until
V has a properly functioning generic system
*/
pub struct Params {
mut:
params []Param
}
struct Param{
typ string
name string
value voidptr
}
pub fn (p Params) get_string(name string) string {
param, is_type := p.get_param(name, "string")
return if is_type {string(byteptr(param.value))}else{""}
}
pub fn (p Params) get_int(name string) int {
param, is_type := p.get_param(name, "num")
return if is_type {int(param.value)}else{0}
}
pub fn (p Params) get_bool(name string) bool {
param, is_type := p.get_param(name, "bool")
return if is_type {int(param.value) == 1}else{false}
}
pub fn (p Params) get_array<T>(name string, def T) []T {
param, is_type := p.get_param(name, "array")
if is_type {
val := param.value
return unmarshall_array(def, val)
} else {
return []
}
}
pub fn (p Params) get_map<T>(name string, def T) map[string]T {
param, is_type := p.get_param(name, "map")
if is_type {
val := param.value
return unmarshall_map(def, val)
} else {
return map[string]T
}
}
pub fn (p Params) get_raw(name string) voidptr {
param, _ := p.get_param(name, "")
return param.value
}
pub fn (p mut Params) put_map(name string, value voidptr) {
p.put_custom(name, "map", value)
}
pub fn (p mut Params) put_array(name string, arr voidptr) {
p.put_custom(name, "array", arr)
}
pub fn (p mut Params) put_int(name string, num int) {
p.put_custom(name, "num", num)
}
pub fn (p mut Params) put_string(name string, s string) {
p.put_custom(name, "string", s.str)
}
pub fn (p mut Params) put_bool(name string, val bool) {
p.put_custom(name, "bool", if val { 1 } else { 0 })
}
pub fn (p mut Params) put_custom(name, typ string, data voidptr) {
p.params << Param {typ, name, data}
}
//HELPERS
fn (p Params) get_param(name string, typ string) (Param, bool) {
for param in p.params {
if param.name == name {
return param, param.typ == typ
}
}
return Param{value: voidptr(0)}, false
}
fn unmarshall_array<T> (s T, m voidptr) array_T {
return *(*array_T(m))
}
fn unmarshall_map<T> (s T, m voidptr) map_T {
return *(*map_T(m))
}