Created
August 1, 2009 02:15
-
-
Save nilium/159547 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Copyright (c) 2009 Noel R. Cower | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | |
]] | |
--[[ | |
name: string name of the class (e.g., 'Object') - cannot be nil | |
superclass: class table or string name for the superclass, if any (nil = no superclass, any other | |
type of value produces an error) - only allows for single inheritance | |
options: table with string=>boolean key-value pairs for options. | |
possible options include: | |
* 'static' -> does not include the constructor call | |
* 'abstract' -> prevents instanciation of this specific class | |
]] | |
function defclass(name, superclass, options) | |
local classtable | |
-- set up options | |
if options then | |
if type(options) == "table" then | |
options = { | |
abstract = (options.abstract==true); | |
static = (options.static==true); | |
} | |
else | |
error("Invalid type for options - must be table") | |
end | |
else | |
options = { | |
abstract = false; | |
static = false; | |
} | |
end | |
if not name then | |
error("Class name is nil") | |
elseif type(name) ~= "string" then | |
error("Invalid type for class name (" .. type(name) .. ")") | |
end | |
if type(superclass) == "string" then | |
superclass = _G[superclass] | |
end | |
-- set up class table | |
if superclass then | |
if type(superclass) ~= "table" then | |
error("Superclass is not a table") | |
end | |
classtable = table_clone(superclass, true) | |
-- inherit previous class's 'static' option | |
options.static = getmetatable(superclass).options.static or options.static | |
else | |
classtable = {} | |
end | |
do -- set metatable for class | |
local classmeta = { | |
name = name; | |
superclass = superclass; | |
options = options; | |
type = "class"; | |
__newindex = function(obj, key, value) | |
local objmeta = getmetatable(obj) | |
if type(value) == "function" then | |
local env = {} | |
setmetatable(env, { | |
["__newindex"] = function(t, k, v) | |
if k == "super" then error("Cannot set 'super'") end | |
_G[k] = v | |
end; | |
["__index"] = function(t, k) | |
if k == "super" then | |
local oldval = (superclass and superclass[key]) | |
if type(oldval) == "function" then | |
return function(...) return superclass[key](...) end | |
else | |
return nil | |
end | |
end | |
return _G[k] | |
end | |
}) | |
setfenv(value, env) | |
objmeta.methods[key] = value | |
else | |
-- check for setter method | |
if type(key) == "string" and 0 < #key and string.match(key, "^%l") then | |
local setterkey = key | |
if 1 < #setterkey then | |
setterkey = string.upper(string.sub(setterkey, 1, 1)) .. string.sub(setterkey, 2) | |
else | |
setterkey = string.upper(setterkey) | |
end | |
local setter = objmeta.methods["set"..setterkey] or objmeta.methods["Set"..setterkey] | |
if setter then | |
setter(obj, value) | |
return | |
end | |
end | |
rawset(obj, key, value) | |
end | |
end; | |
__index = function(obj, key) | |
local objmeta = getmetatable(obj) | |
local method = objmeta.methods[key] | |
if method then return method end | |
-- check for getter method | |
if type(key) == "string" and 0 < #key and string.match(key, "^%l") then | |
local getterkey = key | |
if 1 < #getterkey then | |
getterkey = string.upper(string.sub(getterkey, 1, 1)) .. string.sub(getterkey, 2) | |
else | |
getterkey = string.upper(getterkey) | |
end | |
method = objmeta.methods["get"..getterkey] or objmeta.methods["Get"..getterkey] | |
if method then | |
return method(obj) | |
end | |
end | |
return rawget(obj, key) | |
end; | |
} | |
if not options.static and not options.abstract then | |
function classmeta.__call(class, ...) | |
local instance = table_clone(class, true, false) | |
local meta = table_clone(getmetatable(class)) | |
meta.class = class | |
meta.__call = nil | |
meta.type = "instance" | |
function meta.__newindex(obj, key, value) | |
local objmeta = getmetatable(obj) | |
if type(value) == "function" and objmeta.methods[key] then | |
-- check for method | |
local oldval = objmeta.methods[key] | |
local env = {} | |
setmetatable(env, { | |
["__newindex"] = function(t, k, v) _G[k] = v end; | |
["__index"] = function(t, k) | |
if k == "super" then | |
if type(oldval) == "function" then | |
return function(...) return oldval(...) end | |
else | |
return nil | |
end | |
end | |
return _G[k] | |
end | |
}) | |
setfenv(value, env) | |
objmeta.methods[key] = value | |
else | |
-- check for setter method | |
if type(key) == "string" and 0 < #key and string.match(key, "^%l") then | |
local setterkey = key | |
if 1 < #setterkey then | |
setterkey = string.upper(string.sub(setterkey, 1, 1)) .. string.sub(setterkey, 2) | |
else | |
setterkey = string.upper(setterkey) | |
end | |
local setter = objmeta.methods["set"..setterkey] or objmeta.methods["Set"..setterkey] | |
if setter then | |
setter(obj, value) | |
return | |
end | |
end | |
rawset(obj, key, value) | |
end | |
end | |
setmetatable(instance, meta) | |
return instance | |
end | |
else | |
function classmeta.__call(class, ...) | |
local meta = getmetatable(class) | |
if meta.options.abstract then | |
return error("Cannot instanciate abstract class " .. meta.name) | |
elseif meta.options.static then | |
return error("Cannot instanciate static class " .. meta.name) | |
else | |
return error("Cannot instanciate class " .. meta.name) | |
end | |
end | |
end | |
if superclass then | |
classmeta.methods = table_clone(getmetatable(superclass).methods, true, false) | |
else | |
classmeta.methods = {} | |
end | |
setmetatable(classtable, classmeta) | |
end | |
classes[name] = classtable | |
_G[name] = classtable | |
return classtable | |
end | |
function getclass(name) | |
return classes[name] | |
end | |
--[[ | |
set up environment for the class functions so that table_clone and the classes table is only | |
visible to them | |
]] | |
do | |
local classfunc_meta = {__newindex=_G,__index=_G} | |
local classfunc_env = {classes={}} | |
function classfunc_env.table_clone(t, recurse, meta) | |
local copy = {} | |
if meta then setmetatable(copy, getmetatable(t)) end | |
for key in next, t, nil do | |
local valuecopy = rawget(t, key) | |
local keycopy = key | |
if recurse then | |
local kt = (type(keycopy) == "table") | |
local vt = (type(valuecopy) == "table") | |
if vt then | |
valuecopy = table_clone(valuecopy, true) | |
end | |
if kt then | |
keycopy = table_clone(keycopy, true) | |
end | |
end | |
rawset(copy, keycopy, valuecopy) | |
end | |
return copy | |
end | |
setmetatable(classfunc_env, classfunc_meta) | |
setfenv(defclass, classfunc_env) | |
setfenv(getclass, classfunc_env) | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Copyright (c) 2009 Noel R. Cower | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | |
]] | |
do -- begin Object class | |
defclass("Object", nil, {abstract=true}) | |
function Object:GetMethods() | |
return table.clone(getmetatable(self).methods, false, false) | |
end | |
function Object:RespondsToMessage(msg) | |
local methods = self:GetMethods() | |
for key in next, methods, nil do | |
if key == msg then | |
return true | |
end | |
end | |
return false | |
end | |
function Object:Method(method) | |
return getmetatable(self).methods[method] | |
end | |
function Object:Delegate(method) | |
if type(method) == string then method = self[method] end | |
return function(...) return method(self, ...) end | |
end | |
function Object:GetClassName() | |
return getmetatable(self).name | |
end | |
function Object:GetClass() | |
local meta = getmetatable(self) | |
if meta.type == "class" then | |
return self | |
else | |
return meta.class | |
end | |
end | |
function Object:GetMetaClass() | |
return getmetatable(self) | |
end | |
function Object:GetSuperClass() | |
return getmetatable(self).superclass | |
end | |
function Object:IsKindOf(class) | |
if type(class) == "string" then | |
class = getfenv(0)[class] | |
end | |
local selfclass = self:GetClass() | |
while selfclass do | |
if selfclass == class then | |
return true | |
end | |
selfclass = selfclass:GetSuperClass() | |
end | |
return false | |
end | |
end -- end Object class |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Copyright (c) 2009 Noel R. Cower | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | |
]] | |
do -- some random test crap | |
defclass("Foobar", Object) | |
defclass("Woop", Object) | |
-- default value for m_name | |
Woop.m_name = "Foobar" | |
function Woop:GetName() | |
local f = self.m_name | |
print("Name gotten: "..f) | |
return f | |
end | |
function Woop:SetName(name) | |
self.m_name = name | |
print("Name set: "..self.m_name) | |
end | |
local w = Woop() | |
print("w.name:", w.name) | |
print('w.name = "Wooperton"') | |
w.name = "Wooperton" | |
print("w.name:", w.name) | |
print("w.className:", w.className) | |
print("w:IsKindOf(Woop):", w:IsKindOf(Woop)) | |
print("w:IsKindOf(Object):", w:IsKindOf(Object)) | |
print("w:IsKindOf(Foobar):", w:IsKindOf(Foobar)) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment