annotate LibModuleDBShare-1.0/LibModuleDBShare-1.0.lua @ 37:f971130a84bb v1.2 release

Timestamps should now be recorded even if dual spec is not used. Profile options tables should now update immediately when dual spec is enabled. Incremented minor version number.
author Andrew Knoll <andrewtknoll@gmail.com>
date Thu, 04 Apr 2013 20:10:20 -0400
parents 328df380892c
children c6d1b0d7f8f9
rev   line source
andrewtknoll@33 1 --- **LibModuleDBShare-1.0** provides a shared profile manager for addons without a central core.
andrewtknoll@33 2 -- A basic options panel for the group is added to the Blizzard options panel, as well as a
andrewtknoll@33 3 -- standard profile manager as a subpanel. Changes through the profiles panel are propagated
andrewtknoll@33 4 -- to member databases. The root panel can be used as a parent for your module config panels,
andrewtknoll@33 5 -- to keep all your addon's config in one place. The root panel's name is the same as the group's
andrewtknoll@33 6 -- name.\\
andrewtknoll@33 7 -- \\
andrewtknoll@35 8 -- A group can be created using the ':NewGroup' library method. The returned object inherits all
andrewtknoll@35 9 -- methods of the DBGroup object described below.\\
andrewtknoll@33 10 -- \\
andrewtknoll@33 11 -- **LibDualSpec Support**\\
andrewtknoll@33 12 -- LibModuleDBShare can use LibDualSpec to manage automatic profile switching with talent spec
andrewtknoll@33 13 -- changes. This integration is handled by the library; there is no need to use LibDualSpec
andrewtknoll@33 14 -- on member databases directly.
>@5 15 --
>@5 16 -- @usage
andrewtknoll@33 17 -- local database;
andrewtknoll@33 18 -- -- this function is called after the ADDON_LOADED event fires
andrewtknoll@33 19 -- function initializeDB()
andrewtknoll@33 20 -- database = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true);
andrewtknoll@33 21 -- local group = LibStub("LibModuleDBShare-1.0"):GetGroup("Group Name");
andrewtknoll@33 22 -- if not group then
andrewtknoll@33 23 -- group = LibStub("LibModuleDBShare-1.0"):NewGroup("Group Name", "A description for this group.", database);
andrewtknoll@33 24 -- else
andrewtknoll@33 25 -- group:AddDB(database);
andrewtknoll@33 26 -- end
andrewtknoll@33 27 -- end
>@5 28 -- @class file
andrewtknoll@33 29 -- @name LibModuleDBShare-1.0
andrewtknoll@37 30 local MAJOR, MINOR = "LibModuleDBShare-1.0", 4
>@3 31 local LibModuleDBShare, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
>@3 32
>@4 33 if not LibModuleDBShare then return end -- No upgrade needed
>@4 34
andrewtknoll@29 35 -- Lua functions
andrewtknoll@26 36 local error, type, pairs, time = error, type, pairs, time;
andrewtknoll@15 37
andrewtknoll@17 38 -- Required Libraries
@12 39 local AceDB = LibStub("AceDB-3.0");
andrewtknoll@17 40 local AceDBOptions = LibStub("AceDBOptions-3.0");
andrewtknoll@17 41 local AceConfigRegistry = LibStub("AceConfigRegistry-3.0");
andrewtknoll@17 42 local AceConfigDialog = LibStub("AceConfigDialog-3.0");
@12 43
andrewtknoll@28 44 -- Optional Libraries
andrewtknoll@28 45 local LibDualSpec = LibStub("LibDualSpec-1.0", true);
andrewtknoll@28 46
>@4 47 LibModuleDBShare.groups = LibModuleDBShare.groups or {};
>@4 48
>@5 49 local DBGroup = {};
>@5 50
>@5 51 --- Creates a new DB group.
andrewtknoll@33 52 -- @param groupName The name of the new DB group, as shown in the options panel.
andrewtknoll@21 53 -- @param groupDescription A description of the group to be shown in the root options panel.
andrewtknoll@21 54 -- @param initialDB The first DB to add to the group.
andrewtknoll@28 55 -- @param usesDualSpec True if this group should use LibDualSpec, false otherwise.
>@5 56 -- @usage
andrewtknoll@33 57 -- local myDB = LibStub("AceDB-3.0"):New("MySavedVar");
andrewtknoll@33 58 -- local myAddonDBGroup = LibStub("LibModuleDBShare-1.0"):NewGroup("MyAddonGroupName", "MyDescription", myDB, true)
>@5 59 -- @return the new DB group object
andrewtknoll@33 60 -- @name LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB[, usesDualSpec]);
andrewtknoll@21 61 function LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec)
andrewtknoll@35 62 -- check to see if LibDualSpec has been loaded
andrewtknoll@35 63 if not LibDualSpec then
andrewtknoll@35 64 LibDualSpec = LibStub("LibDualSpec-1.0", true);
andrewtknoll@35 65 end
andrewtknoll@21 66 -- verify parameters
andrewtknoll@26 67 if type(groupName) ~= "string" then
andrewtknoll@26 68 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'groupName' must be a string.", 2);
andrewtknoll@26 69 elseif type(groupDescription) ~= "string" then
andrewtknoll@26 70 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'groupDescription' must be a string.", 2);
andrewtknoll@26 71 elseif type(LibModuleDBShare.groups[groupName]) ~= "nil" then
andrewtknoll@31 72 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): group '"..groupName.."' already exists.", 2);
andrewtknoll@27 73 elseif type(initialDB) ~= "table" or not AceDB.db_registry[initialDB] then
andrewtknoll@31 74 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'initialDB' must be an AceDB-3.0 database.", 2);
andrewtknoll@31 75 elseif initialDB.parent then
andrewtknoll@31 76 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'initialDB' must not be a namespace.", 2)
andrewtknoll@28 77 elseif type(usesDualSpec) ~= "boolean" and type(usesDualSpec) ~= "nil" then
andrewtknoll@31 78 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'usesDualSpec' must be a boolean or nil.", 2);
andrewtknoll@28 79 elseif usesDualSpec and not LibDualSpec then
andrewtknoll@31 80 error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'usesDualSpec' cannot be true without LibDualSpec-1.0 installed.", 2);
andrewtknoll@26 81 end
andrewtknoll@21 82 -- create group
@12 83 local group = {}
@12 84 group.name = groupName;
andrewtknoll@21 85 group.members = {};
andrewtknoll@21 86 -- create root option panel for group
andrewtknoll@17 87 group.rootOptionsTable = {
andrewtknoll@17 88 type = "group",
andrewtknoll@17 89 name = groupName,
andrewtknoll@17 90 args = {
andrewtknoll@17 91 text = {
andrewtknoll@17 92 type = "description",
andrewtknoll@21 93 name = groupDescription,
andrewtknoll@17 94 },
andrewtknoll@17 95 },
andrewtknoll@17 96 };
andrewtknoll@17 97 AceConfigRegistry:RegisterOptionsTable(groupName, group.rootOptionsTable);
andrewtknoll@17 98 AceConfigDialog:AddToBlizOptions(groupName);
andrewtknoll@21 99 -- create sync DB and profile options page
@12 100 group.syncDBTable = {};
andrewtknoll@21 101 group.syncDB = AceDB:New(group.syncDBTable, nil, initialDB:GetCurrentProfile());
andrewtknoll@17 102 group.profileOptionsTable = AceDBOptions:GetOptionsTable(group.syncDB, false);
andrewtknoll@28 103 if usesDualSpec then
andrewtknoll@29 104 group.usesDualSpec = true;
andrewtknoll@28 105 LibDualSpec:EnhanceDatabase(group.syncDB, groupName);
andrewtknoll@28 106 LibDualSpec:EnhanceOptions(group.profileOptionsTable, group.syncDB);
andrewtknoll@29 107 else
andrewtknoll@29 108 group.usesDualSpec = false;
andrewtknoll@28 109 end
andrewtknoll@17 110 AceConfigRegistry:RegisterOptionsTable(groupName.."Profiles", group.profileOptionsTable);
andrewtknoll@18 111 AceConfigDialog:AddToBlizOptions(groupName.."Profiles", group.profileOptionsTable.name, groupName);
andrewtknoll@21 112 -- add all profiles from initialDB to syncDB
andrewtknoll@21 113 for i, profile in pairs(initialDB:GetProfiles()) do
andrewtknoll@21 114 group.syncDB:SetProfile(profile);
andrewtknoll@21 115 end
andrewtknoll@28 116 -- load profile info from initialDB
andrewtknoll@21 117 group.syncDB:SetProfile(initialDB:GetCurrentProfile());
andrewtknoll@27 118 group.members[initialDB] = initialDB:GetNamespace(MAJOR, true) or initialDB:RegisterNamespace(MAJOR);
andrewtknoll@29 119 local storedData = group.members[initialDB].char;
andrewtknoll@29 120 if type(storedData.logoutTimestamp) == "number" then
andrewtknoll@29 121 group.profileTimestamp = storedData.logoutTimestamp;
andrewtknoll@22 122 else
andrewtknoll@22 123 group.profileTimestamp = 0;
andrewtknoll@22 124 end
andrewtknoll@35 125 if usesDualSpec then
andrewtknoll@35 126 local LDSnamespace = group.syncDB:GetNamespace("LibDualSpec-1.0");
andrewtknoll@35 127 LDSnamespace.char.enabled = storedData.dualSpecEnabled;
andrewtknoll@35 128 LDSnamespace.char.profile = storedData.altProfile;
andrewtknoll@35 129 LDSnamespace.char.specGroup = storedData.activeSpecGroup;
andrewtknoll@30 130 group.syncDB:CheckDualSpecState();
andrewtknoll@35 131 else
andrewtknoll@35 132 group.syncDB.char.enabled = storedData.dualSpecEnabled;
andrewtknoll@35 133 group.syncDB.char.profile = storedData.altProfile;
andrewtknoll@35 134 group.syncDB.char.specGroup = storedData.activeSpecGroup;
andrewtknoll@28 135 end
andrewtknoll@21 136 -- add methods and callbacks
@12 137 for k, v in pairs(DBGroup) do
@12 138 group[k] = v;
@12 139 end
andrewtknoll@19 140 group.syncDB.RegisterCallback(group, "OnProfileChanged", "OnProfileChanged");
andrewtknoll@19 141 group.syncDB.RegisterCallback(group, "OnProfileDeleted", "OnProfileDeleted");
andrewtknoll@19 142 group.syncDB.RegisterCallback(group, "OnProfileCopied", "OnProfileCopied");
andrewtknoll@19 143 group.syncDB.RegisterCallback(group, "OnProfileReset", "OnProfileReset");
andrewtknoll@28 144 group.syncDB.RegisterCallback(group, "OnDatabaseShutdown", "OnSyncShutdown");
andrewtknoll@28 145 initialDB.RegisterCallback(group, "OnDatabaseShutdown", "OnMemberShutdown");
andrewtknoll@20 146 group.squelchCallbacks = false;
andrewtknoll@19 147 LibModuleDBShare.groups[groupName] = group;
@12 148 return group;
>@4 149 end
>@4 150
>@5 151 --- Retrieves an existing DB group.
>@5 152 -- @param groupName The name of the DB group to retrieve.
>@5 153 -- @usage
>@5 154 -- local myAddonDBGroup = LibStub("LibModuleDBShare-1.0"):GetGroup("MyAddonGroupName")
andrewtknoll@35 155 -- @return the DB group object, or ##nil## if not found
>@5 156 function LibModuleDBShare:GetGroup(groupName)
andrewtknoll@27 157 if type(groupName) ~= "string" then
andrewtknoll@27 158 error("Usage: LibModuleDBShare:GetGroup(groupName): 'groupName' must be a string.", 2);
andrewtknoll@27 159 end
@12 160 return LibModuleDBShare.groups[groupName];
>@4 161 end
>@5 162
>@5 163 --- Adds a database to the group.
andrewtknoll@22 164 -- @param newDB The database to add.
>@5 165 -- @usage
>@5 166 -- myAddonDBGroup:AddDB(MyAddon.db)
andrewtknoll@22 167 function DBGroup:AddDB(newDB)
andrewtknoll@22 168 -- verify parameters
andrewtknoll@26 169 if type(newDB) ~= "table" or not AceDB.db_registry[newDB] then
andrewtknoll@31 170 error("Usage: DBGroup:AddDB(newDB): 'newDB' must be an AceDB-3.0 database.", 2);
andrewtknoll@31 171 elseif newDB.parent then
andrewtknoll@31 172 error("Usage: DBGroup:AddDB(newDB): 'newDB' must not be a namespace.", 2)
andrewtknoll@26 173 elseif type(self.members[newDB]) ~= "nil" then
andrewtknoll@31 174 error("Usage: DBGroup:AddDB(newDB): 'newDB' is already a member of DBGroup.", 2);
andrewtknoll@31 175 end
andrewtknoll@31 176 for groupName, group in pairs(LibModuleDBShare.groups) do
andrewtknoll@31 177 if group.members[newDB] ~= nil then
andrewtknoll@31 178 error("Usage: DBGroup:AddDB(newDB): 'newDB' is already a member of group '"..groupName.."'.", 2);
andrewtknoll@31 179 end
andrewtknoll@26 180 end
andrewtknoll@22 181 -- record current profile
andrewtknoll@20 182 local syncProfile = self.syncDB:GetCurrentProfile();
andrewtknoll@22 183 -- add new profiles to syncDB
andrewtknoll@20 184 self.squelchCallbacks = true;
andrewtknoll@22 185 for i, profile in pairs(newDB:GetProfiles()) do
andrewtknoll@20 186 self.syncDB:SetProfile(profile);
andrewtknoll@20 187 end
andrewtknoll@22 188 -- set current profile based on timestamps
andrewtknoll@27 189 local namespace = newDB:GetNamespace(MAJOR, true) or newDB:RegisterNamespace(MAJOR);
andrewtknoll@29 190 local storedData = namespace.char;
andrewtknoll@29 191 if type(storedData.logoutTimestamp) == "number" and storedData.logoutTimestamp > self.profileTimestamp then
andrewtknoll@22 192 self.squelchCallbacks = false;
andrewtknoll@22 193 self.syncDB:SetProfile(newDB:GetCurrentProfile());
andrewtknoll@29 194 self.profileTimestamp = storedData.logoutTimestamp;
andrewtknoll@35 195 if self.usesDualSpec and storedData.altProfile then
andrewtknoll@35 196 local LDSnamespace = group.syncDB:GetNamespace("LibDualSpec-1.0");
andrewtknoll@35 197 LDSnamespace.char.enabled = storedData.dualSpecEnabled;
andrewtknoll@35 198 LDSnamespace.char.profile = storedData.altProfile;
andrewtknoll@35 199 LDSnamespace.char.specGroup = storedData.activeSpecGroup;
andrewtknoll@30 200 group.syncDB:CheckDualSpecState();
andrewtknoll@35 201 elseif storedData.altProfile then
andrewtknoll@35 202 self.syncDB.char.enabled = storedData.dualSpecEnabled;
andrewtknoll@35 203 self.syncDB.char.profile = storedData.altProfile;
andrewtknoll@35 204 self.syncDB.char.specGroup = storedData.activeSpecGroup;
andrewtknoll@28 205 end
andrewtknoll@20 206 else
andrewtknoll@20 207 self.syncDB:SetProfile(syncProfile);
andrewtknoll@22 208 newDB:SetProfile(syncProfile);
andrewtknoll@22 209 self.squelchCallbacks = false;
andrewtknoll@20 210 end
andrewtknoll@22 211 -- add to members list
andrewtknoll@27 212 self.members[newDB] = namespace;
andrewtknoll@28 213 newDB.RegisterCallback(self, "OnDatabaseShutdown", "OnMemberShutdown");
>@5 214 end
andrewtknoll@18 215
andrewtknoll@35 216 --- Checks to see if this group uses LibDualSpec.
andrewtknoll@35 217 -- @return ##true## if this group uses LibDualSpec, ##false## otherwise
andrewtknoll@35 218 function DBGroup:IsUsingDualSpec()
andrewtknoll@35 219 return self.usesDualSpec;
andrewtknoll@35 220 end
andrewtknoll@35 221
andrewtknoll@35 222 --- Enables dual spec support if not already enabled.
andrewtknoll@35 223 function DBGroup:EnableDualSpec()
andrewtknoll@35 224 if not LibDualSpec then
andrewtknoll@35 225 LibDualSpec = LibStub("LibDualSpec-1.0"); -- this will error if LDS isn't found
andrewtknoll@35 226 end
andrewtknoll@35 227 if not self.usesDualSpec then
andrewtknoll@35 228 LibDualSpec:EnhanceDatabase(self.syncDB, self.name);
andrewtknoll@35 229 LibDualSpec:EnhanceOptions(self.profileOptionsTable, self.syncDB);
andrewtknoll@37 230 AceConfigRegistry:NotifyChange(self.name.."Profiles");
andrewtknoll@35 231 self.usesDualSpec = true;
andrewtknoll@35 232 local namespace = self.syncDB:GetNamespace("LibDualSpec-1.0");
andrewtknoll@35 233 namespace.char.enabled = self.syncDB.char.enabled;
andrewtknoll@35 234 namespace.char.profile = self.syncDB.char.profile;
andrewtknoll@35 235 namespace.char.specGroup = self.syncDB.char.specGroup;
andrewtknoll@35 236 self.syncDB:CheckDualSpecState();
andrewtknoll@35 237 end
andrewtknoll@35 238 end
andrewtknoll@35 239
andrewtknoll@19 240 -- callback handlers (new profiles are handled by OnProfileChanged)
andrewtknoll@19 241
andrewtknoll@24 242 function DBGroup:OnProfileChanged(callback, syncDB, profile)
andrewtknoll@24 243 if not self.squelchCallbacks then
andrewtknoll@24 244 for db, _ in pairs(self.members) do
andrewtknoll@25 245 db:SetProfile(profile);
andrewtknoll@24 246 end
andrewtknoll@24 247 end
andrewtknoll@18 248 end
andrewtknoll@18 249
andrewtknoll@24 250 function DBGroup:OnProfileDeleted(callback, syncDB, profile)
andrewtknoll@24 251 for db, _ in pairs(self.members) do
andrewtknoll@24 252 db:DeleteProfile(profile, true);
andrewtknoll@24 253 end
andrewtknoll@18 254 end
andrewtknoll@18 255
andrewtknoll@24 256 function DBGroup:OnProfileCopied(callback, syncDB, profile)
andrewtknoll@24 257 for db, _ in pairs(self.members) do
andrewtknoll@24 258 db:CopyProfile(profile, true);
andrewtknoll@24 259 end
andrewtknoll@18 260 end
andrewtknoll@18 261
andrewtknoll@24 262 function DBGroup:OnProfileReset(callback, syncDB)
andrewtknoll@24 263 for db, _ in pairs(self.members) do
andrewtknoll@24 264 db:ResetProfile(false, false);
andrewtknoll@24 265 end
andrewtknoll@18 266 end
andrewtknoll@23 267
andrewtknoll@29 268 local altProfile = nil;
andrewtknoll@29 269 local dualSpecEnabled = nil;
andrewtknoll@29 270 local activeSpecGroup = nil;
andrewtknoll@28 271
andrewtknoll@28 272 function DBGroup:OnSyncShutdown(callback, syncDB)
andrewtknoll@29 273 if self.usesDualSpec and not altProfile then
andrewtknoll@29 274 altProfile = syncDB:GetDualSpecProfile();
andrewtknoll@29 275 dualSpecEnabled = syncDB:IsDualSpecEnabled();
andrewtknoll@29 276 activeSpecGroup = GetActiveSpecGroup();
andrewtknoll@28 277 end
andrewtknoll@28 278 end
andrewtknoll@28 279
andrewtknoll@24 280 local timestamp = nil;
andrewtknoll@24 281
andrewtknoll@28 282 function DBGroup:OnMemberShutdown(callback, db)
andrewtknoll@27 283 if not timestamp then -- ensure uniform timestamps to minimize
andrewtknoll@27 284 timestamp = time(); -- calls to SetProfile in NewGroup
andrewtknoll@24 285 end
andrewtknoll@37 286 self.members[db].char.logoutTimestamp = timestamp;
andrewtknoll@29 287 if self.usesDualSpec then
andrewtknoll@29 288 if not altProfile then
andrewtknoll@29 289 altProfile = self.syncDB:GetDualSpecProfile();
andrewtknoll@29 290 dualSpecEnabled = self.syncDB:IsDualSpecEnabled();
andrewtknoll@29 291 activeSpecGroup = GetActiveSpecGroup();
andrewtknoll@29 292 end
andrewtknoll@29 293 self.members[db].char.altProfile = altProfile;
andrewtknoll@29 294 self.members[db].char.dualSpecEnabled = dualSpecEnabled;
andrewtknoll@29 295 self.members[db].char.activeSpecGroup = activeSpecGroup;
andrewtknoll@28 296 end
andrewtknoll@23 297 end
andrewtknoll@33 298
andrewtknoll@33 299 -- update existing groups
andrewtknoll@33 300 for groupName, group in pairs(LibModuleDBShare.groups) do
andrewtknoll@33 301 for funcName, func in pairs(DBGroup) do
andrewtknoll@33 302 group[funcName] = func;
andrewtknoll@33 303 end
andrewtknoll@33 304 end