vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: AceLibrary
3 Revision: $Rev: 9154 $
4 Author(s): ckknight (ckknight@gmail.com)
5 cladhaire (cladhaire@gmail.com)
6 Inspired By: Iriel (iriel@vigilance-committee.org)
7 Tekkub (tekkub@gmail.com)
8 Website: http://www.wowace.com/
9 Documentation: http://wiki.wowace.com/index.php/AceLibrary
10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
11 Description: Versioning library to handle other library instances, upgrading,
12 and proper access.
13 It also provides a base for libraries to work off of, providing
14 proper error tools. It is handy because all the errors occur in the
15 file that called it, not in the library file itself.
16 Dependencies: None
17 ]]
18  
19 local ACELIBRARY_MAJOR = "AceLibrary"
20 local ACELIBRARY_MINOR = "$Revision: 9154 $"
21  
22 -- CHANGE DEBUG TO ``false`` ON RELEASE -------------------
23 local DEBUG = true
24 -- CHANGE DEBUG TO ``false`` ON RELEASE -------------------
25  
26 local _G = getfenv(0)
27 local previous = _G[ACELIBRARY_MAJOR]
28 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
29  
30 -- @table AceLibrary
31 -- @brief System to handle all versioning of libraries.
32 local AceLibrary = {}
33 local AceLibrary_mt = {}
34 setmetatable(AceLibrary, AceLibrary_mt)
35  
36 local tmp
37 local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
38 if not tmp then
39 tmp = {}
40 else
41 for k in pairs(tmp) do tmp[k] = nil end
42 table.setn(tmp, 0)
43 end
44  
45 tmp.n = 0
46 table.insert(tmp, a1)
47 table.insert(tmp, a2)
48 table.insert(tmp, a3)
49 table.insert(tmp, a4)
50 table.insert(tmp, a5)
51 table.insert(tmp, a6)
52 table.insert(tmp, a7)
53 table.insert(tmp, a8)
54 table.insert(tmp, a9)
55 table.insert(tmp, a10)
56 table.insert(tmp, a11)
57 table.insert(tmp, a12)
58 table.insert(tmp, a13)
59 table.insert(tmp, a14)
60 table.insert(tmp, a15)
61 table.insert(tmp, a16)
62 table.insert(tmp, a17)
63 table.insert(tmp, a18)
64 table.insert(tmp, a19)
65 table.insert(tmp, a20)
66  
67 local stack = debugstack()
68 if not message then
69 local _,_,second = string.find(stack, "\n(.-)\n")
70 message = "error raised! " .. second
71 else
72 for i = 1,table.getn(tmp) do
73 tmp[i] = tostring(tmp[i])
74 end
75 for i = 1,10 do
76 table.insert(tmp, "nil")
77 end
78 message = string.format(message, unpack(tmp))
79 end
80  
81 if getmetatable(self) and getmetatable(self).__tostring then
82 message = string.format("%s: %s", tostring(self), message)
83 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
84 message = string.format("%s: %s", self:GetLibraryVersion(), message)
85 elseif type(rawget(self, 'class')) == "table" and type(self.class.GetLibraryVersion) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
86 message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
87 end
88  
89 local first = string.gsub(stack, "\n.*", "")
90 local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
91 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
92  
93 local i = 0
94 for s in string.gfind(stack, "\n([^\n]*)") do
95 i = i + 1
96 if not string.find(s, file .. "%.lua:%d+:") then
97 file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
98 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
99 break
100 end
101 end
102 local j = 0
103 for s in string.gfind(stack, "\n([^\n]*)") do
104 j = j + 1
105 if j > i and not string.find(s, file .. "%.lua:%d+:") then
106 _G.error(message, j + 1)
107 return
108 end
109 end
110 _G.error(message, 2)
111 return
112 end
113  
114 local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
115 if not condition then
116 if not message then
117 local stack = debugstack()
118 local _,_,second = string.find(stack, "\n(.-)\n")
119 message = "assertion failed! " .. second
120 end
121 error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
122 return
123 end
124 return condition
125 end
126  
127 local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
128 if type(num) ~= "number" then
129 error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
130 elseif type(kind) ~= "string" then
131 error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
132 end
133 local errored = false
134 arg = type(arg)
135 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
136 local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
137 if not func then
138 _,_,func = string.find(debugstack(), "([`<].-['>])")
139 end
140 if kind5 then
141 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)
142 elseif kind4 then
143 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)
144 elseif kind3 then
145 error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
146 elseif kind2 then
147 error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
148 else
149 error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
150 end
151 end
152 end
153  
154 local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
155 a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
156 if not a1 then
157 error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
158 else
159 return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
160 end
161 end
162  
163 local recurse = {}
164 local function addToPositions(t, major)
165 if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
166 rawset(t, recurse, true)
167 AceLibrary.positions[t] = major
168 for k,v in pairs(t) do
169 if type(v) == "table" and not rawget(v, recurse) then
170 addToPositions(v, major)
171 end
172 if type(k) == "table" and not rawget(k, recurse) then
173 addToPositions(k, major)
174 end
175 end
176 local mt = getmetatable(t)
177 if mt and not rawget(mt, recurse) then
178 addToPositions(mt, major)
179 end
180 rawset(t, recurse, nil)
181 end
182 end
183  
184 local function svnRevisionToNumber(text)
185 if type(text) == "string" then
186 if string.find(text, "^%$Revision: (%d+) %$$") then
187 return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
188 elseif string.find(text, "^%$Rev: (%d+) %$$") then
189 return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
190 elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
191 return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
192 end
193 elseif type(text) == "number" then
194 return text
195 end
196 return nil
197 end
198  
199 local crawlReplace
200 do
201 local recurse = {}
202 local function func(t, to, from)
203 if recurse[t] then
204 return
205 end
206 recurse[t] = true
207 local mt = getmetatable(t)
208 setmetatable(t, nil)
209 rawset(t, to, rawget(t, from))
210 rawset(t, from, nil)
211 for k,v in pairs(t) do
212 if v == from then
213 t[k] = to
214 elseif type(v) == "table" then
215 if not recurse[v] then
216 func(v, to, from)
217 end
218 end
219  
220 if type(k) == "table" then
221 if not recurse[k] then
222 func(k, to, from)
223 end
224 end
225 end
226 setmetatable(t, mt)
227 if mt then
228 if mt == from then
229 setmetatable(t, to)
230 elseif not recurse[mt] then
231 func(mt, to, from)
232 end
233 end
234 end
235 function crawlReplace(t, to, from)
236 func(t, to, from)
237 for k in pairs(recurse) do
238 recurse[k] = nil
239 end
240 end
241 end
242  
243 -- @function destroyTable
244 -- @brief remove all the contents of a table
245 -- @param t table to destroy
246 local function destroyTable(t)
247 setmetatable(t, nil)
248 for k,v in pairs(t) do t[k] = nil end
249 table.setn(t, 0)
250 end
251  
252 local function isFrame(frame)
253 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"
254 end
255  
256 local new, del
257 do
258 local tables = setmetatable({}, {__mode = "k"})
259  
260 function new()
261 local t = next(tables)
262 if t then
263 tables[t] = nil
264 return t
265 else
266 return {}
267 end
268 end
269  
270 function del(t, depth)
271 if depth and depth > 0 then
272 for k,v in pairs(t) do
273 if type(v) == "table" and not isFrame(v) then
274 del(v, depth - 1)
275 end
276 end
277 end
278 destroyTable(t)
279 tables[t] = true
280 end
281 end
282  
283 -- @function copyTable
284 -- @brief Create a shallow copy of a table and return it.
285 -- @param from The table to copy from
286 -- @return A shallow copy of the table
287 local function copyTable(from)
288 local to = new()
289 for k,v in pairs(from) do to[k] = v end
290 table.setn(to, table.getn(from))
291 setmetatable(to, getmetatable(from))
292 return to
293 end
294  
295 -- @function deepTransfer
296 -- @brief Fully transfer all data, keeping proper previous table
297 -- backreferences stable.
298 -- @param to The table with which data is to be injected into
299 -- @param from The table whose data will be injected into the first
300 -- @param saveFields If available, a shallow copy of the basic data is saved
301 -- in here.
302 -- @param list The account of table references
303 -- @param list2 The current status on which tables have been traversed.
304 local deepTransfer
305 do
306 -- @function examine
307 -- @brief Take account of all the table references to be shared
308 -- between the to and from tables.
309 -- @param to The table with which data is to be injected into
310 -- @param from The table whose data will be injected into the first
311 -- @param list An account of the table references
312 local function examine(to, from, list, major)
313 list[from] = to
314 for k,v in pairs(from) do
315 if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
316 if from[k] == to[k] then
317 list[from[k]] = to[k]
318 elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
319 list[from[k]] = from[k]
320 elseif not list[from[k]] then
321 examine(to[k], from[k], list, major)
322 end
323 end
324 end
325 return list
326 end
327  
328 function deepTransfer(to, from, saveFields, major, list, list2)
329 setmetatable(to, nil)
330 local createdList
331 if not list then
332 createdList = true
333 list = new()
334 list2 = new()
335 examine(to, from, list, major)
336 end
337 list2[to] = to
338 for k,v in pairs(to) do
339 if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
340 if saveFields then
341 saveFields[k] = v
342 end
343 to[k] = nil
344 elseif v ~= _G then
345 if saveFields then
346 saveFields[k] = copyTable(v)
347 end
348 end
349 end
350 for k in pairs(from) do
351 if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
352 if not list2[to[k]] then
353 deepTransfer(to[k], from[k], nil, major, list, list2)
354 end
355 to[k] = list[to[k]] or list2[to[k]]
356 else
357 rawset(to, k, from[k])
358 end
359 end
360 table.setn(to, table.getn(from))
361 setmetatable(to, getmetatable(from))
362 local mt = getmetatable(to)
363 if mt then
364 if list[mt] then
365 setmetatable(to, list[mt])
366 elseif mt.__index and list[mt.__index] then
367 mt.__index = list[mt.__index]
368 end
369 end
370 destroyTable(from)
371 if createdList then
372 del(list)
373 del(list2)
374 end
375 end
376 end
377  
378 -- @method IsNewVersion
379 -- @brief Obtain whether the supplied version would be an upgrade to the
380 -- current version. This allows for bypass code in library
381 -- declaration.
382 -- @param major A string representing the major version
383 -- @param minor An integer or an svn revision string representing the minor version
384 -- @return whether the supplied version would be newer than what is
385 -- currently available.
386 function AceLibrary:IsNewVersion(major, minor)
387 argCheck(self, major, 2, "string")
388 if type(minor) == "string" then
389 local m = svnRevisionToNumber(minor)
390 if m then
391 minor = m
392 else
393 _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
394 end
395 end
396 argCheck(self, minor, 3, "number")
397 local data = self.libs[major]
398 if not data then
399 return true
400 end
401 return data.minor < minor
402 end
403  
404 -- @method HasInstance
405 -- @brief Returns whether an instance exists. This allows for optional support of a library.
406 -- @param major A string representing the major version.
407 -- @param minor (optional) An integer or an svn revision string representing the minor version.
408 -- @return Whether an instance exists.
409 function AceLibrary:HasInstance(major, minor)
410 argCheck(self, major, 2, "string")
411 if minor then
412 if type(minor) == "string" then
413 local m = svnRevisionToNumber(minor)
414 if m then
415 minor = m
416 else
417 _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
418 end
419 end
420 argCheck(self, minor, 3, "number")
421 if not self.libs[major] then
422 return
423 end
424 return self.libs[major].minor == minor
425 end
426 return self.libs[major] and true
427 end
428  
429 -- @method GetInstance
430 -- @brief Returns the library with the given major/minor version.
431 -- @param major A string representing the major version.
432 -- @param minor (optional) An integer or an svn revision string representing the minor version.
433 -- @return The library with the given major/minor version.
434 function AceLibrary:GetInstance(major, minor)
435 argCheck(self, major, 2, "string")
436  
437 local data = self.libs[major]
438 if not data then
439 _G.error(string.format("Cannot find a library instance of %s.", major), 2)
440 return
441 end
442 if minor then
443 if type(minor) == "string" then
444 local m = svnRevisionToNumber(minor)
445 if m then
446 minor = m
447 else
448 _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
449 end
450 end
451 argCheck(self, minor, 2, "number")
452 if data.minor ~= minor then
453 _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
454 return
455 end
456 end
457 return data.instance
458 end
459  
460 -- Syntax sugar. AceLibrary("FooBar-1.0")
461 AceLibrary_mt.__call = AceLibrary.GetInstance
462  
463 local donothing
464  
465 local AceEvent
466  
467 -- @method Register
468 -- @brief Registers a new version of a given library.
469 -- @param newInstance the library to register
470 -- @param major the major version of the library
471 -- @param minor the minor version of the library
472 -- @param activateFunc (optional) A function to be called when the library is
473 -- fully activated. Takes the arguments
474 -- (newInstance [, oldInstance, oldDeactivateFunc]). If
475 -- oldInstance is given, you should probably call
476 -- oldDeactivateFunc(oldInstance).
477 -- @param deactivateFunc (optional) A function to be called by a newer library's
478 -- activateFunc.
479 -- @param externalFunc (optional) A function to be called whenever a new
480 -- library is registered.
481 function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
482 argCheck(self, newInstance, 2, "table")
483 argCheck(self, major, 3, "string")
484 if type(minor) == "string" then
485 local m = svnRevisionToNumber(minor)
486 if m then
487 minor = m
488 else
489 _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
490 end
491 end
492 argCheck(self, minor, 4, "number")
493 if math.floor(minor) ~= minor or minor < 0 then
494 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
495 end
496 argCheck(self, activateFunc, 5, "function", "nil")
497 argCheck(self, deactivateFunc, 6, "function", "nil")
498 argCheck(self, externalFunc, 7, "function", "nil")
499 if not deactivateFunc then
500 if not donothing then
501 donothing = function() end
502 end
503 deactivateFunc = donothing
504 end
505 local data = self.libs[major]
506 if not data then
507 -- This is new
508 local instance = copyTable(newInstance)
509 crawlReplace(instance, instance, newInstance)
510 destroyTable(newInstance)
511 if AceLibrary == newInstance then
512 self = instance
513 AceLibrary = instance
514 end
515 self.libs[major] = {
516 instance = instance,
517 minor = minor,
518 deactivateFunc = deactivateFunc,
519 externalFunc = externalFunc,
520 }
521 rawset(instance, 'GetLibraryVersion', function(self)
522 return major, minor
523 end)
524 if not rawget(instance, 'error') then
525 rawset(instance, 'error', error)
526 end
527 if not rawget(instance, 'assert') then
528 rawset(instance, 'assert', assert)
529 end
530 if not rawget(instance, 'argCheck') then
531 rawset(instance, 'argCheck', argCheck)
532 end
533 if not rawget(instance, 'pcall') then
534 rawset(instance, 'pcall', pcall)
535 end
536 addToPositions(instance, major)
537 if activateFunc then
538 activateFunc(instance, nil, nil) -- no old version, so explicit nil
539 end
540  
541 if externalFunc then
542 for k,data in pairs(self.libs) do
543 if k ~= major then
544 externalFunc(instance, k, data.instance)
545 end
546 end
547 end
548  
549 for k,data in pairs(self.libs) do
550 if k ~= major and data.externalFunc then
551 data.externalFunc(data.instance, major, instance)
552 end
553 end
554 if major == "AceEvent-2.0" then
555 AceEvent = instance
556 end
557 if AceEvent then
558 AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
559 end
560  
561 return instance
562 end
563 local instance = data.instance
564 if minor <= data.minor then
565 if DEBUG then
566 -- This one is already obsolete, raise an error.
567 error(string.format("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", major, data.minor, minor, major, minor), 2)
568 return
569 end
570 return instance
571 end
572 -- This is an update
573 local oldInstance = new()
574  
575 addToPositions(newInstance, major)
576 local isAceLibrary = (AceLibrary == newInstance)
577 local old_error, old_assert, old_argCheck, old_pcall
578 if isAceLibrary then
579 self = instance
580 AceLibrary = instance
581  
582 old_error = instance.error
583 old_assert = instance.assert
584 old_argCheck = instance.argCheck
585 old_pcall = instance.pcall
586  
587 self.error = error
588 self.assert = assert
589 self.argCheck = argCheck
590 self.pcall = pcall
591 end
592 deepTransfer(instance, newInstance, oldInstance, major)
593 crawlReplace(instance, instance, newInstance)
594 local oldDeactivateFunc = data.deactivateFunc
595 data.minor = minor
596 data.deactivateFunc = deactivateFunc
597 data.externalFunc = externalFunc
598 rawset(instance, 'GetLibraryVersion', function(self)
599 return major, minor
600 end)
601 if not rawget(instance, 'error') then
602 rawset(instance, 'error', error)
603 end
604 if not rawget(instance, 'assert') then
605 rawset(instance, 'assert', assert)
606 end
607 if not rawget(instance, 'argCheck') then
608 rawset(instance, 'argCheck', argCheck)
609 end
610 if not rawget(instance, 'pcall') then
611 rawset(instance, 'pcall', pcall)
612 end
613 if isAceLibrary then
614 for _,v in pairs(self.libs) do
615 local i = type(v) == "table" and v.instance
616 if type(i) == "table" then
617 if not rawget(i, 'error') or i.error == old_error then
618 rawset(i, 'error', error)
619 end
620 if not rawget(i, 'assert') or i.assert == old_assert then
621 rawset(i, 'assert', assert)
622 end
623 if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
624 rawset(i, 'argCheck', argCheck)
625 end
626 if not rawget(i, 'pcall') or i.pcall == old_pcall then
627 rawset(i, 'pcall', pcall)
628 end
629 end
630 end
631 end
632 if activateFunc then
633 activateFunc(instance, oldInstance, oldDeactivateFunc)
634 else
635 oldDeactivateFunc(oldInstance)
636 end
637 del(oldInstance)
638  
639 if externalFunc then
640 for k,data in pairs(self.libs) do
641 if k ~= major then
642 externalFunc(instance, k, data.instance)
643 end
644 end
645 end
646  
647 return instance
648 end
649  
650 local iter
651 function AceLibrary:IterateLibraries()
652 if not iter then
653 local function iter(t, k)
654 k = next(t, k)
655 if not k then
656 return nil
657 else
658 return k, t[k].instance
659 end
660 end
661 end
662 return iter, self.libs, nil
663 end
664  
665 -- @function Activate
666 -- @brief The activateFunc for AceLibrary itself. Called when
667 -- AceLibrary properly registers.
668 -- @param self Reference to AceLibrary
669 -- @param oldLib (optional) Reference to an old version of AceLibrary
670 -- @param oldDeactivate (optional) Function to deactivate the old lib
671 local function activate(self, oldLib, oldDeactivate)
672 if not self.libs then
673 if oldLib then
674 self.libs = oldLib.libs
675 end
676 if not self.libs then
677 self.libs = {}
678 end
679 end
680 if not self.positions then
681 if oldLib then
682 self.positions = oldLib.positions
683 end
684 if not self.positions then
685 self.positions = setmetatable({}, { __mode = "k" })
686 end
687 end
688  
689 -- Expose the library in the global environment
690 _G[ACELIBRARY_MAJOR] = self
691  
692 if oldDeactivate then
693 oldDeactivate(oldLib)
694 end
695 end
696  
697 if not previous then
698 previous = AceLibrary
699 end
700 if not previous.libs then
701 previous.libs = {}
702 end
703 AceLibrary.libs = previous.libs
704 if not previous.positions then
705 previous.positions = setmetatable({}, { __mode = "k" })
706 end
707 AceLibrary.positions = previous.positions
708 AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)