-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
339 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
--[[ $Id: CallbackHandler-1.0.lua 60548 2008-02-07 11:04:06Z nevcairiel $ ]] | ||
local MAJOR, MINOR = "CallbackHandler-1.0", 3 | ||
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR) | ||
|
||
if not CallbackHandler then return end -- No upgrade needed | ||
|
||
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end} | ||
|
||
local type = type | ||
local pcall = pcall | ||
local pairs = pairs | ||
local assert = assert | ||
local concat = table.concat | ||
local loadstring = loadstring | ||
local next = next | ||
local select = select | ||
local type = type | ||
local xpcall = xpcall | ||
|
||
local function errorhandler(err) | ||
return geterrorhandler()(err) | ||
end | ||
|
||
local function CreateDispatcher(argCount) | ||
local code = [[ | ||
local next, xpcall, eh = ... | ||
local method, ARGS | ||
local function call() method(ARGS) end | ||
local function dispatch(handlers, ...) | ||
local index | ||
index, method = next(handlers) | ||
if not method then return end | ||
local OLD_ARGS = ARGS | ||
ARGS = ... | ||
repeat | ||
xpcall(call, eh) | ||
index, method = next(handlers, index) | ||
until not method | ||
ARGS = OLD_ARGS | ||
end | ||
return dispatch | ||
]] | ||
|
||
local ARGS, OLD_ARGS = {}, {} | ||
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end | ||
code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", ")) | ||
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler) | ||
end | ||
|
||
local Dispatchers = setmetatable({}, {__index=function(self, argCount) | ||
local dispatcher = CreateDispatcher(argCount) | ||
rawset(self, argCount, dispatcher) | ||
return dispatcher | ||
end}) | ||
|
||
-------------------------------------------------------------------------- | ||
-- CallbackHandler:New | ||
-- | ||
-- target - target object to embed public APIs in | ||
-- RegisterName - name of the callback registration API, default "RegisterCallback" | ||
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback" | ||
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API. | ||
|
||
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused) | ||
-- TODO: Remove this after beta has gone out | ||
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused") | ||
|
||
RegisterName = RegisterName or "RegisterCallback" | ||
UnregisterName = UnregisterName or "UnregisterCallback" | ||
if UnregisterAllName==nil then -- false is used to indicate "don't want this method" | ||
UnregisterAllName = "UnregisterAllCallbacks" | ||
end | ||
|
||
-- we declare all objects and exported APIs inside this closure to quickly gain access | ||
-- to e.g. function names, the "target" parameter, etc | ||
|
||
|
||
-- Create the registry object | ||
local events = setmetatable({}, meta) | ||
local registry = { recurse=0, events=events } | ||
|
||
-- registry:Fire() - fires the given event/message into the registry | ||
function registry:Fire(eventname, ...) | ||
if not rawget(events, eventname) or not next(events[eventname]) then return end | ||
local oldrecurse = registry.recurse | ||
registry.recurse = oldrecurse + 1 | ||
|
||
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...) | ||
|
||
registry.recurse = oldrecurse | ||
|
||
if registry.insertQueue and oldrecurse==0 then | ||
-- Something in one of our callbacks wanted to register more callbacks; they got queued | ||
for eventname,callbacks in pairs(registry.insertQueue) do | ||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. | ||
for self,func in pairs(callbacks) do | ||
events[eventname][self] = func | ||
-- fire OnUsed callback? | ||
if first and registry.OnUsed then | ||
registry.OnUsed(registry, target, eventname) | ||
first = nil | ||
end | ||
end | ||
end | ||
registry.insertQueue = nil | ||
end | ||
end | ||
|
||
-- Registration of a callback, handles: | ||
-- self["method"], leads to self["method"](self, ...) | ||
-- self with function ref, leads to functionref(...) | ||
-- "addonId" (instead of self) with function ref, leads to functionref(...) | ||
-- all with an optional arg, which, if present, gets passed as first argument (after self if present) | ||
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]]) | ||
if type(eventname) ~= "string" then | ||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2) | ||
end | ||
|
||
method = method or eventname | ||
|
||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. | ||
|
||
if type(method) ~= "string" and type(method) ~= "function" then | ||
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2) | ||
end | ||
|
||
local regfunc | ||
|
||
if type(method) == "string" then | ||
-- self["method"] calling style | ||
if type(self) ~= "table" then | ||
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2) | ||
elseif self==target then | ||
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2) | ||
elseif type(self[method]) ~= "function" then | ||
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2) | ||
end | ||
|
||
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! | ||
local arg=select(1,...) | ||
regfunc = function(...) self[method](self,arg,...) end | ||
else | ||
regfunc = function(...) self[method](self,...) end | ||
end | ||
else | ||
-- function ref with self=object or self="addonId" | ||
if type(self)~="table" and type(self)~="string" then | ||
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2) | ||
end | ||
|
||
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! | ||
local arg=select(1,...) | ||
regfunc = function(...) method(arg,...) end | ||
else | ||
regfunc = method | ||
end | ||
end | ||
|
||
|
||
if events[eventname][self] or registry.recurse<1 then | ||
-- if registry.recurse<1 then | ||
-- we're overwriting an existing entry, or not currently recursing. just set it. | ||
events[eventname][self] = regfunc | ||
-- fire OnUsed callback? | ||
if registry.OnUsed and first then | ||
registry.OnUsed(registry, target, eventname) | ||
end | ||
else | ||
-- we're currently processing a callback in this registry, so delay the registration of this new entry! | ||
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency | ||
registry.insertQueue = registry.insertQueue or setmetatable({},meta) | ||
registry.insertQueue[eventname][self] = regfunc | ||
end | ||
end | ||
|
||
-- Unregister a callback | ||
target[UnregisterName] = function(self, eventname) | ||
if not self or self==target then | ||
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2) | ||
end | ||
if type(eventname) ~= "string" then | ||
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2) | ||
end | ||
if rawget(events, eventname) and events[eventname][self] then | ||
events[eventname][self] = nil | ||
-- Fire OnUnused callback? | ||
if registry.OnUnused and not next(events[eventname]) then | ||
registry.OnUnused(registry, target, eventname) | ||
end | ||
end | ||
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then | ||
registry.insertQueue[eventname][self] = nil | ||
end | ||
end | ||
|
||
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds | ||
if UnregisterAllName then | ||
target[UnregisterAllName] = function(...) | ||
if select("#",...)<1 then | ||
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2) | ||
end | ||
if select("#",...)==1 and ...==target then | ||
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2) | ||
end | ||
|
||
|
||
for i=1,select("#",...) do | ||
local self = select(i,...) | ||
if registry.insertQueue then | ||
for eventname, callbacks in pairs(registry.insertQueue) do | ||
if callbacks[self] then | ||
callbacks[self] = nil | ||
end | ||
end | ||
end | ||
for eventname, callbacks in pairs(events) do | ||
if callbacks[self] then | ||
callbacks[self] = nil | ||
-- Fire OnUnused callback? | ||
if registry.OnUnused and not next(callbacks) then | ||
registry.OnUnused(registry, target, eventname) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
return registry | ||
end | ||
|
||
|
||
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it | ||
-- try to upgrade old implicit embeds since the system is selfcontained and | ||
-- relies on closures to work. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
|
||
assert(LibStub, "LibDataBroker-1.1 requires LibStub") | ||
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0") | ||
|
||
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 3) | ||
if not lib then return end | ||
oldminor = oldminor or 0 | ||
|
||
|
||
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib) | ||
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {} | ||
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks | ||
|
||
if oldminor < 2 then | ||
lib.domt = { | ||
__metatable = "access denied", | ||
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end, | ||
} | ||
end | ||
|
||
if oldminor < 3 then | ||
lib.domt.__newindex = function(self, key, value) | ||
if not attributestorage[self] then attributestorage[self] = {} end | ||
if attributestorage[self][key] == value then return end | ||
attributestorage[self][key] = value | ||
local name = namestorage[self] | ||
if not name then return end | ||
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self) | ||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self) | ||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self) | ||
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self) | ||
end | ||
end | ||
|
||
if oldminor < 2 then | ||
function lib:NewDataObject(name, dataobj) | ||
if self.proxystorage[name] then return end | ||
|
||
if dataobj then | ||
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table") | ||
self.attributestorage[dataobj] = {} | ||
for i,v in pairs(dataobj) do | ||
self.attributestorage[dataobj][i] = v | ||
dataobj[i] = nil | ||
end | ||
end | ||
dataobj = setmetatable(dataobj or {}, self.domt) | ||
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name | ||
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj) | ||
return dataobj | ||
end | ||
end | ||
|
||
if oldminor < 1 then | ||
function lib:DataObjectIterator() | ||
return pairs(self.proxystorage) | ||
end | ||
|
||
function lib:GetDataObjectByName(dataobjectname) | ||
return self.proxystorage[dataobjectname] | ||
end | ||
|
||
function lib:GetNameByDataObject(dataobject) | ||
return self.namestorage[dataobject] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info | ||
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke | ||
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! | ||
local LibStub = _G[LIBSTUB_MAJOR] | ||
|
||
if not LibStub or LibStub.minor < LIBSTUB_MINOR then | ||
LibStub = LibStub or {libs = {}, minors = {} } | ||
_G[LIBSTUB_MAJOR] = LibStub | ||
LibStub.minor = LIBSTUB_MINOR | ||
|
||
function LibStub:NewLibrary(major, minor) | ||
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") | ||
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") | ||
|
||
local oldminor = self.minors[major] | ||
if oldminor and oldminor >= minor then return nil end | ||
self.minors[major], self.libs[major] = minor, self.libs[major] or {} | ||
return self.libs[major], oldminor | ||
end | ||
|
||
function LibStub:GetLibrary(major, silent) | ||
if not self.libs[major] and not silent then | ||
error(("Cannot find a library instance of %q."):format(tostring(major)), 2) | ||
end | ||
return self.libs[major], self.minors[major] | ||
end | ||
|
||
function LibStub:IterateLibraries() return pairs(self.libs) end | ||
setmetatable(LibStub, { __call = LibStub.GetLibrary }) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters