You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.6 KiB

5 years ago
-------------------------------------------------------------------------------
---------------------------------- NAMESPACE ----------------------------------
-------------------------------------------------------------------------------
local _, ns = ...
-------------------------------------------------------------------------------
------------------------------------ CLASS ------------------------------------
-------------------------------------------------------------------------------
ns.Class = function (name, parent, attrs)
if type(name) ~= 'string' then error('name param must be a string') end
if parent and not ns.IsClass(parent) then error('parent param must be a class') end
local Class = attrs or {}
Class.getters = Class.getters or {}
Class.setters = Class.setters or {}
local instance_metatable = {
__tostring = function (self)
return '<'..name..' instance at '..self.__address..'>'
end,
__index = function (self, index)
-- Walk up the class hierarchy and check for a static value
-- followed by a getter function on each parent class
local _Class = Class
repeat
-- Use rawget to skip __index on Class, we want to
-- check each class object individually
local value = rawget(_Class, index)
if value ~= nil then return value end
local getter = _Class.getters[index]
if getter then return getter(self) end
_Class = _Class.__parent
until _Class == nil
end,
__newindex = function (self, index, value)
local setter = Class.setters[index]
if setter then
setter(self, value)
else
rawset(self, index, value)
end
end
}
setmetatable(Class, {
__call = function (self, ...)
local instance = {}
instance.__class = Class
instance.__address = tostring(instance):gsub("table: ", "", 1)
setmetatable(instance, instance_metatable)
instance:Initialize(...)
return instance
end,
__tostring = function ()
return '<class "'..name..'">'
end,
-- Make parent class attributes accessible on child class objects
__index = parent
})
if parent then
-- Set parent class and allow parent class setters to be used
Class.__parent = parent
setmetatable(Class.setters, { __index = parent.setters })
elseif not Class.Initialize then
-- Add default Initialize() method for base class
Class.Initialize = function (self) end
end
return Class
end
-------------------------------------------------------------------------------
----------------------------------- HELPERS -----------------------------------
-------------------------------------------------------------------------------
ns.IsClass = function (class)
return type(class) == 'table' and class.getters and class.setters
end
ns.IsInstance = function (instance, class)
if type(instance) ~= 'table' then return false end
local function compare (c1, c2)
if c2 == nil then return false end
if c1 == c2 then return true end
return compare(c1, c2.__parent)
end
return compare(class, instance.__class)
end
ns.Clone = function (instance, newattrs)
local clone = {}
for k, v in pairs(instance) do clone[k] = v end
if newattrs then
for k, v in pairs(newattrs) do clone[k] = v end
end
return instance.__class(clone)
end