Files
lua-matrix/matrix/eventable.lua
Adrian Perez de Castro fc99710658 Improvements to matrix.eventable
* Support debug-logging to stderr by defining the MATRIX_EVENTABLE_DEBUG_LOG
  environment variable.
* Support for unhooking handlers from events.
* Simplify the low-level interface: eventable.functions() now returns the
  created functions directly, cutting down on an intermediate table.
* A new eventable.object() allows adding :hook(), :unhook() and :fire()
  methods to any table. Firing events always sends the table itself as first
  event parameter when firing any event.
2016-07-01 04:55:16 +03:00

155 lines
3.8 KiB
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
local log = (function ()
local env_value = os.getenv("MATRIX_EVENTABLE_DEBUG_LOG")
if env_value and #env_value > 0 and env_value ~= "0" then
local out, _tostring = io.stderr, tostring
return function (...)
out:write("[eventable]")
local args = _pack(...)
for i = 1, args.n do
out:write(" " .. _tostring(args[i]))
end
out:write("\n")
out:flush()
end
else
return function (...) end
end
end)()
local function eventable_functions (...)
local event_map = {}
local hook = function (event, handler)
log("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 unhook = function (event, handler)
log("unhook:", event, handler)
local old_handlers = event_map[event]
if old_handlers then
local handlers = {}
for i = 1, #old_handlers do
local h = old_handlers[i]
if h ~= handler then
handlers[#handlers + 1] = h
end
end
event_map[event] = handlers
end
end
local fire
local nargs = _select("#", ...)
if nargs == 0 then
-- Simplest version, no arguments.
fire = function (event, ...)
log("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, ...)
fire = function (event, ...)
log("fire: " .. event .. ":", arg, ...)
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(...)
fire = function (event, ...)
log("fire: " .. event .. ":", _expack(args, ...))
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 fire, hook, unhook
end
local function eventable_object(obj)
local fire, hook, unhook = eventable_functions()
function obj:fire(name, ...)
return fire(name, self, ...)
end
function obj:hook(...)
hook(...)
return self -- Allow chaining
end
function obj:unhook(...)
unhook(...)
return self -- Allow chaining
end
return obj
end
return {
functions = eventable_functions,
object = eventable_object,
}