mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
improve eventbus
This commit is contained in:
parent
136c469ef7
commit
43ba6766ba
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
@ -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))
|
||||
}
|
Loading…
Reference in New Issue
Block a user