The class module code:
Utilities.import(getfenv(), "*"); --Imports all modules
--// Special Services:
local loadstring = loadstring;
local table = table;
--// Services:
--// MAIN CODE //--
local module = {};
-------------------
function module:RemoveSelf(...) --Remove Self is a function that allows both : and . to be called
local arguments = {...}; --Packs the arguments
if(module == arguments[1]) then table.remove(arguments, 1) end; --Checks if the first
-- argument is "module", if so then the function was called with a ":"
return arguments;
end;
module.New = function(...)
local rootArguments = module:RemoveSelf(...);
local class = {};
local metaData = {
__type = "utilities-class",
__id = tostring(os.time()) .. "_" .. tostring(math.random(1,10000))
};
local classMetatable = {};
-------------------// Prototype of the class
class.prototype = {};
local prototypeMetaData = {
__type = "utilities-class"
};
-------------------
function class:RemoveSelf(...)
local arguments = {...};
if(arguments[1] == self) then
table.remove(arguments, 1);
end;
return arguments;
end;
-------------------
function class:InstanceOf(...)
local arguments = {...};
return arguments[1]["__holder-id"] == self.__id;
end;
-------------------
function class.prototype:Extends(...)
local arguments = {...};
local root = self;
function self:Super(...)
local result = arguments[1].call(root, ...);
for key, element in pairs(result) do
if(not root[key]) then
root[key] = element;
end;
end;
if(not root.__extensions) then
root.__extensions = {result};
else
table.insert(root.__extensions, result);
end;
for _, currentExtension in pairs(root.__extensions) do
currentExtension:__setid(root.__id);
end;
return result;
end;
return self;
end;
-------------------
function class:Extends(...) --Extends a class
return class.prototype:Extends(...);
end;
-------------------
function class:Super(...) --Calls super if the class is extended
return class.prototype:Super(...);
end;
-------------------
local updateConstructor = function(constructor) --This is the constructor that makes the
-- class, so when .new() is called this is the builder.
local prototypeMetatable = getmetatable(class.prototype) or {};
local builder = function(self, ...)
local callArguments = {...};
local extension = false;
if(type(callArguments[1]) == "table" and callArguments[1]["__class-extension"] == true) then
extension = true;
self = callArguments[1]["__self"];
table.remove(callArguments, 1);
end;
local clonedPrototype;
if(extension == true) then
clonedPrototype = table.clone(self);
clonedPrototype["__holder-id"] = metaData.__id;
else clonedPrototype = self end;
clonedPrototype.__id = tostring(os.time()) .. "_" .. tostring(math.random(1,10000));
function clonedPrototype:__setid(id)
clonedPrototype.__id = id;
end;
local clonedPrototypeMetatable = {};
------------------------
function clonedPrototype:RemoveSelf(...)
local arguments = {...};
if(arguments and arguments[1] and (arguments[1] == self or arguments[1].__id == self.__id)) then
table.remove(arguments, 1);
end;
return arguments;
end;
------------------------
function clonedPrototype:InstanceOf(...)
local arguments = {...};
return self["__holder-id"] == arguments[1].__id;
end;
------------------------
clonedPrototypeMetatable.__index = function(self, k)
if(rawget(self, "__index")) then
return rawget(self, "__index")(self, k) or rawget(self, k);
end;
return rawget(self, k);
end;
------------------------
local bindKeys = {}; --It will bind 2 keys, if you modify one, the other one does
-- aswell
clonedPrototypeMetatable.__newindex = function(self, k, v)
if(extension and k == "__newindex") then
local oldFunction = rawget(self, k);
if(oldFunction) then
local newFunction = function(self, k2, v2)
v(self, k2, v2);
oldFunction(self,k2,v2);
end;
rawset(self, k, newFunction);
else
rawset(self, k, v);
end;
else
rawset(self,k,v);
end;
for _, keys in pairs(bindKeys) do
if(table.includes(keys, k)) then
for _, currentKey in pairs(keys) do
rawset(self,currentKey,v);
end;
end;
end;
if(clonedPrototype.__newindex) then clonedPrototype.__newindex(self,k,v) end;
end;
------------------------
function clonedPrototype:BindKeys(...)
table.insert(bindKeys, {...});
return self;
end;
------------------------// RETURN CONSTRUCTOR
return constructor(clonedPrototype, callArguments) or clonedPrototype;
end;
------------------------
prototypeMetatable.__call = builder;
------------------------
class.prototype.new = function(self, ...)
local arguments = {...};
if(self ~= class.prototype) then table.insert(arguments, 1, self) end;
return class.prototype(table.unpack(arguments));
end;
------------------------
class.prototype.call = function(self, ...)
return class.prototype({
["__class-extension"] = true,
["__self"] = self
}, ...);
end;
setmetatable(class.prototype, prototypeMetatable);
end;
------------------------// class.constructor = * will trigger the constructor.
classMetatable.__newindex = function(self, k, v)
if(k == "constructor") then
updateConstructor(v);
end;
end;
------------------------
classMetatable.__call = function(self, ...)
return class.prototype(...);
end;
------------------------
class.new = function(...)
return class(class:RemoveSelf(...));
end;
------------------------
class.call = function(self, ...)
return class.prototype.call(self, ...);
end;
-------------------//
setmetatable(class, classMetatable);
table.extend(class.prototype, prototypeMetaData);
class.prototype = table.updated(class.prototype, function(self,k,v) --This function will update metaData if it is modified
prototypeMetaData = class.prototype["__extend-table"];
end, true);
prototypeMetaData = table.updated(prototypeMetaData, function(self,k,v) --This function will update class if the metadata
-- table is modified
class.prototype["__set-extend-table"](prototypeMetaData);
end, true);
-------------------
class = table.extend(class, metaData);
class = table.updated(class, function(self,k,v) --This function will update metaData if it is modified
metaData = class["__extend-table"];
if(k == "constructor") then
updateConstructor(v);
rawset(self, k, nil);
end;
end, true);
metaData = table.updated(metaData, function(self,k,v) --This function will update class if the metadata
-- table is modified
class["__set-extend-table"](metaData);
end, true);
return class;
end; module.new = module.New;
return module;