Quantcast

Updated to Dongle-Beta0

James Whitehead II [01-11-07 - 02:43]
Updated to Dongle-Beta0
Filename
Dongle.lua
diff --git a/Dongle.lua b/Dongle.lua
index 321fb22..a3a9b48 100644
--- a/Dongle.lua
+++ b/Dongle.lua
@@ -28,8 +28,8 @@
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------]]
-local major = "DongleStub"
-local minor = tonumber(string.match("$Revision: 173 $", "(%d+)") or 1)
+local major = "DongleStub-Beta0"
+local minor = tonumber(string.match("$Revision: 221 $", "(%d+)") or 1)

 local g = getfenv(0)

@@ -37,7 +37,7 @@ if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then
 	local lib = setmetatable({}, {
 		__call = function(t,k)
 			if type(t.versions) == "table" and t.versions[k] then
-				return t.versions[k]
+				return t.versions[k].instance
 			else
 				error("Cannot find a library with name '"..tostring(k).."'", 2)
 			end
@@ -45,60 +45,92 @@ if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then
 	})

 	function lib:IsNewerVersion(major, minor)
-		local entry = self.versions and self.versions[major]
+		local versionData = self.versions and self.versions[major]

-		if not entry then return true end
-		local oldmajor,oldminor = entry:GetVersion()
+		if not versionData then return true end
+		local oldmajor,oldminor = versionData.instance:GetVersion()

 		return minor > oldminor
 	end

-	function lib:Register(new)
-		local major,minor = new:GetVersion()
+	local function NilCopyTable(src, dest)
+		for k,v in pairs(dest) do dest[k] = nil end
+		for k,v in pairs(src) do dest[k] = v end
+	end
+
+	function lib:Register(newInstance, activate, deactivate)
+		local major,minor = newInstance:GetVersion()
 		if not self:IsNewerVersion(major, minor) then return false end
-		local old = self.versions and self.versions[major]
-		-- Run the new libraries activation
-		if type(new.Activate) == "function" then
-			new:Activate(old)
+		if not self.versions then self.versions = {} end
+
+		local versionData = self.versions[major]
+		if not versionData then
+			-- New major version
+			versionData = {
+				["instance"] = newInstance,
+				["deactivate"] = deactivate,
+			}
+
+			self.versions[major] = versionData
+			if type(activate) == "function" then
+				activate(newInstance)
+			end
+			return newInstance
 		end

+		local oldDeactivate = versionData.deactivate
+		local oldInstance = versionData.instance
+
+		versionData.deactivate = deactivate
+
+		local skipCopy
+		if type(activate) == "function" then
+			 skipCopy = activate(newInstance, oldInstance)
+		end
+
 		-- Deactivate the old libary if necessary
-		if old and type(old.Deactivate) == "function" then
-			old:Deactivate(new)
+		if type(oldDeactivate) == "function" then
+			oldDeactivate(oldInstance, newInstance)
 		end
-
-		self.versions[major] = new
+
+		-- Re-use the old table, and discard the new one
+		if not skipCopy then
+			NilCopyTable(newInstance, oldInstance)
+		end
+		return oldInstance
 	end

 	function lib:GetVersion() return major,minor end

-	function lib:Activate(old)
-		if old then
-			self.versions = old.versions
-		else
-			self.versions = {}
+	local function Activate(new, old)
+		if old then
+			new.versions = old.versions
 		end
-		g.DongleStub = self
+		g.DongleStub = new
 	end

 	-- Actually trigger libary activation here
 	local stub = g.DongleStub or lib
-	stub:Register(lib)
+	stub:Register(lib, Activate)
 end

 --[[-------------------------------------------------------------------------
   Begin Library Implementation
 ---------------------------------------------------------------------------]]

-local major = "Dongle"
-local minor = tonumber(string.match("$Revision: 194 $", "(%d+)") or 1)
+local major = "Dongle-Beta0"
+local minor = tonumber(string.match("$Revision: 224 $", "(%d+)") or 1)
+
+assert(DongleStub, string.format("Dongle requires DongleStub.", major))
+assert(DongleStub and DongleStub:GetVersion() == "DongleStub-Beta0",
+	string.format("Dongle requires DongleStub-Beta0.  You are using an older version.", major))

-assert(DongleStub, string.format("%s requires DongleStub.", major))
 if not DongleStub:IsNewerVersion(major, minor) then return end

-Dongle = {}
+local Dongle = {}
 local methods = {
-	"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "TriggerEvent",
+	"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents",
+	"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage",
 	"EnableDebug", "Print", "PrintF", "Debug", "DebugF",
 	"InitializeDB",
 	"InitializeSlashCommand",
@@ -112,6 +144,8 @@ local loadorder = {}
 local events = {}
 local databases = {}
 local commands = {}
+local messages = {}
+
 local frame

 local function assert(level,condition,message)
@@ -193,24 +227,25 @@ function Dongle:HasModule(module)
 	return reg.modules[module]
 end

-local NIL_FUNC = function() end
+local function ModuleIterator(t, name)
+	if not t then return end
+	local module
+	repeat
+		name,module = next(t, name)
+	until type(name) == "string" or not name
+
+	if not name then return end
+	return name, module
+end

 function Dongle:IterateModules()
 	local reg = lookup[self]
 	assert(3, reg, "You must call 'IterateModules' from a registered Dongle.")
-
-	if not reg.modules or not next(reg.modules) then return NIL_FUNC end

-	local i=1
-	return function()
-		local name = reg.modules[i]
-		if not name then return end
-		i = i + 1
-		return name, reg.modules[name]
-	end
+	return ModuleIterator, reg.modules
 end

-function Dongle:ADDON_LOADED(event, ...)
+local function ADDON_LOADED(event, ...)
 	for i=1, #loadqueue do
 		local obj = loadqueue[i]
 		table.insert(loadorder, obj)
@@ -219,48 +254,42 @@ function Dongle:ADDON_LOADED(event, ...)
 			safecall(obj.Initialize, obj)
 		end

-		if self.initialized and type(obj.Enable) == "function" then
+		if Dongle.initialized and type(obj.Enable) == "function" then
 			safecall(obj.Enable, obj)
 		end
 		loadqueue[i] = nil
 	end
 end

-function Dongle:PLAYER_LOGIN()
-	self.initialized = true
-	for i,obj in ipairs(loadorder) do
-		if type(obj.Enable) == "function" then
-			safecall(obj.Enable, obj)
+local function PLAYER_LOGOUT(event)
+	self:ClearDBDefaults()
+	for k,v in pairs(registry) do
+		local obj = v.obj
+		if type(obj["Disable"]) == "function" then
+			safecall(obj["Disable"], obj)
 		end
 	end
 end

-function Dongle:TriggerEvent(event, ...)
-	argcheck(event, 2, "string")
-	local eventTbl = events[event]
-	if eventTbl then
-		for obj,func in pairs(eventTbl) do
-			if type(func) == "string" then
-				if type(obj[func]) == "function" then
-					safecall(obj[func], obj, event, ...)
-				end
-			else
-				safecall(func,event,...)
-			end
+local function PLAYER_LOGIN()
+	Dongle.initialized = true
+	for i,obj in ipairs(loadorder) do
+		if type(obj.Enable) == "function" then
+			safecall(obj.Enable, obj)
 		end
 	end
 end

-function Dongle:OnEvent(frame, event, ...)
+local function OnEvent(frame, event, ...)
 	local eventTbl = events[event]
 	if eventTbl then
 		for obj,func in pairs(eventTbl) do
 			if type(func) == "string" then
 				if type(obj[func]) == "function" then
-					obj[func](obj, event, ...)
+					safecall(obj[func], obj, event, ...)
 				end
 			else
-				func(event, ...)
+				safecall(func, event, ...)
 			end
 		end
 	end
@@ -308,21 +337,57 @@ function Dongle:UnregisterAllEvents()
 	end
 end

-function Dongle:AdminEvents(event)
-	local method
-	if event == "PLAYER_LOGOUT" then
-		Dongle:ClearDBDefaults()
-		method = "Disable"
-	elseif event == "PLAYER_REGEN_DISABLED" then
-		method = "CombatLockdown"
-	elseif event == "PLAYER_REGEN_ENABLED" then
-		method = "CombatUnlock"
+function Dongle:RegisterMessage(msg, func)
+	local reg = lookup[self]
+	assert(3, reg, "You must call 'RegisterMessage' from a registered Dongle.")
+	argcheck(msg, 2, "string")
+	argcheck(func, 3, "string", "function", "nil")
+
+	-- Name the method the same as the event if necessary
+	if not func then func = msg end
+
+	if not messages[msg] then
+		messages[msg] = {}
 	end
+	messages[msg][self] = func
+end

-	if method then
-		for k,v in pairs(registry) do
-			local obj = v.obj
-			if obj[method] then obj[method](obj) end
+function Dongle:UnregisterMessage(msg)
+	local reg = lookup[self]
+	assert(3, reg, "You must call 'UnregisterMessage' from a registered Dongle.")
+	argcheck(msg, 2, "string")
+
+	if messages[msg] then
+		messages[msg][self] = nil
+		if not next(messages[msg]) then
+			messages[msg] = nil
+		end
+	end
+end
+
+function Dongle:UnregisterAllMessages()
+	assert(3, lookup[self], "You must call 'UnregisterAllMessages' from a registered Dongle.")
+
+	for msg,tbl in pairs(messages) do
+		tbl[self] = nil
+		if not next(tbl) then
+			messages[msg] = nil
+		end
+	end
+end
+
+function Dongle:TriggerMessage(msg, ...)
+	argcheck(msg, 2, "string")
+	local msgTbl = messages[msg]
+	if not msgTbl then return end
+
+	for obj,func in pairs(msgTbl) do
+		if type(func) == "string" then
+			if type(obj[func]) == "function" then
+				safecall(obj[func], obj, msg, ...)
+			end
+		else
+			safecall(func, msg, ...)
 		end
 	end
 end
@@ -497,7 +562,7 @@ function Dongle:InitializeDB(name, defaults, defaultProfile)
 	local db,profileCreated = initdb(self, name, defaults, defaultProfile)

 	if profileCreated then
-		Dongle:TriggerEvent("DONGLE_PROFILE_CREATED", db, self, db.sv_name, db.profileKey)
+		Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", db, self, db.sv_name, db.profileKey)
 	end
 	return db
 end
@@ -603,10 +668,10 @@ function Dongle.SetProfile(db, name)
     db.profileKey = name

 	if profileCreated then
-		Dongle:TriggerEvent("DONGLE_PROFILE_CREATED", db, db.parent, db.sv_name, db.profileKey)
+		Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", db, db.parent, db.sv_name, db.profileKey)
 	end

-	Dongle:TriggerEvent("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.profileKey)
+	Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.profileKey)
 end

 function Dongle.GetProfiles(db, t)
@@ -631,7 +696,7 @@ function Dongle.DeleteProfile(db, name)
 	end

 	db.sv.profiles[name] = nil
-	Dongle:TriggerEvent("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name)
+	Dongle:TriggerMessage("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name)
 end

 function Dongle.CopyProfile(db, name)
@@ -645,7 +710,7 @@ function Dongle.CopyProfile(db, name)
 	local source = db.sv.profiles[name]

 	copyDefaults(profile, source, true)
-	Dongle:TriggerEvent("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.profileKey)
+	Dongle:TriggerMessage("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.profileKey)
 end

 function Dongle.ResetProfile(db)
@@ -659,7 +724,7 @@ function Dongle.ResetProfile(db)
 	if db.defaults and db.defaults.profile then
 		copyDefaults(profile, db.defaults.profile)
 	end
-	Dongle:TriggerEvent("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.profileKey)
+	Dongle:TriggerMessage("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.profileKey)
 end


@@ -675,9 +740,9 @@ function Dongle.ResetDB(db, defaultProfile)
 	local parent = db.parent

 	initdb(parent, db.sv_name, db.defaults, defaultProfile, db)
-	Dongle:TriggerEvent("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.profileKey)
-	Dongle:TriggerEvent("DONGLE_PROFILE_CREATED", db, db.parent, db.sv_name, db.profileKey)
-	Dongle:TriggerEvent("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.profileKey)
+	Dongle:TriggerMessage("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.profileKey)
+	Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", db, db.parent, db.sv_name, db.profileKey)
+	Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.profileKey)
 	return db
 end

@@ -693,7 +758,7 @@ local function OnSlashCommand(cmd, cmd_line)
 				if type(tbl.handler) == "string" then
 					cmd.parent[tbl.handler](cmd.parent, string.match(cmd_line, pattern))
 				else
-					tbl.handler(cmd.parent, string.match(cmd_line, pattern))
+					tbl.handler(string.match(cmd_line, pattern))
 				end
 				return
 			end
@@ -728,10 +793,15 @@ function Dongle:InitializeSlashCommand(desc, name, ...)
 	end

 	genv.SlashCmdList[name] = function(...) OnSlashCommand(cmd, ...) end
+
+	commands[cmd] = true
+
 	return cmd
 end

 function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler)
+	assert(3, commands[cmd], "You must call 'RegisterSlashHandler' from a Dongle slash command object.")
+
 	argcheck(desc, 2, "string")
 	argcheck(pattern, 3, "string")
 	argcheck(handler, 4, "function", "string")
@@ -746,6 +816,8 @@ function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler)
 end

 function Dongle.PrintUsage(cmd)
+	assert(3, commands[cmd], "You must call 'PrintUsage' from a Dongle slash command object.")
+
 	local usage = cmd.desc.."\n".."/"..table.concat(cmd.slashes, ", /")..":\n"
 	if cmd.patterns then
 		local descs = {}
@@ -766,7 +838,7 @@ end

 function Dongle:GetVersion() return major,minor end

-function Dongle:Activate(old)
+local function Activate(self, old)
 	if old then
 		self.registry = old.registry or registry
 		self.lookup = old.lookup or lookup
@@ -775,6 +847,7 @@ function Dongle:Activate(old)
 		self.events = old.events or events
 		self.databases = old.databases or databases
 		self.commands = old.commands or commands
+		self.messages = old.messages or messages

 		registry = self.registry
 		lookup = self.lookup
@@ -783,6 +856,7 @@ function Dongle:Activate(old)
 		events = self.events
 		databases = self.databases
 		commands = self.commands
+		messages = self.messages

 		frame = old.frame
 		self.registry[major].obj = self
@@ -794,6 +868,7 @@ function Dongle:Activate(old)
 		self.events = events
 		self.databases = databases
 		self.commands = commands
+		self.messages = messages

 		local reg = {obj = self, name = "Dongle"}
 		registry[major] = reg
@@ -806,14 +881,12 @@ function Dongle:Activate(old)
 	end

 	self.frame = frame
-	frame:SetScript("OnEvent", function(...) self:OnEvent(...) end)
+	frame:SetScript("OnEvent", OnEvent)

 	-- Register for events using Dongle itself
-	self:RegisterEvent("ADDON_LOADED")
-	self:RegisterEvent("PLAYER_LOGIN")
-	self:RegisterEvent("PLAYER_LOGOUT", "AdminEvents")
-	self:RegisterEvent("PLAYER_REGEN_ENABLED", "AdminEvents")
-	self:RegisterEvent("PLAYER_REGEN_DISABLED", "AdminEvents")
+	self:RegisterEvent("ADDON_LOADED", ADDON_LOADED)
+	self:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN)
+	self:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT)

 	-- Convert all the modules handles
 	for name,obj in pairs(registry) do
@@ -828,11 +901,18 @@ function Dongle:Activate(old)
 			db[method] = self[method]
 		end
 	end
+
+	-- Convert all slash command methods
+	for cmd in pairs(commands) do
+		for idx,method in ipairs(slashCmdMethods) do
+			cmd[method] = self[method]
+		end
+	end
 end

-function Dongle:Deactivate(new)
+local function Deactivate(self, new)
 	lookup[self] = nil
 	self:UnregisterAllEvents()
 end

-DongleStub:Register(Dongle)
+DongleStub:Register(Dongle, Activate, Deactivate)