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:
Adrian Perez de Castro
2016-06-30 00:24:24 +03:00
parent 5a23cb5b85
commit 99fbf8eb28
2 changed files with 148 additions and 0 deletions

96
matrix/eventable.lua Normal file
View 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