# HG changeset patch
# User mckenziemc
# Date 1291162384 28800
# Node ID 9852fcd5e59edc7e944c02a6e70a3a684d1574a8
initial import
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader/DependencyLoader.toc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader/DependencyLoader.toc Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,11 @@
+## Interface: 40000
+
+## Notes: Bootstrap for DependencyLoader_Core
+## Author: mckenziemc
+
+## LoadManagers: !!!LoadFirst, AddonLoader
+## X-LoadFirst: true
+## X-LoadOn-Always: true
+
+
+start.xml
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader/bootstrap.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader/bootstrap.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,36 @@
+-- bootstrap.lua
+-- Bootstrap code for DependencyLoader, allowing
+-- the user to successfully load it without
+-- explicitly enabling its own dependencies.
+
+
+local addonName, addonTable = ...
+
+-- TODO: move and use dependency parsing function here?
+local dependencies = {"LibStub", "LibPrint-1.0", "Ace3"}
+
+local canLoad = true
+for _, addon in pairs(dependencies) do
+ local reason = select(6, GetAddOnInfo(addon))
+
+ if reason ~= nil and reason ~= "DISABLED" then
+ canLoad = false
+ break
+ end
+end
+
+if not canLoad then
+ print("Can't load DependencyLoader")
+ return
+end
+
+
+print("Loading DependencyLoader")
+
+for _, addon in pairs(dependencies) do
+ EnableAddOn(addon)
+end
+
+EnableAddOn("DependencyLoader_Core")
+LoadAddOn("DependencyLoader_Core")
+
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader/start.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader/start.xml Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,1 @@
+
\ No newline at end of file
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/Addon.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/Addon.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,152 @@
+-- Addon
+-- Represents individual addon modules
+
+
+local addonName, addonTable = ...
+
+print("running Addon.lua")
+
+-- NOTE: I assume that the API addon functions are
+-- slightly quicker with an index than with a number.
+
+-- TODO: modify the dependency stuff to use the Errata module if available
+
+local Addon, addon = addonTable:NewClass("Addon")
+
+function Addon:New(id)
+ assert(type(id) == "number" or type(id) == "string")
+
+ local instance = {}
+
+ setmetatable(instance, self.instanceMetatable)
+
+ if type(id) == "number" then
+ -- TODO: make sure it's in range
+ instance.index = id
+ instance.name = GetAddOnInfo(id)
+ else
+ -- FIXME: allow blizzard addons?
+ local index
+
+ for i=1,GetNumAddOns() do
+ if GetAddOnInfo(i) == id then
+ index = i
+ break
+ end
+ end
+
+ if index then
+ instance.name = GetAddOnInfo(id)
+ instance.index = index
+ else
+ error("Addon not found")
+ end
+ end
+
+ return instance
+end
+
+
+-- Checks if an addon exists with the specified name.
+-- @param addon Name of the addon.
+-- @return True if the addon is present, false otherwise.
+function Addon:Exists(addon)
+ if type(addon) == "number" then
+ if addon >= 1 and addon <= GetNumAddOns() then
+ return true
+ else
+ return false
+ end
+ elseif type(addon) == "string" then
+ local status = select(6, GetAddOnInfo(addon))
+
+ if status == "MISSING" then
+ return false
+ else
+ return true
+ end
+ else
+ error()
+ end
+end
+
+
+function addon:GetName()
+ return self.name
+end
+
+
+function addon:GetIndex()
+ return self.index
+end
+
+
+function addon:IsEnabled()
+ -- FIXME: written while tired; review later
+ local status = select(6, GetAddOnInfo(self.index))
+
+ if status == "DISABLED" then
+ return false
+ else
+ return true
+ end
+end
+
+-- FIXME: an addon may be present but unloadable if loading out of date addons is disabled.
+-- NOTE: CanForceLoad and CanLoD don't check the status of dependencies
+
+function addon:CanForceLoad()
+ return true -- TODO: check if there's any reason addons can't be forceloaded
+end
+
+function addon:CanLoD()
+ -- FIXME: what will the client say about addons using LoadManagers if the LM was force-loaded?
+ if IsAddOnLoadOnDemand(self.name) then
+ return true
+ else
+ return false
+ end
+end
+
+-- NOTE: only call for LoD, not force-loading
+function addon:Load()
+ assert(self:CanLoD())
+
+ EnableAddOn(self.name)
+ LoadAddOn(self.name)
+end
+
+function addon:ForceLoad()
+ assert(self:CanForceLoad())
+ -- TODO: make sure force-loading is available at this time
+
+ EnableAddOn(self.name) -- This should cause the game to also load this addon
+end
+
+
+function addon:GetDependencies()
+ return GetAddOnDependencies(self.index)
+end
+
+
+function addon:GetEmbeds()
+ local embeds = {}
+
+ local embedString = GetAddOnMetadata(self.name, "X-Embeds")
+
+ if embedString then
+ for match in string.gmatch(embedString, "[^,%s]+") do
+ table.insert(embeds, match)
+ end
+ end
+
+ return unpack(embeds)
+end
+
+function addon:IsLoaded()
+ if IsAddOnLoaded(self.index) then
+ return true
+ else
+ return false
+ end
+end
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/Core.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/Core.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,298 @@
+
+
+local addonName, addonTable = ...
+
+
+print("running Core.lua")
+
+-- TODO: prevent infinite loops in the recursive functions
+
+
+local Core, core = addonTable:NewClass("Core")
+
+local Addon = addonTable.classes.Addon
+
+function Core:New()
+ local instance = {}
+ setmetatable(instance, self.instanceMetatable)
+
+ instance.addons = {}
+ instance.nameToIndex = {}
+
+ for i=1,GetNumAddOns() do
+ local newAddon = Addon:New(i)
+
+ instance.addons[i] = newAddon
+ instance.nameToIndex[newAddon:GetName()] = i
+ end
+
+ -- TODO: enable dependencies for any addons that have already loaded
+
+ return instance
+end
+
+
+-- @param addon Name or index of the addon to retrieve.
+-- @return The addon object, or nil if not found
+function core:GetAddon(addon)
+ if not Addon:Exists(addon) then
+ return nil
+ end
+
+ local index
+
+ if type(addon) == "string" then
+ index = self.nameToIndex[addon]
+ elseif type(addon) == "number" then
+ index = addon
+ end
+
+ return self.addons[index]
+end
+
+
+-- Checks if the tree rooted at the specified addon can be force-loaded.
+-- @param root The name or index of the root addon.
+function core:CanForceLoadTree(root)
+ -- convert root to an Addon object
+ root = self:GetAddon(root)
+ assert(root)
+
+ if root:IsLoaded() then
+ return true
+ end
+
+ -- check if the root itself can be force-loaded
+ if not root:CanForceLoad() then
+ return false
+ end
+
+ -- now check dependencies recursively
+ -- FIXME: prevent infinite recursion
+
+ local dependencies = {root:GetDependencies()}
+ for i, depName in pairs(dependencies) do
+ local dep = self:GetAddon(depName)
+
+ if dep == nil then
+ return false -- missing dependency
+ end
+
+ -- if it's already loaded then skip to next one
+ if not dep:IsLoaded() then
+ if not self:CanForceLoadTree(depName) then
+ return false
+ end
+ end
+ end
+
+ return true
+end
+
+-- NOTE: any tree that can be loaded on demand is also eligible for force-loading
+-- Checks if the tree rooted at the specified addon can be loaded on demand.
+-- @param root The name or index of the root addon.
+function core:CanLoDTree(root)
+ root = self:GetAddon(root)
+ assert(root)
+
+ -- since this will be used recursively, return true if
+ -- this is already loaded.
+ if root:IsLoaded() then
+ return true
+ end
+
+ -- true if all dependencies are loaded or LoD
+
+ if not root:CanLoD() then
+ return false
+ end
+
+ -- now check dependencies recursively
+ local dependencies = {root:GetDependencies()}
+ for i, depName in pairs(dependencies) do
+ local dep = self:GetAddon(depName)
+
+ if not dep then
+ return false -- missing
+ end
+
+ if not dep:IsLoaded() then
+ if not self:CanLoDTree(depName) then
+ return false
+ end
+ end
+ end
+
+ return true
+end
+
+
+-- Checks if the tree rooted at the specified addon
+-- can be loaded if all dependencies are enabled
+-- the UI is reloaded.
+-- Basically makes sure all dependencies are installed.
+function core:CanReloadTree(root)
+ -- convert root to an Addon object
+ root = self:GetAddon(root)
+ assert(root)
+
+ if root:IsLoaded() then
+ return true
+ -- FIXME: deps may have been disabled
+ end
+
+ -- TODO: make sure the root isn't incompatible
+
+ -- now check dependencies recursively
+ -- FIXME: prevent infinite recursion
+
+ local dependencies = {root:GetDependencies()}
+ for i, depName in pairs(dependencies) do
+ local dep = self:GetAddon(depName)
+
+ if dep == nil then
+ return false -- missing dependency
+ end
+
+ -- TODO: make sure it's compatible
+ end
+
+ return true
+end
+
+-- Loads the tree rooted at the specified addon.
+-- FIXME: load the root too or not?
+-- Supports both LoD addons and those that require force-loading.
+function core:LoadTree(addon)
+ -- don't check if the tree can actually be loaded.
+ -- external code should do that itself to check if it
+ -- should even call this at all.
+
+ if self:ForceLoadAvailable() then
+ -- LoD trees can also be force-loaded
+ self:ForceLoadTree(addon)
+ else
+ self:LoadLoDTree(addon)
+ end
+end
+
+
+-- load the root too, since it may actually be a leaf
+function core:ForceLoadTree(root)
+ root = self:GetAddon(root)
+ assert(root)
+
+ -- load dependencies
+ local dependencies = {root:GetDependencies(addon)}
+ for i, depName in pairs(dependencies) do
+ self:ForceLoadTree(depName)
+ end
+
+ -- load embeds, if they are available separately
+ local embeds = {root:GetEmbeds(addon)}
+ for i, embedName in pairs(embeds) do
+ if Addon:Exists(embedName) then
+ self:ForceLoadTree(embedName)
+ end
+ end
+
+ root:ForceLoad()
+end
+
+
+function core:LoadLoDTree(root)
+ root = self:GetAddon(root)
+ assert(root)
+
+ -- load dependencies
+ local dependencies = {root:GetDependencies(addon)}
+ for i, depName in pairs(dependencies) do
+ self:LoadLoDTree(depName)
+ end
+
+ -- load embeds, if they are available separately
+ local embeds = {root:GetEmbeds(addon)}
+ for i, embedName in pairs(embeds) do
+ if Addon:Exists(embedName) then
+ self:LoadLoDTree(embedName)
+ end
+ end
+
+ root:LoD()
+end
+
+
+-- I think the problem this solves is a major issue with
+-- migrating to separate libs. think about it more and document
+-- here and in project description
+function core:PrepareLoDTree(root)
+ root = self:GetAddon(root)
+ assert(root)
+
+ -- assume root is LoD
+
+ -- check dependencies
+ local dependencies = {root:GetDependencies(addon)}
+ for i, depName in pairs(dependencies) do
+ local dep = self:GetAddon(depName)
+
+ -- assume external code made sure it exists
+
+ if dep:CanLoD() then
+ -- don't load it now but make sure its dependencies are prepared
+ self:PrepareLoDTree(depName)
+ else
+ -- FIXME: if it's already loaded
+ -- force-load it now so we can load the parent on demand
+ self:ForceLoadTree(depName)
+ end
+ end
+
+ -- prepare embeds, if they are available separately
+ local embeds = {root:GetEmbeds(addon)} -- FIXME: addon?
+ for i, embedName in pairs(embeds) do
+ local embed = self:GetAddon(embedName)
+
+ if embed then
+ if embed:CanLoD() then
+ -- don't load it now but make sure its dependencies are prepared
+ self:PrepareLoDTree(embedName)
+ else
+ -- FIXME: if it's already loaded
+ -- force-load it now so we can load the parent on demand
+ self:ForceLoadTree(depName)
+ end
+ end
+ end
+end
+
+
+function Core:PrepareReloadTree(addon)
+ root = self:GetAddon(root)
+ assert(root)
+
+ root:Enable()
+
+ -- check dependencies
+ local dependencies = {root:GetDependencies()}
+ for i, depName in pairs(dependencies) do
+ self:PrepareReloadTree(depName)
+ end
+
+ -- prepare embeds, if they are available separately
+ local embeds = {root:GetEmbeds(addon)}
+ for i, embedName in pairs(embeds) do
+ local embed = self:GetAddon(embedName)
+
+ if embed then
+ self:PrepareReloadTree(embedName)
+ end
+ end
+end
+
+
+function Core:ForceLoadAvailable()
+ return true
+ -- FIXME: use field and a frame registered for PLAYER_LOGIN
+end
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/DependencyLoader_Core.toc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/DependencyLoader_Core.toc Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,14 @@
+## Interface: 40000
+
+# TODO: actually explain what DependencyLoader does.
+## Notes: Core module of DependencyLoader
+## Author: mckenziemc
+
+# TODO: figure out some way to allow AddonLoader
+# to work with DependencyLoader
+## Dependencies: DependencyLoader, LibStub, LibPrint-1.0, Ace3
+
+## LoadOnDemand: 1
+
+
+start.xml
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/class.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/class.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,23 @@
+-- class.lua
+-- Implements a generic class builder.
+
+
+local addonName, addonTable = ...
+
+
+print( string.format([[running %s\class.lua]], addonName) )
+
+addonTable.classes = {}
+
+function addonTable:NewClass(name)
+ local class, prototype, metatable = {}, {}, {}
+
+ class.prototype = prototype
+
+ metatable.__index = prototype
+ class.instanceMetatable = metatable
+
+ self.classes[name] = class
+
+ return class, prototype
+end
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/frontend.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/frontend.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,83 @@
+-- main.lua
+--
+
+local addonName, addonTable = ...
+
+
+local DependencyLoader = LibStub("AceAddon-3.0"):NewAddon(addonName)
+_G[addonName] = DependencyLoader
+
+local libPrint = LibStub("LibPrint-1.0")
+DependencyLoader.printStream = libPrint:NewStream("DependencyLoader", "DpLdr", print)
+DependencyLoader.debugStream = libPrint:NewStream("DependencyLoader", "DpLdr", "mcm")
+
+-- temp
+DependencyLoader.classes = addonTable.classes
+
+function DependencyLoader:Print(...)
+ self.printStream:Print(...)
+end
+
+
+function DependencyLoader:Debug(...)
+ self.debugStream:Print(...)
+end
+
+
+function DependencyLoader:OnInitialize()
+ self:Debug("Initializing and enabling", addonName)
+ self:Enable()
+end
+
+
+function DependencyLoader:OnEnable()
+ self.core = addonTable.classes.Core:New()
+
+ self:Print("Enabled", addonName)
+
+ self:FixCurrentAddons()
+end
+
+function DependencyLoader:OnDisable()
+ self:Print("Disabled", addonName)
+end
+
+
+-- TODO: move this into core?
+
+-- Enables any dependencies needed by the addons
+-- that have already been enabled
+function DependencyLoader:FixCurrentAddons()
+ local core = self.core
+
+ for i=1, GetNumAddOns() do
+ local addon = self.core:GetAddon(i)
+
+ if addon:IsEnabled() then
+ if addon:IsLoaded() then
+ -- TODO: it might still help to enable its embeds
+ else
+ self:Debug("Checking", addon:GetName())
+
+ if addonTable.classes.Core:ForceLoadAvailable() then
+ if core:CanForceLoadTree(addon:GetName()) then
+ core:ForceLoadTree(addon:GetName())
+ else
+ print("Can't force load", addon:GetName())
+ end
+ else
+ if core:CanLoDTree(addon:GetName()) then
+ core:PrepareLoDTree(addon:GetName())
+ else
+ print("Couldn't load", addon:GetName(), "on demand.")
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/start.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/start.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,21 @@
+-- start.lua
+-- Initializes components of DependencyLoader
+
+
+local addonName, addonTable = ...
+
+
+print( string.format([[running %s\start.lua]], addonName) )
+
+-- NOTE: We don't have to check if this module's dependencies
+-- are available: it lists them normally in its .toc file and
+-- the bootstrap module will take care of enabling them first.
+
+-- prepare output functions
+local lp = LibStub("LibPrint-1.0")
+local printStream = lp:NewStream("DependencyLoader", "DepLoader", "DepLdr", print)
+local debugStream = lp:NewStream("DependencyLoader", "DepLoader", "DepLdr", "mcm")
+
+addonTable.print = function(...) printStream:Print(...) end
+addonTable.debug = function(...) debugStream:Print(...) end
+
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Core/start.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/start.xml Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,1 @@
+
\ No newline at end of file
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Errata/DependencyLoader_Errata.toc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Errata/DependencyLoader_Errata.toc Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,10 @@
+## Interface: 40000
+
+## Author: mckenziemc
+
+## Dependencies: DependencyLoader
+
+## LoadOnDemand: 1
+
+
+start.xml
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Errata/main.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Errata/main.lua Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,1 @@
+print("running DependencyLoader_Errata\main.lua")
diff -r 000000000000 -r 9852fcd5e59e DependencyLoader_Errata/start.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Errata/start.xml Tue Nov 30 16:13:04 2010 -0800
@@ -0,0 +1,3 @@
+
+
+