diff --git a/examples/eventbus/eventbus.v b/examples/eventbus/eventbus.v index 5480f8f299..e4d45f0403 100644 --- a/examples/eventbus/eventbus.v +++ b/examples/eventbus/eventbus.v @@ -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) } diff --git a/examples/eventbus/modules/some_module/some_module.v b/examples/eventbus/modules/some_module/some_module.v index 6963209480..f30f720985 100644 --- a/examples/eventbus/modules/some_module/some_module.v +++ b/examples/eventbus/modules/some_module/some_module.v @@ -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 { diff --git a/vlib/eventbus/README.md b/vlib/eventbus/README.md index b9e4404557..1d13c31bf9 100644 --- a/vlib/eventbus/README.md +++ b/vlib/eventbus/README.md @@ -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. diff --git a/vlib/eventbus/eventbus.v b/vlib/eventbus/eventbus.v index 628556365a..6a5f45f915 100644 --- a/vlib/eventbus/eventbus.v +++ b/vlib/eventbus/eventbus.v @@ -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) -} diff --git a/vlib/eventbus/eventbus_test.v b/vlib/eventbus/eventbus_test.v index 8c76d77882..173188459c 100644 --- a/vlib/eventbus/eventbus_test.v +++ b/vlib/eventbus/eventbus_test.v @@ -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" +} \ No newline at end of file diff --git a/vlib/eventbus/params.v b/vlib/eventbus/params.v deleted file mode 100644 index 1ddbb4aa1d..0000000000 --- a/vlib/eventbus/params.v +++ /dev/null @@ -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(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(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 (s T, m voidptr) array_T { - return *(*array_T(m)) -} - -fn unmarshall_map (s T, m voidptr) map_T { - return *(*map_T(m)) -}