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 (
|
import (
|
||||||
some_module
|
some_module
|
||||||
eventbus
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fn main(){
|
fn main(){
|
||||||
@ -11,8 +10,6 @@ fn main(){
|
|||||||
some_module.do_work()
|
some_module.do_work()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_error(sender voidptr, p eventbus.Params) {
|
fn on_error(sender voidptr, e &some_module.Error) {
|
||||||
work := *(*some_module.Work(sender))
|
println(e.message)
|
||||||
println(work.hours)
|
|
||||||
println(p.get_string("error"))
|
|
||||||
}
|
}
|
||||||
|
@ -13,19 +13,22 @@ pub struct Work {
|
|||||||
hours int
|
hours int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Error {
|
||||||
|
pub:
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
pub fn do_work(){
|
pub fn do_work(){
|
||||||
work := Work{20}
|
work := Work{20}
|
||||||
mut params := eventbus.Params{}
|
|
||||||
for i in 0..20 {
|
for i in 0..20 {
|
||||||
println("working...")
|
println("working...")
|
||||||
if i == 15 {
|
if i == 15 {
|
||||||
params.put_string("error", "CRASH!!")
|
error := &Error{"There was an error."}
|
||||||
eb.publish("error", work, params)
|
eb.publish("error", work, error)
|
||||||
eb.publish("error", work, params)
|
eb.publish("error", work, error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_subscriber() eventbus.Subscriber {
|
pub fn get_subscriber() eventbus.Subscriber {
|
||||||
|
@ -10,27 +10,36 @@ A module to provide eventing capabilities using pub/sub.
|
|||||||
|
|
||||||
**EventBus:**
|
**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
|
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:**
|
**Subscriber:**
|
||||||
|
|
||||||
1. `subscribe(string, fn(voidptr, Params))` - subscribe to an event
|
1. `subscribe(name string, handler EventHandlerFn)` - subscribe to an event
|
||||||
2. `subscribe_once(string, fn(voidptr, Params))` - subscribe only once to an event
|
2. `subscribe_once(name string, handler EventHandlerFn)` - subscribe only once to an event
|
||||||
3. `is_subscribed(string)` - check if we are subscribed 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. `unsubscribe(string)` - unsubscribe from an event
|
4. `is_subscribed(name string)` - check if we are subscribed to an event
|
||||||
|
5. `unsubscribe(name string)` - unsubscribe from an event
|
||||||
|
|
||||||
**Event Handler Signature:**
|
**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
|
```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...
|
//your code here...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -62,13 +71,8 @@ fn main(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the event handler
|
// the event handler
|
||||||
fn on_error(sender voidptr, p eventbus.Params) {
|
fn on_error(work &Work, e &Error) {
|
||||||
//cast the sender to the real type
|
println('error occured on ${work.hours}. Error: ${e.message}')
|
||||||
//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}')
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -77,55 +81,23 @@ fn on_error(sender voidptr, p eventbus.Params) {
|
|||||||
```v
|
```v
|
||||||
module main
|
module main
|
||||||
|
|
||||||
import (
|
|
||||||
eventbus
|
|
||||||
)
|
|
||||||
|
|
||||||
struct Work{
|
struct Work{
|
||||||
hours int
|
hours int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
fn do_work(){
|
fn do_work(){
|
||||||
work := Work{20}
|
work := Work{20}
|
||||||
// get a mutable Params instance & put some data into it
|
// get a mutable Params instance & put some data into it
|
||||||
mut params := eventbus.Params{}
|
error := &Error{"Error: no internet connection."}
|
||||||
params.put_string("error", "Error: no internet connection.")
|
|
||||||
// publish the event
|
// 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:
|
### 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. 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
|
module eventbus
|
||||||
|
|
||||||
|
pub type EventHandlerFn fn(voidptr, voidptr, voidptr)
|
||||||
|
|
||||||
pub struct Publisher {
|
pub struct Publisher {
|
||||||
mut:
|
mut:
|
||||||
registry &Registry
|
registry &Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Subscriber {
|
pub struct Subscriber {
|
||||||
mut:
|
mut:
|
||||||
registry &Registry
|
registry &Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Registry{
|
struct Registry {
|
||||||
mut:
|
mut:
|
||||||
names []string
|
events []EventHandler
|
||||||
events []voidptr
|
|
||||||
once []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventHandler {
|
struct EventHandler {
|
||||||
func fn(voidptr, Params)
|
name string
|
||||||
|
handler EventHandlerFn
|
||||||
|
receiver voidptr
|
||||||
|
once bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventBus{
|
pub struct EventBus {
|
||||||
pub mut:
|
pub mut:
|
||||||
registry &Registry
|
registry &Registry
|
||||||
publisher &Publisher
|
publisher &Publisher
|
||||||
pub:
|
pub:
|
||||||
subscriber &Subscriber
|
subscriber &Subscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() &EventBus{
|
pub fn new() &EventBus {
|
||||||
registry := &Registry{
|
registry := &Registry{
|
||||||
names: []
|
|
||||||
events: []
|
events: []
|
||||||
once: []
|
|
||||||
}
|
}
|
||||||
return &EventBus{
|
return &EventBus{
|
||||||
registry,
|
registry,&Publisher{
|
||||||
&Publisher{registry},
|
registry},&Subscriber{
|
||||||
&Subscriber{registry}
|
registry}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventBus Methods
|
// EventBus Methods
|
||||||
|
pub fn (eb &EventBus) publish(name string, sender voidptr, args voidptr) {
|
||||||
pub fn (eb &EventBus) publish(name string, sender voidptr, p Params) {
|
|
||||||
mut publisher := eb.publisher
|
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
|
mut publisher := eb.publisher
|
||||||
publisher.clear_all()
|
publisher.clear_all()
|
||||||
}
|
}
|
||||||
@ -59,80 +59,77 @@ pub fn (eb &EventBus) has_subscriber(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publisher Methods
|
// Publisher Methods
|
||||||
|
fn (pb mut Publisher) publish(name string, sender voidptr, args voidptr) {
|
||||||
fn (pb mut Publisher) publish(name string, sender voidptr, p Params){
|
for i, event in pb.registry.events {
|
||||||
//p.put_custom("sender", "any", sender) //add sender to params
|
if event.name == name {
|
||||||
for i, n in pb.registry.names {
|
if event.once {
|
||||||
if name == n {
|
|
||||||
eh := pb.registry.events[i]
|
|
||||||
once_index := pb.registry.once.index(pb.registry.names[i])
|
|
||||||
if once_index > -1 {
|
|
||||||
pb.registry.events.delete(i)
|
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(){
|
fn (p mut Publisher) clear_all() {
|
||||||
if p.registry.names.len == 0 {return}
|
if p.registry.events.len == 0 {
|
||||||
for i := p.registry.names.len - 1; i >= 0; i-- {
|
return
|
||||||
p.registry.delete_entry(i)
|
}
|
||||||
|
for i := p.registry.events.len - 1; i >= 0; i-- {
|
||||||
|
p.registry.events.delete(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscriber Methods
|
// Subscriber Methods
|
||||||
|
pub fn (s mut Subscriber) subscribe(name string, handler EventHandlerFn) {
|
||||||
pub fn (s mut Subscriber) subscribe(name string, handler fn(voidptr, Params)){
|
s.registry.events << EventHandler {
|
||||||
s.registry.names << name
|
name: name
|
||||||
v := voidptr(handler)
|
handler: handler
|
||||||
s.registry.events << v
|
receiver: voidptr(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s mut Subscriber) subscribe_once(name string, handler fn(voidptr, Params)){
|
pub fn (s mut Subscriber) subscribe_method(name string, handler EventHandlerFn, receiver voidptr) {
|
||||||
s.subscribe(name, handler)
|
s.registry.events << EventHandler {
|
||||||
s.registry.once << name
|
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 {
|
pub fn (s &Subscriber) is_subscribed(name string) bool {
|
||||||
return s.registry.check_subscriber(name)
|
return s.registry.check_subscriber(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s mut Subscriber) unsubscribe(name string, handler fn(voidptr, Params)){
|
pub fn (s mut Subscriber) unsubscribe(name string, handler EventHandlerFn) {
|
||||||
v := voidptr(handler)
|
// v := voidptr(handler)
|
||||||
for i, n in s.registry.names {
|
for i, event in s.registry.events {
|
||||||
if name == n {
|
if event.name == name {
|
||||||
eh := s.registry.events[i]
|
if event.handler == handler {
|
||||||
if eh == v {
|
s.registry.events.delete(i)
|
||||||
s.registry.delete_entry(i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry Methods
|
// Registry Methods
|
||||||
|
|
||||||
fn (r &Registry) check_subscriber(name string) bool {
|
fn (r &Registry) check_subscriber(name string) bool {
|
||||||
for n in r.names {
|
for event in r.events {
|
||||||
if name == n {return true}
|
if event.name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
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
|
eventbus
|
||||||
)
|
)
|
||||||
|
|
||||||
|
struct EventData {
|
||||||
|
data string
|
||||||
|
}
|
||||||
|
|
||||||
fn test_eventbus(){
|
fn test_eventbus(){
|
||||||
|
ev_data := &EventData{'hello'}
|
||||||
mut eb := eventbus.new()
|
mut eb := eventbus.new()
|
||||||
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.subscriber.is_subscribed("on_test")
|
assert eb.subscriber.is_subscribed("on_test")
|
||||||
|
|
||||||
mut params := eventbus.Params{}
|
eb.publish("on_test", eb, ev_data)
|
||||||
params.put_string("eventbus", "vevent")
|
|
||||||
|
|
||||||
eb.publish("on_test", eb, params)
|
|
||||||
|
|
||||||
assert !eb.has_subscriber("on_test")
|
assert !eb.has_subscriber("on_test")
|
||||||
assert !eb.subscriber.is_subscribed("on_test")
|
assert !eb.subscriber.is_subscribed("on_test")
|
||||||
@ -28,37 +30,6 @@ fn test_eventbus(){
|
|||||||
assert !eb.subscriber.is_subscribed("on_test")
|
assert !eb.subscriber.is_subscribed("on_test")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_params(){
|
fn on_test(sender voidptr, ev &EventData) {
|
||||||
mut params := eventbus.Params{}
|
assert ev.data == "hello"
|
||||||
|
|
||||||
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){}
|
|
||||||
|
@ -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