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.
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
#! /usr/bin/env lua
|
|
||||||
--
|
--
|
||||||
-- eventable.lua
|
-- eventable.lua
|
||||||
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
|
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
|
||||||
@@ -32,11 +31,29 @@ local function _expack (args, ...)
|
|||||||
return _unpack(r)
|
return _unpack(r)
|
||||||
end
|
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 (...)
|
return function (...)
|
||||||
local event_map = {}
|
out:write("[eventable]")
|
||||||
local events = {}
|
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)()
|
||||||
|
|
||||||
function events.hook(event, handler)
|
local function eventable_functions (...)
|
||||||
|
local event_map = {}
|
||||||
|
|
||||||
|
local hook = function (event, handler)
|
||||||
|
log("hook:", event, handler)
|
||||||
if not handler then
|
if not handler then
|
||||||
event_map[event] = nil
|
event_map[event] = nil
|
||||||
else
|
else
|
||||||
@@ -48,10 +65,28 @@ return function (...)
|
|||||||
end
|
end
|
||||||
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("#", ...)
|
local nargs = _select("#", ...)
|
||||||
if nargs == 0 then
|
if nargs == 0 then
|
||||||
-- Simplest version, no arguments.
|
-- Simplest version, no arguments.
|
||||||
function events.fire(event, ...)
|
fire = function (event, ...)
|
||||||
|
log("fire: " .. event .. ":", ...)
|
||||||
local handlers = event_map[event]
|
local handlers = event_map[event]
|
||||||
if handlers then
|
if handlers then
|
||||||
for i = 1, #handlers do
|
for i = 1, #handlers do
|
||||||
@@ -65,7 +100,8 @@ return function (...)
|
|||||||
elseif nargs == 1 then
|
elseif nargs == 1 then
|
||||||
-- Common single-argument case: optimized.
|
-- Common single-argument case: optimized.
|
||||||
local arg = _select(1, ...)
|
local arg = _select(1, ...)
|
||||||
function events.fire(event, ...)
|
fire = function (event, ...)
|
||||||
|
log("fire: " .. event .. ":", arg, ...)
|
||||||
local handlers = event_map[event]
|
local handlers = event_map[event]
|
||||||
if handlers then
|
if handlers then
|
||||||
for i = 1, #handlers do
|
for i = 1, #handlers do
|
||||||
@@ -79,7 +115,8 @@ return function (...)
|
|||||||
else
|
else
|
||||||
-- Generic multi-argument case.
|
-- Generic multi-argument case.
|
||||||
local args = _pack(...)
|
local args = _pack(...)
|
||||||
function events.fire(event, ...)
|
fire = function (event, ...)
|
||||||
|
log("fire: " .. event .. ":", _expack(args, ...))
|
||||||
local handlers = event_map[event]
|
local handlers = event_map[event]
|
||||||
if handlers then
|
if handlers then
|
||||||
for i = 1, #handlers do
|
for i = 1, #handlers do
|
||||||
@@ -92,5 +129,26 @@ return function (...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return events
|
return fire, hook, unhook
|
||||||
end
|
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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#! /usr/bin/env lua
|
|
||||||
--
|
--
|
||||||
-- eventable.lua
|
-- eventable.lua
|
||||||
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
|
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
|
||||||
@@ -9,20 +8,20 @@
|
|||||||
local eventable = require "matrix.eventable"
|
local eventable = require "matrix.eventable"
|
||||||
|
|
||||||
do -- Simple event
|
do -- Simple event
|
||||||
local ev = assert(eventable())
|
local fire, hook = assert(eventable())
|
||||||
local flag = false
|
local flag = false
|
||||||
ev.hook("foo", function () flag = true end)
|
hook("foo", function () flag = true end)
|
||||||
ev.fire("foo")
|
fire("foo")
|
||||||
assert(flag)
|
assert(flag)
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- Stop at first handler that returns some value
|
do -- Stop at first handler that returns some value
|
||||||
local ev = assert(eventable())
|
local fire, hook = assert(eventable())
|
||||||
local flag1, flag2, flag3 = false, false, false
|
local flag1, flag2, flag3 = false, false, false
|
||||||
ev.hook("foo", function () flag1 = true end)
|
hook("foo", function () flag1 = true end)
|
||||||
ev.hook("foo", function () flag2 = true ; return 42 end)
|
hook("foo", function () flag2 = true ; return 42 end)
|
||||||
ev.hook("foo", function () flag3 = true ; return 0 end)
|
hook("foo", function () flag3 = true ; return 0 end)
|
||||||
assert(ev.fire("foo") == 42)
|
assert(fire("foo") == 42)
|
||||||
assert(flag1 == true)
|
assert(flag1 == true)
|
||||||
assert(flag2 == true)
|
assert(flag2 == true)
|
||||||
assert(flag3 == false)
|
assert(flag3 == false)
|
||||||
@@ -30,23 +29,45 @@ end
|
|||||||
|
|
||||||
do -- Arguments to eventable() are passed to handlers
|
do -- Arguments to eventable() are passed to handlers
|
||||||
local obj = { answer = 42 }
|
local obj = { answer = 42 }
|
||||||
local ev = assert(eventable(obj))
|
local fire, hook = assert(eventable(obj))
|
||||||
ev.hook("foo", function (o)
|
hook("foo", function (o)
|
||||||
assert(o == obj)
|
assert(o == obj)
|
||||||
assert(o.answer == 42)
|
assert(o.answer == 42)
|
||||||
o.answer = 0 -- Mutate
|
o.answer = 0 -- Mutate
|
||||||
end)
|
end)
|
||||||
ev.fire("foo")
|
fire("foo")
|
||||||
assert(obj.answer == 0)
|
assert(obj.answer == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- Multiple arguments passed to eventable
|
do -- Multiple arguments passed to eventable
|
||||||
local ev = assert(eventable(42, "bar", nil, { v=10 }))
|
local fire, hook = assert(eventable(42, "bar", nil, { v=10 }))
|
||||||
ev.hook("foo", function (a, s, n, o)
|
hook("foo", function (a, s, n, o)
|
||||||
assert(a == 42)
|
assert(a == 42)
|
||||||
assert(s == "bar")
|
assert(s == "bar")
|
||||||
assert(n == nil)
|
assert(n == nil)
|
||||||
assert(o.v == 10)
|
assert(o.v == 10)
|
||||||
end)
|
end)
|
||||||
ev.fire("foo")
|
fire("foo")
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- Unhooking should work
|
||||||
|
local flag1, flag2 = false, false
|
||||||
|
local h1 = function () flag1 = true end
|
||||||
|
local h2 = function () flag2 = true end
|
||||||
|
|
||||||
|
local fire, hook, unhook = assert(eventable())
|
||||||
|
hook("foo", h1)
|
||||||
|
hook("foo", h2)
|
||||||
|
fire("foo")
|
||||||
|
assert(flag1 == true)
|
||||||
|
assert(flag2 == true)
|
||||||
|
|
||||||
|
flag1, flag2 = false, false
|
||||||
|
unhook("foo", h2)
|
||||||
|
fire("foo")
|
||||||
|
assert(flag1 == true)
|
||||||
|
assert(flag2 == false)
|
||||||
|
|
||||||
|
flag1, flag2 = false, false
|
||||||
|
unhook("foo")
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user