First pass at an event-based notification facilty
The matrix.eventable module/function returns a table with a pair of functions
which can be used to connect event handlers to events, and to generate them.
Basic usage is as follows:
obj = eventable()
obj.hook("event-name", print) -- Handle the event with "print"
obj.fire("event-name", "Hello, world") -- Prints "Hello, world"
This initial implementation has the following limitations:
* Event handlers are always invoked in the same order in which they have been
connected using the .hook() function.
* Individual event handlers cannot be disconnected. Only disconnecting all
handlers for en event at once is possible via .hook("event", nil).
This commit is contained in:
96
matrix/eventable.lua
Normal file
96
matrix/eventable.lua
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#! /usr/bin/env lua
|
||||||
|
--
|
||||||
|
-- eventable.lua
|
||||||
|
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
|
||||||
|
--
|
||||||
|
-- Distributed under terms of the MIT license.
|
||||||
|
--
|
||||||
|
|
||||||
|
local _select, _pack, _unpack = select, table.pack, table.unpack or unpack
|
||||||
|
|
||||||
|
if not _pack then
|
||||||
|
_pack = function (...)
|
||||||
|
local n = _select("#", ...)
|
||||||
|
local r = { n = n }
|
||||||
|
for i = 1, n do
|
||||||
|
r[i] = _select(i, ...)
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _expack (args, ...)
|
||||||
|
local r = { n = args.n }
|
||||||
|
local n = _select("#", ...)
|
||||||
|
for i = 1, args.n do
|
||||||
|
r[i] = args[i]
|
||||||
|
end
|
||||||
|
for i = 1, n do
|
||||||
|
r[r.n + i] = _select(i, ...)
|
||||||
|
end
|
||||||
|
r.n = r.n + n
|
||||||
|
return _unpack(r)
|
||||||
|
end
|
||||||
|
|
||||||
|
return function (...)
|
||||||
|
local event_map = {}
|
||||||
|
local events = {}
|
||||||
|
|
||||||
|
function events.hook(event, handler)
|
||||||
|
if not handler then
|
||||||
|
event_map[event] = nil
|
||||||
|
else
|
||||||
|
if not event_map[event] then
|
||||||
|
event_map[event] = {}
|
||||||
|
end
|
||||||
|
local handlers = event_map[event]
|
||||||
|
handlers[#handlers + 1] = handler
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local nargs = _select("#", ...)
|
||||||
|
if nargs == 0 then
|
||||||
|
-- Simplest version, no arguments.
|
||||||
|
function events.fire(event, ...)
|
||||||
|
local handlers = event_map[event]
|
||||||
|
if handlers then
|
||||||
|
for i = 1, #handlers do
|
||||||
|
local ret = _pack(handlers[i](...))
|
||||||
|
if ret.n > 0 then
|
||||||
|
return _unpack(ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif nargs == 1 then
|
||||||
|
-- Common single-argument case: optimized.
|
||||||
|
local arg = _select(1, ...)
|
||||||
|
function events.fire(event, ...)
|
||||||
|
local handlers = event_map[event]
|
||||||
|
if handlers then
|
||||||
|
for i = 1, #handlers do
|
||||||
|
local ret = _pack(handlers[i](arg, ...))
|
||||||
|
if ret.n > 0 then
|
||||||
|
return _unpack(ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Generic multi-argument case.
|
||||||
|
local args = _pack(...)
|
||||||
|
function events.fire(event, ...)
|
||||||
|
local handlers = event_map[event]
|
||||||
|
if handlers then
|
||||||
|
for i = 1, #handlers do
|
||||||
|
local ret = _pack(handlers[i](_expack(args, ...)))
|
||||||
|
if ret.n > 0 then
|
||||||
|
return _unpack(ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return events
|
||||||
|
end
|
||||||
52
test/eventable.lua
Normal file
52
test/eventable.lua
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#! /usr/bin/env lua
|
||||||
|
--
|
||||||
|
-- eventable.lua
|
||||||
|
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
|
||||||
|
--
|
||||||
|
-- Distributed under terms of the MIT license.
|
||||||
|
--
|
||||||
|
|
||||||
|
local eventable = require "matrix.eventable"
|
||||||
|
|
||||||
|
do -- Simple event
|
||||||
|
local ev = assert(eventable())
|
||||||
|
local flag = false
|
||||||
|
ev.hook("foo", function () flag = true end)
|
||||||
|
ev.fire("foo")
|
||||||
|
assert(flag)
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- Stop at first handler that returns some value
|
||||||
|
local ev = assert(eventable())
|
||||||
|
local flag1, flag2, flag3 = false, false, false
|
||||||
|
ev.hook("foo", function () flag1 = true end)
|
||||||
|
ev.hook("foo", function () flag2 = true ; return 42 end)
|
||||||
|
ev.hook("foo", function () flag3 = true ; return 0 end)
|
||||||
|
assert(ev.fire("foo") == 42)
|
||||||
|
assert(flag1 == true)
|
||||||
|
assert(flag2 == true)
|
||||||
|
assert(flag3 == false)
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- Arguments to eventable() are passed to handlers
|
||||||
|
local obj = { answer = 42 }
|
||||||
|
local ev = assert(eventable(obj))
|
||||||
|
ev.hook("foo", function (o)
|
||||||
|
assert(o == obj)
|
||||||
|
assert(o.answer == 42)
|
||||||
|
o.answer = 0 -- Mutate
|
||||||
|
end)
|
||||||
|
ev.fire("foo")
|
||||||
|
assert(obj.answer == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- Multiple arguments passed to eventable
|
||||||
|
local ev = assert(eventable(42, "bar", nil, { v=10 }))
|
||||||
|
ev.hook("foo", function (a, s, n, o)
|
||||||
|
assert(a == 42)
|
||||||
|
assert(s == "bar")
|
||||||
|
assert(n == nil)
|
||||||
|
assert(o.v == 10)
|
||||||
|
end)
|
||||||
|
ev.fire("foo")
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user