comparison libs/AceLibrary/AceLibrary.lua @ 7:f920db5fc6b1

version 0.3
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:25:29 +0000
parents c11ca1d8ed91
children
comparison
equal deleted inserted replaced
6:2da5089ab7ff 7:f920db5fc6b1
1 --[[ 1 --[[
2 Name: AceLibrary 2 Name: AceLibrary
3 Revision: $Rev: 19062 $ 3 Revision: $Rev$
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
5 Inspired By: Iriel (iriel@vigilance-committee.org) 5 Inspired By: Iriel (iriel@vigilance-committee.org)
6 Tekkub (tekkub@gmail.com) 6 Tekkub (tekkub@gmail.com)
7 Revision: $Rev: 19062 $ 7 Revision: $Rev$
8 Website: http://www.wowace.com/ 8 Website: http://www.wowace.com/
9 Documentation: http://www.wowace.com/index.php/AceLibrary 9 Documentation: http://www.wowace.com/index.php/AceLibrary
10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary 10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
11 Description: Versioning library to handle other library instances, upgrading, 11 Description: Versioning library to handle other library instances, upgrading,
12 and proper access. 12 and proper access.
13 It also provides a base for libraries to work off of, providing 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 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. 15 file that called it, not in the library file itself.
16 Dependencies: None 16 Dependencies: None
17 License: LGPL v2.1
17 ]] 18 ]]
18 19
19 local ACELIBRARY_MAJOR = "AceLibrary" 20 local ACELIBRARY_MAJOR = "AceLibrary"
20 local ACELIBRARY_MINOR = "$Revision: 19062 $" 21 local ACELIBRARY_MINOR = "$Revision: 20000 $"
21 22
22 local _G = getfenv(0) 23 local _G = getfenv(0)
23 local previous = _G[ACELIBRARY_MAJOR] 24 local previous = _G[ACELIBRARY_MAJOR]
24 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end 25 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
25 26
26 local function safecall(func,...) 27 local function safecall(func,...)
27 local success, err = pcall(func,...) 28 local success, err = pcall(func,...)
28 if not success then geterrorhandler()(err) end 29 if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end
29 end 30 end
30 31
31 -- @table AceLibrary 32 -- @table AceLibrary
32 -- @brief System to handle all versioning of libraries. 33 -- @brief System to handle all versioning of libraries.
33 local AceLibrary = {} 34 local AceLibrary = {}
34 local AceLibrary_mt = {} 35 local AceLibrary_mt = {}
35 setmetatable(AceLibrary, AceLibrary_mt) 36 setmetatable(AceLibrary, AceLibrary_mt)
36 37
37 local function error(self, message, ...) 38 local function error(self, message, ...)
38 if type(self) ~= "table" then 39 if type(self) ~= "table" then
39 return _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2) 40 return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2)
40 end 41 end
41 42
42 local stack = debugstack() 43 local stack = debugstack()
43 if not message then 44 if not message then
44 local _,_,second = string.find(stack, "\n(.-)\n") 45 local _,_,second = stack:find("\n(.-)\n")
45 message = "error raised! " .. second 46 message = "error raised! " .. second
46 else 47 else
47 local arg = { ... } 48 local arg = { ... } -- not worried about table creation, as errors don't happen often
48 49
49 for i = 1, #arg do 50 for i = 1, #arg do
50 arg[i] = tostring(arg[i]) 51 arg[i] = tostring(arg[i])
51 end 52 end
52 for i = 1, 10 do 53 for i = 1, 10 do
53 table.insert(arg, "nil") 54 table.insert(arg, "nil")
54 end 55 end
55 message = string.format(message, unpack(arg)) 56 message = message:format(unpack(arg))
56 end 57 end
57 58
58 if getmetatable(self) and getmetatable(self).__tostring then 59 if getmetatable(self) and getmetatable(self).__tostring then
59 message = string.format("%s: %s", tostring(self), message) 60 message = ("%s: %s"):format(tostring(self), message)
60 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then 61 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
61 message = string.format("%s: %s", self:GetLibraryVersion(), message) 62 message = ("%s: %s"):format(self:GetLibraryVersion(), message)
62 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then 63 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
63 message = string.format("%s: %s", self.class:GetLibraryVersion(), message) 64 message = ("%s: %s"):format(self.class:GetLibraryVersion(), message)
64 end 65 end
65 66
66 local first = string.gsub(stack, "\n.*", "") 67 local first = stack:gsub("\n.*", "")
67 local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1") 68 local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1")
68 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") 69 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
69 70
70 71
71 local i = 0 72 local i = 0
72 for s in string.gmatch(stack, "\n([^\n]*)") do 73 for s in stack:gmatch("\n([^\n]*)") do
73 i = i + 1 74 i = i + 1
74 if not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then 75 if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
75 file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1") 76 file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1")
76 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") 77 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
77 break 78 break
78 end 79 end
79 end 80 end
80 local j = 0 81 local j = 0
81 for s in string.gmatch(stack, "\n([^\n]*)") do 82 for s in stack:gmatch("\n([^\n]*)") do
82 j = j + 1 83 j = j + 1
83 if j > i and not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then 84 if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
84 return _G.error(message, j+1) 85 return _G.error(message, j+1)
85 end 86 end
86 end 87 end
87 return _G.error(message, 2) 88 return _G.error(message, 2)
88 end 89 end
89 90
90 local function assert(self, condition, message, ...) 91 local function assert(self, condition, message, ...)
91 if not condition then 92 if not condition then
92 if not message then 93 if not message then
93 local stack = debugstack() 94 local stack = debugstack()
94 local _,_,second = string.find(stack, "\n(.-)\n") 95 local _,_,second = stack:find("\n(.-)\n")
95 message = "assertion failed! " .. second 96 message = "assertion failed! " .. second
96 end 97 end
97 return error(self, message, ...) 98 return error(self, message, ...)
98 end 99 end
99 return condition 100 return condition
106 return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) 107 return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
107 end 108 end
108 local errored = false 109 local errored = false
109 arg = type(arg) 110 arg = type(arg)
110 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then 111 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
111 local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])") 112 local stack = debugstack()
113 local _,_,func = stack:find("`argCheck'.-([`<].-['>])")
112 if not func then 114 if not func then
113 _,_,func = string.find(debugstack(), "([`<].-['>])") 115 _,_,func = stack:find("([`<].-['>])")
114 end 116 end
115 if kind5 then 117 if kind5 then
116 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) 118 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)
117 elseif kind4 then 119 elseif kind4 then
118 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) 120 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)
128 130
129 local pcall 131 local pcall
130 do 132 do
131 local function check(self, ret, ...) 133 local function check(self, ret, ...)
132 if not ret then 134 if not ret then
133 return error(self, (string.gsub(..., ".-%.lua:%d-: ", ""))) 135 local s = ...
136 return error(self, (s:gsub(".-%.lua:%d-: ", "")))
134 else 137 else
135 return ... 138 return ...
136 end 139 end
137 end 140 end
138 141
162 end 165 end
163 end 166 end
164 167
165 local function svnRevisionToNumber(text) 168 local function svnRevisionToNumber(text)
166 if type(text) == "string" then 169 if type(text) == "string" then
167 if string.find(text, "^%$Revision: (%d+) %$$") then 170 if text:find("^%$Revision: (%d+) %$$") then
168 return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1"))) 171 return tonumber((text:gsub("^%$Revision: (%d+) %$$", "%1")))
169 elseif string.find(text, "^%$Rev: (%d+) %$$") then 172 elseif text:find("^%$Rev: (%d+) %$$") then
170 return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1"))) 173 return tonumber((text:gsub("^%$Rev: (%d+) %$$", "%1")))
171 elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then 174 elseif text:find("^%$LastChangedRevision: (%d+) %$$") then
172 return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1"))) 175 return tonumber((text:gsub("^%$LastChangedRevision: (%d+) %$$", "%1")))
173 end 176 end
174 elseif type(text) == "number" then 177 elseif type(text) == "number" then
175 return text 178 return text
176 end 179 end
177 return nil 180 return nil
365 if type(minor) == "string" then 368 if type(minor) == "string" then
366 local m = svnRevisionToNumber(minor) 369 local m = svnRevisionToNumber(minor)
367 if m then 370 if m then
368 minor = m 371 minor = m
369 else 372 else
370 _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 373 _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
371 end 374 end
372 end 375 end
373 argCheck(self, minor, 3, "number") 376 argCheck(self, minor, 3, "number")
374 local data = self.libs[major] 377 local data = self.libs[major]
375 if not data then 378 if not data then
391 if type(minor) == "string" then 394 if type(minor) == "string" then
392 local m = svnRevisionToNumber(minor) 395 local m = svnRevisionToNumber(minor)
393 if m then 396 if m then
394 minor = m 397 minor = m
395 else 398 else
396 _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 399 _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
397 end 400 end
398 end 401 end
399 argCheck(self, minor, 3, "number") 402 argCheck(self, minor, 3, "number")
400 if not self.libs[major] then 403 if not self.libs[major] then
401 return 404 return
414 argCheck(self, major, 2, "string") 417 argCheck(self, major, 2, "string")
415 TryToLoadStandalone(major) 418 TryToLoadStandalone(major)
416 419
417 local data = self.libs[major] 420 local data = self.libs[major]
418 if not data then 421 if not data then
419 _G.error(string.format("Cannot find a library instance of %s.", major), 2) 422 _G.error(("Cannot find a library instance of %s."):format(major), 2)
420 return 423 return
421 end 424 end
422 if minor then 425 if minor then
423 if type(minor) == "string" then 426 if type(minor) == "string" then
424 local m = svnRevisionToNumber(minor) 427 local m = svnRevisionToNumber(minor)
425 if m then 428 if m then
426 minor = m 429 minor = m
427 else 430 else
428 _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 431 _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
429 end 432 end
430 end 433 end
431 argCheck(self, minor, 2, "number") 434 argCheck(self, minor, 2, "number")
432 if data.minor ~= minor then 435 if data.minor ~= minor then
433 _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2) 436 _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2)
434 end 437 end
435 end 438 end
436 return data.instance 439 return data.instance
437 end 440 end
438 441
461 argCheck(self, newInstance, 2, "table") 464 argCheck(self, newInstance, 2, "table")
462 argCheck(self, major, 3, "string") 465 argCheck(self, major, 3, "string")
463 if major ~= ACELIBRARY_MAJOR then 466 if major ~= ACELIBRARY_MAJOR then
464 for k,v in pairs(_G) do 467 for k,v in pairs(_G) do
465 if v == newInstance then 468 if v == newInstance then
466 geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k)) 469 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
467 end 470 end
468 end 471 end
469 end 472 end
470 if major ~= ACELIBRARY_MAJOR and not string.find(major, "^[%a%-][%a%d%-]+[%a%-]%-%d+%.%d+$") and not string.find(major, "^[%a%-]+%-%d+%.%d+$") then 473 if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then
471 _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) 474 _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
472 end 475 end
473 if type(minor) == "string" then 476 if type(minor) == "string" then
474 local m = svnRevisionToNumber(minor) 477 local m = svnRevisionToNumber(minor)
475 if m then 478 if m then
476 minor = m 479 minor = m
477 else 480 else
478 _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 481 _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
479 end 482 end
480 end 483 end
481 argCheck(self, minor, 4, "number") 484 argCheck(self, minor, 4, "number")
482 if math.floor(minor) ~= minor or minor < 0 then 485 if math.floor(minor) ~= minor or minor < 0 then
483 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) 486 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
524 safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil 527 safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
525 528
526 --[[ if major ~= ACELIBRARY_MAJOR then 529 --[[ if major ~= ACELIBRARY_MAJOR then
527 for k,v in pairs(_G) do 530 for k,v in pairs(_G) do
528 if v == instance then 531 if v == instance then
529 geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k)) 532 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
530 end 533 end
531 end 534 end
532 end]] 535 end]]
533 end 536 end
534 537
555 return instance 558 return instance
556 end 559 end
557 local instance = data.instance 560 local instance = data.instance
558 if minor <= data.minor then 561 if minor <= data.minor then
559 -- This one is already obsolete, raise an error. 562 -- This one is already obsolete, raise an error.
560 _G.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) 563 _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)
561 return 564 return
562 end 565 end
563 -- This is an update 566 -- This is an update
564 local oldInstance = {} 567 local oldInstance = {}
565 568
621 end 624 end
622 end 625 end
623 if activateFunc then 626 if activateFunc then
624 safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) 627 safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
625 628
626 if major ~= ACELIBRARY_MAJOR then 629 --[[ if major ~= ACELIBRARY_MAJOR then
627 for k,v in pairs(_G) do 630 for k,v in pairs(_G) do
628 if v == instance then 631 if v == instance then
629 geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k)) 632 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
630 end 633 end
631 end 634 end
632 end 635 end]]
633 else 636 else
634 safecall(oldDeactivateFunc, oldInstance) 637 safecall(oldDeactivateFunc, oldInstance)
635 end 638 end
636 oldInstance = nil 639 oldInstance = nil
637 640