annotate lib/AceLibrary/AceLibrary.lua @ 22:1b9323256a1b

Merging in 1.0 dev tree
author Flick <flickerstreak@gmail.com>
date Fri, 07 Mar 2008 22:10:55 +0000
parents libs/AceLibrary/AceLibrary.lua@f920db5fc6b1
children
rev   line source
flickerstreak@1 1 --[[
flickerstreak@1 2 Name: AceLibrary
flickerstreak@22 3 Revision: $Rev: 49421 $
flickerstreak@1 4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
flickerstreak@1 5 Inspired By: Iriel (iriel@vigilance-committee.org)
flickerstreak@1 6 Tekkub (tekkub@gmail.com)
flickerstreak@22 7 Revision: $Rev: 49421 $
flickerstreak@1 8 Website: http://www.wowace.com/
flickerstreak@1 9 Documentation: http://www.wowace.com/index.php/AceLibrary
flickerstreak@1 10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
flickerstreak@1 11 Description: Versioning library to handle other library instances, upgrading,
flickerstreak@1 12 and proper access.
flickerstreak@1 13 It also provides a base for libraries to work off of, providing
flickerstreak@1 14 proper error tools. It is handy because all the errors occur in the
flickerstreak@1 15 file that called it, not in the library file itself.
flickerstreak@1 16 Dependencies: None
flickerstreak@7 17 License: LGPL v2.1
flickerstreak@1 18 ]]
flickerstreak@1 19
flickerstreak@1 20 local ACELIBRARY_MAJOR = "AceLibrary"
flickerstreak@22 21 local ACELIBRARY_MINOR = "$Revision: 49421 $"
flickerstreak@1 22
flickerstreak@1 23 local _G = getfenv(0)
flickerstreak@1 24 local previous = _G[ACELIBRARY_MAJOR]
flickerstreak@1 25 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
flickerstreak@1 26
flickerstreak@22 27 do
flickerstreak@22 28 -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
flickerstreak@22 29 -- LibStub is hereby placed in the Public Domain -- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
flickerstreak@22 30 local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
flickerstreak@22 31 local LibStub = _G[LIBSTUB_MAJOR]
flickerstreak@22 32
flickerstreak@22 33 if not LibStub or LibStub.minor < LIBSTUB_MINOR then
flickerstreak@22 34 LibStub = LibStub or {libs = {}, minors = {} }
flickerstreak@22 35 _G[LIBSTUB_MAJOR] = LibStub
flickerstreak@22 36 LibStub.minor = LIBSTUB_MINOR
flickerstreak@22 37
flickerstreak@22 38 function LibStub:NewLibrary(major, minor)
flickerstreak@22 39 assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
flickerstreak@22 40 minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
flickerstreak@22 41 local oldminor = self.minors[major]
flickerstreak@22 42 if oldminor and oldminor >= minor then return nil end
flickerstreak@22 43 self.minors[major], self.libs[major] = minor, self.libs[major] or {}
flickerstreak@22 44 return self.libs[major], oldminor
flickerstreak@22 45 end
flickerstreak@22 46
flickerstreak@22 47 function LibStub:GetLibrary(major, silent)
flickerstreak@22 48 if not self.libs[major] and not silent then
flickerstreak@22 49 error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
flickerstreak@22 50 end
flickerstreak@22 51 return self.libs[major], self.minors[major]
flickerstreak@22 52 end
flickerstreak@22 53
flickerstreak@22 54 function LibStub:IterateLibraries() return pairs(self.libs) end
flickerstreak@22 55 setmetatable(LibStub, { __call = LibStub.GetLibrary })
flickerstreak@22 56 end
flickerstreak@22 57 end
flickerstreak@22 58 local LibStub = _G.LibStub
flickerstreak@22 59
flickerstreak@22 60 -- If you don't want AceLibrary to enable libraries that are LoadOnDemand but
flickerstreak@22 61 -- disabled in the addon screen, set this to true.
flickerstreak@22 62 local DONT_ENABLE_LIBRARIES = nil
flickerstreak@22 63
flickerstreak@1 64 local function safecall(func,...)
flickerstreak@1 65 local success, err = pcall(func,...)
flickerstreak@7 66 if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end
flickerstreak@1 67 end
flickerstreak@1 68
flickerstreak@22 69 local WoW22 = false
flickerstreak@22 70 if type(GetBuildInfo) == "function" then
flickerstreak@22 71 local success, buildinfo = pcall(GetBuildInfo)
flickerstreak@22 72 if success and type(buildinfo) == "string" then
flickerstreak@22 73 local num = tonumber(buildinfo:match("^(%d+%.%d+)"))
flickerstreak@22 74 if num and num >= 2.2 then
flickerstreak@22 75 WoW22 = true
flickerstreak@22 76 end
flickerstreak@22 77 end
flickerstreak@22 78 end
flickerstreak@22 79
flickerstreak@1 80 -- @table AceLibrary
flickerstreak@1 81 -- @brief System to handle all versioning of libraries.
flickerstreak@1 82 local AceLibrary = {}
flickerstreak@1 83 local AceLibrary_mt = {}
flickerstreak@1 84 setmetatable(AceLibrary, AceLibrary_mt)
flickerstreak@1 85
flickerstreak@1 86 local function error(self, message, ...)
flickerstreak@1 87 if type(self) ~= "table" then
flickerstreak@7 88 return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2)
flickerstreak@1 89 end
flickerstreak@1 90
flickerstreak@1 91 local stack = debugstack()
flickerstreak@1 92 if not message then
flickerstreak@22 93 local second = stack:match("\n(.-)\n")
flickerstreak@1 94 message = "error raised! " .. second
flickerstreak@1 95 else
flickerstreak@7 96 local arg = { ... } -- not worried about table creation, as errors don't happen often
flickerstreak@1 97
flickerstreak@1 98 for i = 1, #arg do
flickerstreak@1 99 arg[i] = tostring(arg[i])
flickerstreak@1 100 end
flickerstreak@1 101 for i = 1, 10 do
flickerstreak@1 102 table.insert(arg, "nil")
flickerstreak@1 103 end
flickerstreak@7 104 message = message:format(unpack(arg))
flickerstreak@1 105 end
flickerstreak@1 106
flickerstreak@1 107 if getmetatable(self) and getmetatable(self).__tostring then
flickerstreak@7 108 message = ("%s: %s"):format(tostring(self), message)
flickerstreak@1 109 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
flickerstreak@7 110 message = ("%s: %s"):format(self:GetLibraryVersion(), message)
flickerstreak@1 111 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
flickerstreak@7 112 message = ("%s: %s"):format(self.class:GetLibraryVersion(), message)
flickerstreak@1 113 end
flickerstreak@1 114
flickerstreak@7 115 local first = stack:gsub("\n.*", "")
flickerstreak@7 116 local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1")
flickerstreak@7 117 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
flickerstreak@1 118
flickerstreak@1 119
flickerstreak@1 120 local i = 0
flickerstreak@7 121 for s in stack:gmatch("\n([^\n]*)") do
flickerstreak@1 122 i = i + 1
flickerstreak@7 123 if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
flickerstreak@7 124 file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1")
flickerstreak@7 125 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
flickerstreak@1 126 break
flickerstreak@1 127 end
flickerstreak@1 128 end
flickerstreak@1 129 local j = 0
flickerstreak@7 130 for s in stack:gmatch("\n([^\n]*)") do
flickerstreak@1 131 j = j + 1
flickerstreak@7 132 if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
flickerstreak@1 133 return _G.error(message, j+1)
flickerstreak@1 134 end
flickerstreak@1 135 end
flickerstreak@1 136 return _G.error(message, 2)
flickerstreak@1 137 end
flickerstreak@1 138
flickerstreak@22 139 local assert
flickerstreak@22 140 if not WoW22 then
flickerstreak@22 141 function assert(self, condition, message, ...)
flickerstreak@22 142 if not condition then
flickerstreak@22 143 if not message then
flickerstreak@22 144 local stack = debugstack()
flickerstreak@22 145 local second = stack:match("\n(.-)\n")
flickerstreak@22 146 message = "assertion failed! " .. second
flickerstreak@22 147 end
flickerstreak@22 148 return error(self, message, ...)
flickerstreak@1 149 end
flickerstreak@22 150 return condition
flickerstreak@1 151 end
flickerstreak@1 152 end
flickerstreak@1 153
flickerstreak@22 154 local type = type
flickerstreak@1 155 local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
flickerstreak@1 156 if type(num) ~= "number" then
flickerstreak@1 157 return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
flickerstreak@1 158 elseif type(kind) ~= "string" then
flickerstreak@1 159 return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
flickerstreak@1 160 end
flickerstreak@1 161 arg = type(arg)
flickerstreak@1 162 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
flickerstreak@7 163 local stack = debugstack()
flickerstreak@22 164 local func = stack:match("`argCheck'.-([`<].-['>])")
flickerstreak@1 165 if not func then
flickerstreak@22 166 func = stack:match("([`<].-['>])")
flickerstreak@1 167 end
flickerstreak@1 168 if kind5 then
flickerstreak@1 169 return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
flickerstreak@1 170 elseif kind4 then
flickerstreak@1 171 return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
flickerstreak@1 172 elseif kind3 then
flickerstreak@1 173 return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
flickerstreak@1 174 elseif kind2 then
flickerstreak@1 175 return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
flickerstreak@1 176 else
flickerstreak@1 177 return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
flickerstreak@1 178 end
flickerstreak@1 179 end
flickerstreak@1 180 end
flickerstreak@1 181
flickerstreak@1 182 local pcall
flickerstreak@1 183 do
flickerstreak@1 184 local function check(self, ret, ...)
flickerstreak@1 185 if not ret then
flickerstreak@7 186 local s = ...
flickerstreak@7 187 return error(self, (s:gsub(".-%.lua:%d-: ", "")))
flickerstreak@1 188 else
flickerstreak@1 189 return ...
flickerstreak@1 190 end
flickerstreak@1 191 end
flickerstreak@1 192
flickerstreak@1 193 function pcall(self, func, ...)
flickerstreak@1 194 return check(self, _G.pcall(func, ...))
flickerstreak@1 195 end
flickerstreak@1 196 end
flickerstreak@1 197
flickerstreak@1 198 local recurse = {}
flickerstreak@1 199 local function addToPositions(t, major)
flickerstreak@1 200 if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
flickerstreak@1 201 rawset(t, recurse, true)
flickerstreak@1 202 AceLibrary.positions[t] = major
flickerstreak@1 203 for k,v in pairs(t) do
flickerstreak@1 204 if type(v) == "table" and not rawget(v, recurse) then
flickerstreak@1 205 addToPositions(v, major)
flickerstreak@1 206 end
flickerstreak@1 207 if type(k) == "table" and not rawget(k, recurse) then
flickerstreak@1 208 addToPositions(k, major)
flickerstreak@1 209 end
flickerstreak@1 210 end
flickerstreak@1 211 local mt = getmetatable(t)
flickerstreak@1 212 if mt and not rawget(mt, recurse) then
flickerstreak@1 213 addToPositions(mt, major)
flickerstreak@1 214 end
flickerstreak@1 215 rawset(t, recurse, nil)
flickerstreak@1 216 end
flickerstreak@1 217 end
flickerstreak@1 218
flickerstreak@1 219 local function svnRevisionToNumber(text)
flickerstreak@22 220 local kind = type(text)
flickerstreak@22 221 if kind == "number" or tonumber(text) then
flickerstreak@22 222 return tonumber(text)
flickerstreak@22 223 elseif kind == "string" then
flickerstreak@7 224 if text:find("^%$Revision: (%d+) %$$") then
flickerstreak@22 225 return tonumber((text:match("^%$Revision: (%d+) %$$")))
flickerstreak@7 226 elseif text:find("^%$Rev: (%d+) %$$") then
flickerstreak@22 227 return tonumber((text:match("^%$Rev: (%d+) %$$")))
flickerstreak@7 228 elseif text:find("^%$LastChangedRevision: (%d+) %$$") then
flickerstreak@22 229 return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$")))
flickerstreak@1 230 end
flickerstreak@1 231 end
flickerstreak@1 232 return nil
flickerstreak@1 233 end
flickerstreak@1 234
flickerstreak@1 235 local crawlReplace
flickerstreak@1 236 do
flickerstreak@1 237 local recurse = {}
flickerstreak@1 238 local function func(t, to, from)
flickerstreak@1 239 if recurse[t] then
flickerstreak@1 240 return
flickerstreak@1 241 end
flickerstreak@1 242 recurse[t] = true
flickerstreak@1 243 local mt = getmetatable(t)
flickerstreak@1 244 setmetatable(t, nil)
flickerstreak@1 245 rawset(t, to, rawget(t, from))
flickerstreak@1 246 rawset(t, from, nil)
flickerstreak@1 247 for k,v in pairs(t) do
flickerstreak@1 248 if v == from then
flickerstreak@1 249 t[k] = to
flickerstreak@1 250 elseif type(v) == "table" then
flickerstreak@1 251 if not recurse[v] then
flickerstreak@1 252 func(v, to, from)
flickerstreak@1 253 end
flickerstreak@1 254 end
flickerstreak@1 255
flickerstreak@1 256 if type(k) == "table" then
flickerstreak@1 257 if not recurse[k] then
flickerstreak@1 258 func(k, to, from)
flickerstreak@1 259 end
flickerstreak@1 260 end
flickerstreak@1 261 end
flickerstreak@1 262 setmetatable(t, mt)
flickerstreak@1 263 if mt then
flickerstreak@1 264 if mt == from then
flickerstreak@1 265 setmetatable(t, to)
flickerstreak@1 266 elseif not recurse[mt] then
flickerstreak@1 267 func(mt, to, from)
flickerstreak@1 268 end
flickerstreak@1 269 end
flickerstreak@1 270 end
flickerstreak@1 271 function crawlReplace(t, to, from)
flickerstreak@1 272 func(t, to, from)
flickerstreak@1 273 for k in pairs(recurse) do
flickerstreak@1 274 recurse[k] = nil
flickerstreak@1 275 end
flickerstreak@1 276 end
flickerstreak@1 277 end
flickerstreak@1 278
flickerstreak@1 279 -- @function destroyTable
flickerstreak@1 280 -- @brief remove all the contents of a table
flickerstreak@1 281 -- @param t table to destroy
flickerstreak@1 282 local function destroyTable(t)
flickerstreak@1 283 setmetatable(t, nil)
flickerstreak@1 284 for k,v in pairs(t) do
flickerstreak@1 285 t[k] = nil
flickerstreak@1 286 end
flickerstreak@1 287 end
flickerstreak@1 288
flickerstreak@1 289 local function isFrame(frame)
flickerstreak@1 290 return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
flickerstreak@1 291 end
flickerstreak@1 292
flickerstreak@1 293 -- @function copyTable
flickerstreak@1 294 -- @brief Create a shallow copy of a table and return it.
flickerstreak@1 295 -- @param from The table to copy from
flickerstreak@1 296 -- @return A shallow copy of the table
flickerstreak@22 297 local function copyTable(from, to)
flickerstreak@22 298 if not to then
flickerstreak@22 299 to = {}
flickerstreak@22 300 end
flickerstreak@1 301 for k,v in pairs(from) do
flickerstreak@1 302 to[k] = v
flickerstreak@1 303 end
flickerstreak@1 304 setmetatable(to, getmetatable(from))
flickerstreak@1 305 return to
flickerstreak@1 306 end
flickerstreak@1 307
flickerstreak@1 308 -- @function deepTransfer
flickerstreak@1 309 -- @brief Fully transfer all data, keeping proper previous table
flickerstreak@1 310 -- backreferences stable.
flickerstreak@1 311 -- @param to The table with which data is to be injected into
flickerstreak@1 312 -- @param from The table whose data will be injected into the first
flickerstreak@1 313 -- @param saveFields If available, a shallow copy of the basic data is saved
flickerstreak@1 314 -- in here.
flickerstreak@1 315 -- @param list The account of table references
flickerstreak@1 316 -- @param list2 The current status on which tables have been traversed.
flickerstreak@1 317 local deepTransfer
flickerstreak@1 318 do
flickerstreak@1 319 -- @function examine
flickerstreak@1 320 -- @brief Take account of all the table references to be shared
flickerstreak@1 321 -- between the to and from tables.
flickerstreak@1 322 -- @param to The table with which data is to be injected into
flickerstreak@1 323 -- @param from The table whose data will be injected into the first
flickerstreak@1 324 -- @param list An account of the table references
flickerstreak@1 325 local function examine(to, from, list, major)
flickerstreak@1 326 list[from] = to
flickerstreak@1 327 for k,v in pairs(from) do
flickerstreak@1 328 if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
flickerstreak@1 329 if from[k] == to[k] then
flickerstreak@1 330 list[from[k]] = to[k]
flickerstreak@1 331 elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
flickerstreak@1 332 list[from[k]] = from[k]
flickerstreak@1 333 elseif not list[from[k]] then
flickerstreak@1 334 examine(to[k], from[k], list, major)
flickerstreak@1 335 end
flickerstreak@1 336 end
flickerstreak@1 337 end
flickerstreak@1 338 return list
flickerstreak@1 339 end
flickerstreak@1 340
flickerstreak@1 341 function deepTransfer(to, from, saveFields, major, list, list2)
flickerstreak@1 342 setmetatable(to, nil)
flickerstreak@1 343 if not list then
flickerstreak@1 344 list = {}
flickerstreak@1 345 list2 = {}
flickerstreak@1 346 examine(to, from, list, major)
flickerstreak@1 347 end
flickerstreak@1 348 list2[to] = to
flickerstreak@1 349 for k,v in pairs(to) do
flickerstreak@1 350 if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
flickerstreak@1 351 if saveFields then
flickerstreak@1 352 saveFields[k] = v
flickerstreak@1 353 end
flickerstreak@1 354 to[k] = nil
flickerstreak@1 355 elseif v ~= _G then
flickerstreak@1 356 if saveFields then
flickerstreak@1 357 saveFields[k] = copyTable(v)
flickerstreak@1 358 end
flickerstreak@1 359 end
flickerstreak@1 360 end
flickerstreak@1 361 for k in pairs(from) do
flickerstreak@1 362 if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
flickerstreak@1 363 if not list2[to[k]] then
flickerstreak@1 364 deepTransfer(to[k], from[k], nil, major, list, list2)
flickerstreak@1 365 end
flickerstreak@1 366 to[k] = list[to[k]] or list2[to[k]]
flickerstreak@1 367 else
flickerstreak@1 368 rawset(to, k, from[k])
flickerstreak@1 369 end
flickerstreak@1 370 end
flickerstreak@1 371 setmetatable(to, getmetatable(from))
flickerstreak@1 372 local mt = getmetatable(to)
flickerstreak@1 373 if mt then
flickerstreak@1 374 if list[mt] then
flickerstreak@1 375 setmetatable(to, list[mt])
flickerstreak@1 376 elseif mt.__index and list[mt.__index] then
flickerstreak@1 377 mt.__index = list[mt.__index]
flickerstreak@1 378 end
flickerstreak@1 379 end
flickerstreak@1 380 destroyTable(from)
flickerstreak@1 381 end
flickerstreak@1 382 end
flickerstreak@1 383
flickerstreak@22 384 local function TryToEnable(addon)
flickerstreak@22 385 if DONT_ENABLE_LIBRARIES then return end
flickerstreak@22 386 local isondemand = IsAddOnLoadOnDemand(addon)
flickerstreak@22 387 if isondemand then
flickerstreak@22 388 local _, _, _, enabled = GetAddOnInfo(addon)
flickerstreak@22 389 EnableAddOn(addon)
flickerstreak@22 390 local _, _, _, _, loadable = GetAddOnInfo(addon)
flickerstreak@22 391 if not loadable and not enabled then
flickerstreak@22 392 DisableAddOn(addon)
flickerstreak@22 393 end
flickerstreak@22 394
flickerstreak@22 395 return loadable
flickerstreak@22 396 end
flickerstreak@22 397 end
flickerstreak@22 398
flickerstreak@1 399 -- @method TryToLoadStandalone
flickerstreak@1 400 -- @brief Attempt to find and load a standalone version of the requested library
flickerstreak@1 401 -- @param major A string representing the major version
flickerstreak@22 402 -- @return If library is found and loaded, true is return. If not loadable, false is returned.
flickerstreak@1 403 -- If the library has been requested previously, nil is returned.
flickerstreak@1 404 local function TryToLoadStandalone(major)
flickerstreak@1 405 if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
flickerstreak@1 406 if AceLibrary.scannedlibs[major] then return end
flickerstreak@1 407
flickerstreak@1 408 AceLibrary.scannedlibs[major] = true
flickerstreak@1 409
flickerstreak@1 410 local name, _, _, enabled, loadable = GetAddOnInfo(major)
flickerstreak@22 411
flickerstreak@22 412 loadable = (enabled and loadable) or TryToEnable(name)
flickerstreak@22 413
flickerstreak@22 414 local loaded = false
flickerstreak@1 415 if loadable then
flickerstreak@22 416 loaded = true
flickerstreak@22 417 LoadAddOn(name)
flickerstreak@1 418 end
flickerstreak@22 419
flickerstreak@22 420 local field = "X-AceLibrary-" .. major
flickerstreak@22 421 for i = 1, GetNumAddOns() do
flickerstreak@22 422 if GetAddOnMetadata(i, field) then
flickerstreak@22 423 name, _, _, enabled, loadable = GetAddOnInfo(i)
flickerstreak@22 424
flickerstreak@22 425 loadable = (enabled and loadable) or TryToEnable(name)
flickerstreak@1 426 if loadable then
flickerstreak@22 427 loaded = true
flickerstreak@22 428 LoadAddOn(name)
flickerstreak@1 429 end
flickerstreak@1 430 end
flickerstreak@1 431 end
flickerstreak@22 432 return loaded
flickerstreak@1 433 end
flickerstreak@1 434
flickerstreak@1 435 -- @method IsNewVersion
flickerstreak@1 436 -- @brief Obtain whether the supplied version would be an upgrade to the
flickerstreak@1 437 -- current version. This allows for bypass code in library
flickerstreak@1 438 -- declaration.
flickerstreak@1 439 -- @param major A string representing the major version
flickerstreak@1 440 -- @param minor An integer or an svn revision string representing the minor version
flickerstreak@1 441 -- @return whether the supplied version would be newer than what is
flickerstreak@1 442 -- currently available.
flickerstreak@1 443 function AceLibrary:IsNewVersion(major, minor)
flickerstreak@1 444 argCheck(self, major, 2, "string")
flickerstreak@1 445 TryToLoadStandalone(major)
flickerstreak@1 446
flickerstreak@1 447 if type(minor) == "string" then
flickerstreak@1 448 local m = svnRevisionToNumber(minor)
flickerstreak@1 449 if m then
flickerstreak@1 450 minor = m
flickerstreak@1 451 else
flickerstreak@7 452 _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
flickerstreak@1 453 end
flickerstreak@1 454 end
flickerstreak@1 455 argCheck(self, minor, 3, "number")
flickerstreak@22 456 local lib, oldMinor = LibStub:GetLibrary(major, true)
flickerstreak@22 457 if lib then
flickerstreak@22 458 return oldMinor < minor
flickerstreak@22 459 end
flickerstreak@1 460 local data = self.libs[major]
flickerstreak@1 461 if not data then
flickerstreak@1 462 return true
flickerstreak@1 463 end
flickerstreak@1 464 return data.minor < minor
flickerstreak@1 465 end
flickerstreak@1 466
flickerstreak@1 467 -- @method HasInstance
flickerstreak@1 468 -- @brief Returns whether an instance exists. This allows for optional support of a library.
flickerstreak@1 469 -- @param major A string representing the major version.
flickerstreak@1 470 -- @param minor (optional) An integer or an svn revision string representing the minor version.
flickerstreak@1 471 -- @return Whether an instance exists.
flickerstreak@1 472 function AceLibrary:HasInstance(major, minor)
flickerstreak@1 473 argCheck(self, major, 2, "string")
flickerstreak@22 474 if minor ~= false then
flickerstreak@22 475 TryToLoadStandalone(major)
flickerstreak@22 476 end
flickerstreak@22 477
flickerstreak@22 478 local lib, ver = LibStub:GetLibrary(major, true)
flickerstreak@22 479 if not lib and self.libs[major] then
flickerstreak@22 480 lib, ver = self.libs[major].instance, self.libs[major].minor
flickerstreak@22 481 end
flickerstreak@1 482 if minor then
flickerstreak@1 483 if type(minor) == "string" then
flickerstreak@1 484 local m = svnRevisionToNumber(minor)
flickerstreak@1 485 if m then
flickerstreak@1 486 minor = m
flickerstreak@1 487 else
flickerstreak@7 488 _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
flickerstreak@1 489 end
flickerstreak@1 490 end
flickerstreak@1 491 argCheck(self, minor, 3, "number")
flickerstreak@22 492 if not lib then
flickerstreak@22 493 return false
flickerstreak@1 494 end
flickerstreak@22 495 return ver == minor
flickerstreak@1 496 end
flickerstreak@22 497 return not not lib
flickerstreak@1 498 end
flickerstreak@1 499
flickerstreak@1 500 -- @method GetInstance
flickerstreak@1 501 -- @brief Returns the library with the given major/minor version.
flickerstreak@1 502 -- @param major A string representing the major version.
flickerstreak@1 503 -- @param minor (optional) An integer or an svn revision string representing the minor version.
flickerstreak@1 504 -- @return The library with the given major/minor version.
flickerstreak@1 505 function AceLibrary:GetInstance(major, minor)
flickerstreak@1 506 argCheck(self, major, 2, "string")
flickerstreak@22 507 if minor ~= false then
flickerstreak@22 508 TryToLoadStandalone(major)
flickerstreak@22 509 end
flickerstreak@1 510
flickerstreak@22 511 local data, ver = LibStub:GetLibrary(major, true)
flickerstreak@1 512 if not data then
flickerstreak@22 513 if self.libs[major] then
flickerstreak@22 514 data, ver = self.libs[major].instance, self.libs[major].minor
flickerstreak@22 515 else
flickerstreak@22 516 _G.error(("Cannot find a library instance of %s."):format(major), 2)
flickerstreak@22 517 return
flickerstreak@22 518 end
flickerstreak@1 519 end
flickerstreak@1 520 if minor then
flickerstreak@1 521 if type(minor) == "string" then
flickerstreak@1 522 local m = svnRevisionToNumber(minor)
flickerstreak@1 523 if m then
flickerstreak@1 524 minor = m
flickerstreak@1 525 else
flickerstreak@7 526 _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
flickerstreak@1 527 end
flickerstreak@1 528 end
flickerstreak@1 529 argCheck(self, minor, 2, "number")
flickerstreak@22 530 if ver ~= minor then
flickerstreak@7 531 _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2)
flickerstreak@1 532 end
flickerstreak@1 533 end
flickerstreak@22 534 return data
flickerstreak@1 535 end
flickerstreak@1 536
flickerstreak@1 537 -- Syntax sugar. AceLibrary("FooBar-1.0")
flickerstreak@1 538 AceLibrary_mt.__call = AceLibrary.GetInstance
flickerstreak@1 539
flickerstreak@1 540 local donothing = function() end
flickerstreak@1 541
flickerstreak@1 542 local AceEvent
flickerstreak@1 543
flickerstreak@22 544 local tmp = {}
flickerstreak@22 545
flickerstreak@1 546 -- @method Register
flickerstreak@1 547 -- @brief Registers a new version of a given library.
flickerstreak@1 548 -- @param newInstance the library to register
flickerstreak@1 549 -- @param major the major version of the library
flickerstreak@1 550 -- @param minor the minor version of the library
flickerstreak@1 551 -- @param activateFunc (optional) A function to be called when the library is
flickerstreak@1 552 -- fully activated. Takes the arguments
flickerstreak@1 553 -- (newInstance [, oldInstance, oldDeactivateFunc]). If
flickerstreak@1 554 -- oldInstance is given, you should probably call
flickerstreak@1 555 -- oldDeactivateFunc(oldInstance).
flickerstreak@1 556 -- @param deactivateFunc (optional) A function to be called by a newer library's
flickerstreak@1 557 -- activateFunc.
flickerstreak@1 558 -- @param externalFunc (optional) A function to be called whenever a new
flickerstreak@1 559 -- library is registered.
flickerstreak@1 560 function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
flickerstreak@1 561 argCheck(self, newInstance, 2, "table")
flickerstreak@1 562 argCheck(self, major, 3, "string")
flickerstreak@1 563 if major ~= ACELIBRARY_MAJOR then
flickerstreak@1 564 for k,v in pairs(_G) do
flickerstreak@1 565 if v == newInstance then
flickerstreak@7 566 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
flickerstreak@1 567 end
flickerstreak@1 568 end
flickerstreak@1 569 end
flickerstreak@7 570 if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then
flickerstreak@1 571 _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
flickerstreak@1 572 end
flickerstreak@1 573 if type(minor) == "string" then
flickerstreak@1 574 local m = svnRevisionToNumber(minor)
flickerstreak@1 575 if m then
flickerstreak@1 576 minor = m
flickerstreak@1 577 else
flickerstreak@7 578 _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
flickerstreak@1 579 end
flickerstreak@1 580 end
flickerstreak@1 581 argCheck(self, minor, 4, "number")
flickerstreak@1 582 if math.floor(minor) ~= minor or minor < 0 then
flickerstreak@1 583 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
flickerstreak@1 584 end
flickerstreak@1 585 argCheck(self, activateFunc, 5, "function", "nil")
flickerstreak@1 586 argCheck(self, deactivateFunc, 6, "function", "nil")
flickerstreak@1 587 argCheck(self, externalFunc, 7, "function", "nil")
flickerstreak@1 588 if not deactivateFunc then
flickerstreak@1 589 deactivateFunc = donothing
flickerstreak@1 590 end
flickerstreak@1 591 local data = self.libs[major]
flickerstreak@1 592 if not data then
flickerstreak@1 593 -- This is new
flickerstreak@22 594 if LibStub:GetLibrary(major, true) then
flickerstreak@22 595 error(self, "Cannot register library %q. It is already registered with LibStub.", major)
flickerstreak@22 596 end
flickerstreak@22 597 local instance = LibStub:NewLibrary(major, minor)
flickerstreak@22 598 copyTable(newInstance, instance)
flickerstreak@1 599 crawlReplace(instance, instance, newInstance)
flickerstreak@1 600 destroyTable(newInstance)
flickerstreak@1 601 if AceLibrary == newInstance then
flickerstreak@1 602 self = instance
flickerstreak@1 603 AceLibrary = instance
flickerstreak@1 604 end
flickerstreak@1 605 self.libs[major] = {
flickerstreak@1 606 instance = instance,
flickerstreak@1 607 minor = minor,
flickerstreak@1 608 deactivateFunc = deactivateFunc,
flickerstreak@1 609 externalFunc = externalFunc,
flickerstreak@1 610 }
flickerstreak@1 611 rawset(instance, 'GetLibraryVersion', function(self)
flickerstreak@1 612 return major, minor
flickerstreak@1 613 end)
flickerstreak@1 614 if not rawget(instance, 'error') then
flickerstreak@1 615 rawset(instance, 'error', error)
flickerstreak@1 616 end
flickerstreak@22 617 if not WoW22 and not rawget(instance, 'assert') then
flickerstreak@1 618 rawset(instance, 'assert', assert)
flickerstreak@1 619 end
flickerstreak@1 620 if not rawget(instance, 'argCheck') then
flickerstreak@1 621 rawset(instance, 'argCheck', argCheck)
flickerstreak@1 622 end
flickerstreak@1 623 if not rawget(instance, 'pcall') then
flickerstreak@1 624 rawset(instance, 'pcall', pcall)
flickerstreak@1 625 end
flickerstreak@1 626 addToPositions(instance, major)
flickerstreak@1 627 if activateFunc then
flickerstreak@1 628 safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
flickerstreak@1 629
flickerstreak@1 630 --[[ if major ~= ACELIBRARY_MAJOR then
flickerstreak@1 631 for k,v in pairs(_G) do
flickerstreak@1 632 if v == instance then
flickerstreak@7 633 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
flickerstreak@1 634 end
flickerstreak@1 635 end
flickerstreak@1 636 end]]
flickerstreak@1 637 end
flickerstreak@1 638
flickerstreak@1 639 if externalFunc then
flickerstreak@22 640 for k, data_instance in LibStub:IterateLibraries() do -- all libraries
flickerstreak@22 641 tmp[k] = data_instance
flickerstreak@22 642 end
flickerstreak@22 643 for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub
flickerstreak@22 644 tmp[k] = data.instance
flickerstreak@22 645 end
flickerstreak@22 646 for k, data_instance in pairs(tmp) do
flickerstreak@1 647 if k ~= major then
flickerstreak@22 648 safecall(externalFunc, instance, k, data_instance)
flickerstreak@1 649 end
flickerstreak@22 650 tmp[k] = nil
flickerstreak@1 651 end
flickerstreak@1 652 end
flickerstreak@1 653
flickerstreak@22 654 for k,data in pairs(self.libs) do -- only Ace libraries
flickerstreak@1 655 if k ~= major and data.externalFunc then
flickerstreak@1 656 safecall(data.externalFunc, data.instance, major, instance)
flickerstreak@1 657 end
flickerstreak@1 658 end
flickerstreak@1 659 if major == "AceEvent-2.0" then
flickerstreak@1 660 AceEvent = instance
flickerstreak@1 661 end
flickerstreak@1 662 if AceEvent then
flickerstreak@1 663 AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
flickerstreak@1 664 end
flickerstreak@1 665
flickerstreak@1 666 return instance
flickerstreak@1 667 end
flickerstreak@1 668 if minor <= data.minor then
flickerstreak@1 669 -- This one is already obsolete, raise an error.
flickerstreak@7 670 _G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2)
flickerstreak@1 671 return
flickerstreak@1 672 end
flickerstreak@22 673 local instance = data.instance
flickerstreak@1 674 -- This is an update
flickerstreak@1 675 local oldInstance = {}
flickerstreak@1 676
flickerstreak@22 677 local libStubInstance = LibStub:GetLibrary(major, true)
flickerstreak@22 678 if not libStubInstance then -- non-LibStub AceLibrary registered the library
flickerstreak@22 679 -- pass
flickerstreak@22 680 elseif libStubInstance ~= instance then
flickerstreak@22 681 error(self, "Cannot register library %q. It is already registered with LibStub.", major)
flickerstreak@22 682 else
flickerstreak@22 683 LibStub:NewLibrary(major, minor) -- upgrade the minor version
flickerstreak@22 684 end
flickerstreak@22 685
flickerstreak@1 686 addToPositions(newInstance, major)
flickerstreak@1 687 local isAceLibrary = (AceLibrary == newInstance)
flickerstreak@1 688 local old_error, old_assert, old_argCheck, old_pcall
flickerstreak@1 689 if isAceLibrary then
flickerstreak@1 690 self = instance
flickerstreak@1 691 AceLibrary = instance
flickerstreak@1 692
flickerstreak@1 693 old_error = instance.error
flickerstreak@22 694 if not WoW22 then
flickerstreak@22 695 old_assert = instance.assert
flickerstreak@22 696 end
flickerstreak@1 697 old_argCheck = instance.argCheck
flickerstreak@1 698 old_pcall = instance.pcall
flickerstreak@1 699
flickerstreak@1 700 self.error = error
flickerstreak@22 701 if not WoW22 then
flickerstreak@22 702 self.assert = assert
flickerstreak@22 703 end
flickerstreak@1 704 self.argCheck = argCheck
flickerstreak@1 705 self.pcall = pcall
flickerstreak@1 706 end
flickerstreak@1 707 deepTransfer(instance, newInstance, oldInstance, major)
flickerstreak@1 708 crawlReplace(instance, instance, newInstance)
flickerstreak@1 709 local oldDeactivateFunc = data.deactivateFunc
flickerstreak@1 710 data.minor = minor
flickerstreak@1 711 data.deactivateFunc = deactivateFunc
flickerstreak@1 712 data.externalFunc = externalFunc
flickerstreak@22 713 rawset(instance, 'GetLibraryVersion', function()
flickerstreak@1 714 return major, minor
flickerstreak@1 715 end)
flickerstreak@1 716 if not rawget(instance, 'error') then
flickerstreak@1 717 rawset(instance, 'error', error)
flickerstreak@1 718 end
flickerstreak@22 719 if not WoW22 and not rawget(instance, 'assert') then
flickerstreak@1 720 rawset(instance, 'assert', assert)
flickerstreak@1 721 end
flickerstreak@1 722 if not rawget(instance, 'argCheck') then
flickerstreak@1 723 rawset(instance, 'argCheck', argCheck)
flickerstreak@1 724 end
flickerstreak@1 725 if not rawget(instance, 'pcall') then
flickerstreak@1 726 rawset(instance, 'pcall', pcall)
flickerstreak@1 727 end
flickerstreak@1 728 if isAceLibrary then
flickerstreak@1 729 for _,v in pairs(self.libs) do
flickerstreak@1 730 local i = type(v) == "table" and v.instance
flickerstreak@1 731 if type(i) == "table" then
flickerstreak@1 732 if not rawget(i, 'error') or i.error == old_error then
flickerstreak@1 733 rawset(i, 'error', error)
flickerstreak@1 734 end
flickerstreak@22 735 if not WoW22 and (not rawget(i, 'assert') or i.assert == old_assert) then
flickerstreak@1 736 rawset(i, 'assert', assert)
flickerstreak@1 737 end
flickerstreak@1 738 if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
flickerstreak@1 739 rawset(i, 'argCheck', argCheck)
flickerstreak@1 740 end
flickerstreak@1 741 if not rawget(i, 'pcall') or i.pcall == old_pcall then
flickerstreak@1 742 rawset(i, 'pcall', pcall)
flickerstreak@1 743 end
flickerstreak@1 744 end
flickerstreak@1 745 end
flickerstreak@1 746 end
flickerstreak@1 747 if activateFunc then
flickerstreak@1 748 safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
flickerstreak@1 749
flickerstreak@7 750 --[[ if major ~= ACELIBRARY_MAJOR then
flickerstreak@1 751 for k,v in pairs(_G) do
flickerstreak@1 752 if v == instance then
flickerstreak@7 753 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
flickerstreak@1 754 end
flickerstreak@1 755 end
flickerstreak@7 756 end]]
flickerstreak@1 757 else
flickerstreak@1 758 safecall(oldDeactivateFunc, oldInstance)
flickerstreak@1 759 end
flickerstreak@1 760 oldInstance = nil
flickerstreak@1 761
flickerstreak@1 762 if externalFunc then
flickerstreak@22 763 for k, data_instance in LibStub:IterateLibraries() do -- all libraries
flickerstreak@22 764 tmp[k] = data_instance
flickerstreak@22 765 end
flickerstreak@22 766 for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub
flickerstreak@22 767 tmp[k] = data.instance
flickerstreak@22 768 end
flickerstreak@22 769 for k, data_instance in pairs(tmp) do
flickerstreak@1 770 if k ~= major then
flickerstreak@22 771 safecall(externalFunc, instance, k, data_instance)
flickerstreak@1 772 end
flickerstreak@22 773 tmp[k] = nil
flickerstreak@1 774 end
flickerstreak@1 775 end
flickerstreak@1 776
flickerstreak@1 777 return instance
flickerstreak@1 778 end
flickerstreak@1 779
flickerstreak@1 780 function AceLibrary:IterateLibraries()
flickerstreak@22 781 local t = {}
flickerstreak@22 782 for major, instance in LibStub:IterateLibraries() do
flickerstreak@22 783 t[major] = instance
flickerstreak@22 784 end
flickerstreak@22 785 for major, data in pairs(self.libs) do
flickerstreak@22 786 t[major] = data.instance
flickerstreak@22 787 end
flickerstreak@22 788 return pairs(t)
flickerstreak@22 789 end
flickerstreak@22 790
flickerstreak@22 791 local function manuallyFinalize(major, instance)
flickerstreak@22 792 if AceLibrary.libs[major] then
flickerstreak@22 793 -- don't work on Ace libraries
flickerstreak@22 794 return
flickerstreak@22 795 end
flickerstreak@22 796 local finalizedExternalLibs = AceLibrary.finalizedExternalLibs
flickerstreak@22 797 if finalizedExternalLibs[major] then
flickerstreak@22 798 return
flickerstreak@22 799 end
flickerstreak@22 800 finalizedExternalLibs[major] = true
flickerstreak@22 801
flickerstreak@22 802 for k,data in pairs(AceLibrary.libs) do -- only Ace libraries
flickerstreak@22 803 if k ~= major and data.externalFunc then
flickerstreak@22 804 safecall(data.externalFunc, data.instance, major, instance)
flickerstreak@1 805 end
flickerstreak@1 806 end
flickerstreak@1 807 end
flickerstreak@1 808
flickerstreak@1 809 -- @function Activate
flickerstreak@1 810 -- @brief The activateFunc for AceLibrary itself. Called when
flickerstreak@1 811 -- AceLibrary properly registers.
flickerstreak@1 812 -- @param self Reference to AceLibrary
flickerstreak@1 813 -- @param oldLib (optional) Reference to an old version of AceLibrary
flickerstreak@1 814 -- @param oldDeactivate (optional) Function to deactivate the old lib
flickerstreak@1 815 local function activate(self, oldLib, oldDeactivate)
flickerstreak@1 816 AceLibrary = self
flickerstreak@1 817 if not self.libs then
flickerstreak@1 818 self.libs = oldLib and oldLib.libs or {}
flickerstreak@1 819 self.scannedlibs = oldLib and oldLib.scannedlibs or {}
flickerstreak@1 820 end
flickerstreak@1 821 if not self.positions then
flickerstreak@1 822 self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" })
flickerstreak@1 823 end
flickerstreak@22 824 self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {}
flickerstreak@22 825 self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
flickerstreak@22 826 self.frame:UnregisterAllEvents()
flickerstreak@22 827 self.frame:RegisterEvent("ADDON_LOADED")
flickerstreak@22 828 self.frame:SetScript("OnEvent", function()
flickerstreak@22 829 for major, instance in LibStub:IterateLibraries() do
flickerstreak@22 830 manuallyFinalize(major, instance)
flickerstreak@22 831 end
flickerstreak@22 832 end)
flickerstreak@22 833 for major, instance in LibStub:IterateLibraries() do
flickerstreak@22 834 manuallyFinalize(major, instance)
flickerstreak@22 835 end
flickerstreak@1 836
flickerstreak@1 837 -- Expose the library in the global environment
flickerstreak@1 838 _G[ACELIBRARY_MAJOR] = self
flickerstreak@1 839
flickerstreak@1 840 if oldDeactivate then
flickerstreak@1 841 oldDeactivate(oldLib)
flickerstreak@1 842 end
flickerstreak@1 843 end
flickerstreak@1 844
flickerstreak@1 845 if not previous then
flickerstreak@1 846 previous = AceLibrary
flickerstreak@1 847 end
flickerstreak@1 848 if not previous.libs then
flickerstreak@1 849 previous.libs = {}
flickerstreak@1 850 end
flickerstreak@1 851 AceLibrary.libs = previous.libs
flickerstreak@1 852 if not previous.positions then
flickerstreak@1 853 previous.positions = setmetatable({}, { __mode = "k" })
flickerstreak@1 854 end
flickerstreak@1 855 AceLibrary.positions = previous.positions
flickerstreak@22 856 AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate, nil)