Module:MakeClass

- --						[Deprecated] Module:MakeClass -- -- This module is used to implement makeClass. Code was written by Thundercraft5. -- Deprecated: Please use Module:Class instead. -- [ CONTENTS ]- -- The following list is all the functions this module houses. -- -- * function: isStaticClass(t: table) -- * function: isClass(t: table) -- * function: makeClass(constructor?: function, methods: table, parentClass?: table, options?: table) -- --[ CURRENT REMAINING DEPENDENCIES ]-- -- As this method of use is deprecated, here lists the remaining dependencies: -- -- * Module:Loader -- * Module:String -- * Module:Constructor -- * Module:Sword upgrades/Stats -- -

local table, lu = require('Module:Table'), require('Module:LibU') local p = {}

- -- function: isStaticClass(t: table) -- -- Checks if a class is static. - function p.isStaticClass(t) local mt = getmetatable(t) return type(t) == 'table' and mt and mt.__isClass and not mt.__constructorCalled end

- -- function: isClass(t: table) -- -- Determines if the table is a class. - function p.isClass(t) return type(t) == 'table' and (getmetatable(t) or {}).__isClass or false end

- -- Base helper methods for .makeClass - local baseMethods = {} local baseStaticMethods = {}

function baseMethods:instanceof(class) lu.checkType('instanceof', 1, class, { 'table' }) lu.assertTrue(p.isClass(class), 'class expected', 2) lu.assertTrue(p.isStaticClass(class), 'the class must be static', 2) if not self.parent then return false end local tmp = self while tmp.parent do		if tmp.parent.static == class then return true else tmp = tmp.parent end end return false end

function baseStaticMethods:checkSelf(toCheck) if type(toCheck) ~= 'table' or toCheck.static.constructor ~= self.constructor then local method = getStackName error(string.format( '%sinvalid self object. Did you call .%s with a dot instead of a colon, i.e. ' .. '%s%s(...) instead of %s%s(...), or did you call the method directly from the prototype?', self.name and self.name .. ': ' or '', method, self.name and self.name .. '.' or '.', method, self.name and self.name .. ':' or ':', method ), 3)	end end

function baseStaticMethods:extendPrototype(methods) lu.checkType('extendPrototype', 1, methods, { 'table' }) table.merge(self.prototype, methods) return self end

- -- function: makeClass(constructor?: function, methods: table, parentClass?: table, options?: table) -- -- Creates a class table. - function p.makeClass(constructor, methods, parentClass, options) lu.checkType('makeClass', 1, constructor, { 'function', 'table', 'nil' }) lu.checkType('makeClass', 2, methods, { 'table', 'nil' }) lu.checkType('makeClass', 3, parentClass, { 'table', 'nil' }) lu.checkType('makeClass', 4, options, 'table', true) local options = options or {} local ignoreTableRet = unpack{ options.ignoreTableRet or options.ignoreTable or options.ignore, }	local origConstructor = constructor function tmpFunc(t1) return t1	end function tmpChildFunc(t1, ...) t1.super(...) return t1	end local get, set = 'get', 'set' local methodIsClass = p.isClass(methods) local constructorIsClass = p.isClass(constructor) local tpConstructor = type(constructor) if tpConstructor == 'table' and (methodIsClass or (methods == nil and parentClass == nil and table.length(options) == 0)) and not constructorIsClass then parentClass = methods methods = constructor origConstructor, constructor = nil, nil elseif constructorIsClass then parentClass = constructor origConstructor, constructor = nil, nil elseif tpConstructor == 'table' and type(methods) == 'table' then options = methods methods = nil parentClass = nil elseif tpConstructor == 'function' and methodIsClass then parentClass = methods methods = nil end methods = methods ~= nil and methods or {} if methods.constructor then constructor = methods.constructor elseif not methods.constructor and type(origConstructor) == 'table' then constructor = parentClass and tmpChildFunc or tmpFunc end local util = {} local t2 = {} local staticReservedNames = table.sequenceToSet{ 'parent', 'methods', 'super', '__constructorCalled', '__isClass', 'prototype', 'name', }	local reservedNames = table.sequenceToSet{ 'parent', 'methods', 'static', '__proto__', 'super', '__constructorCalled', '__isClass', 'name', }	local allowedTypes = table.sequenceToSet{ 'static', }	if constructor == nil then constructor = parentClass and tmpChildFunc or tmpFunc end function util:methodType(f, searchType) if searchType then searchType = searchType:lower end if type(f) == 'function' then return 'function' end if type(f) ~= 'table' then return false end if type(f[2]) ~= 'function' then return false end local f1 = f[1] local tpF1 = type(f1) if tpF1 == 'table' then if #f1 == 1 then if searchType then return f1[1] == searchType and searchType or false else return f1[1] end else if searchType then return f1[1] == searchType and searchType or f1[2] == searchType and searchType or false else return unpack(f1) end end elseif tpF1 == 'string' and searchType then return f1 == searchType and searchType or false else return f1		end end function util:filterMethods(t, _type, invert) local ret = {} for k, v in pairs(t) do			local tmp = util:methodType(v, _type) == _type local compare if invert then compare = not tmp else compare = tmp end if compare then ret[k] = v			end end return ret end function util:new(...) local methods = util:filterMethods(methods, 'static', true) local mt = { methods = methods, parent = parentClass, static = t2, name = options.name, __isClass = true, }		for k, v in pairs(t2.prototype) do			methods[k] = v		end local t1 = {} local metatableMethods = {} local nonFuncMethods = util:filterMethods(methods, false) for k, v in pairs(methods) do			if not reservedNames[k] then t1[k] = nonFuncMethods[k] end end if parentClass and (getmetatable(parentClass) or {}).__constructorCalled == true then for k, v in pairs(parentClass) do				if not reservedNames[k] then t1[k] = v				end end end mt.__proto__ = mt.methods mt.super = function(...) if mt.parent == nil then error('the class must have a parent when calling the super constructor', 2) end for k, v in pairs(mt.parent) do				if not reservedNames[k] then t1[k] = v				end end mt.parent = mt.parent:constructor(...) return mt.parent end mt.methods.getmetatable = getmetatable table.merge(mt.methods, baseMethods) mt.__index = function(_, k)			local value if reservedNames[k] then return mt[k] elseif reservedNames[k] and (k == '__isClass' or k == '__constructorCalled') then return nil end if parentClass then value = mt.methods[k] or (mt.parent.methods or {})[k] else value = mt.methods[k] end return value end mt.__newindex = function(_, k, v)			local mType, mType2 = util:methodType(v) if metatableMethods[k] then rawset(mt.methods, k, metatableMethods[k]) rawset(mt, k, metatableMethods[k]) return mt.methods[k] elseif reservedNames[k] then formattedError('class property %q is readonly', 2, k)			elseif mType == 'static' or mType2 == 'static' then return rawset(mt.static, k, v[2]) else return rawset(t1, k, v)			end end local function methodExists(index) local ind = mt.methods[index] or (mt.parent and mt.parent.methods or {})[index] if ind then return true end end metatableMethods.__tostring = function return t1:__tostring(t1) end metatableMethods.__concat = function(a, b) return t1:__concat(a, b) end metatableMethods.__call = t1.__call metatableMethods.__pairs = function return t1:__pairs(t1, t1.methods, t1.static) end metatableMethods.__ipairs = function return t1:__ipairs(t1, t1.methods, t1.static) end metatableMethods.__add = function(a, b) return t1:__add(a, b) end metatableMethods.__sub = function(a, b) return t1:__sub(a, b) end metatableMethods.__mul = function(a, b) return t1:__mul(a, b) end metatableMethods.__div = function(a, b) return t1:__div(a, b) end metatableMethods.__mod = function(a, b) return t1:__mod(a, b) end metatableMethods.__pow = function(a, b) return t1:__pow(a, b) end metatableMethods.__unm = function(a, b) return t1:__unm(a, b) end -- Custom metamethods metatableMethods.__tonumber = function(a) return t1:__tonumber(a) end metatableMethods.__type = function(a, b) return t1:__type(a, b) end metatableMethods.__unpack = function(a, b) return t1:__type(a, b) end metatableMethods.__toboolean = function(a) return t1:__toboolean(a, b) end metatableMethods.__totable = function(a, b, c, d) return t1:__totable(a, b, c, d) end metatableMethods.__tconcat = function(a, b, c, d) return t1:__tconcat(a, b, c, d) end for k, v in pairs(metatableMethods) do			if methodExists(k) then mt[k] = v			elseif k == '__tostring' and t2.name then mt[k] = function(self) return '[class ' .. name .. ']'				end end end mt.__metatable = mt 		setmetatable(t1, mt) local constructorValue = constructor(t1, ...) if t1.parent and p.isStaticClass(t1.parent) then error('makeClass: parent class constructor must be called if the child constructor was called', 2) end mt.__constructorCalled = true local ret if ignoreTableRet or type(constuctorValue) == 'table' then ret = constructorValue else ret = t1		end if ret == nil then ret = t1		end return ret end local metatableMethods, mt = {}, {} mt.parent = (parentClass or {}).static mt.name = options.name mt.methods = util:filterMethods(methods, 'static') or {} mt.prototype = table.merge(util:filterMethods(methods, 'static', true), (parentClass or {}).prototype or {}) or {} mt.methods.constructor, mt.prototype.constructor = util.new, util.new table.merge(mt.methods, baseStaticMethods) mt.__isClass = true mt.__constructorCalled = false mt.__call = util.new mt.__index = function(_, k)		local value if mt.parent ~= nil then value = mt.methods[k] or mt.parent[k] or (mt.parent.methods or {})[k] else value = mt.methods[k] end local mType, mType2 = util:methodType(value) if staticReservedNames[k] then return mt[k] elseif mType == 'static' or mType == 'static' then return value[2] else return value end end mt.__newindex = function(_, k, v)		local function mType(type) return util:methodType(v, type) end if metatableMethods[k] then rawset(mt.methods, k, v)			rawset(mt, k, metatableMethods[k]) elseif staticReservedNames[k] then formattedError('class property %q is readonly', 2, k)		elseif type(v) == 'function' then rawset(mt.methods, k, v)		elseif util:methodType(v, 'static') then rawset(mt, k, v[2]) else rawset(t2, k, v)		end return t2[k] end local function methodExists(index) local ind = (mt.methods or {})[index] or ((mt.parent or {}).methods or {})[index] if ind then return true end end metatableMethods.__tostring = function return t2:__tostring(t2) end metatableMethods.__concat = function(a, b) return t2:__concat(a, b) end metatableMethods.__pairs = function return t2:__pairs(t2, t2.methods) end metatableMethods.__ipairs = function return t2:__ipairs(t2, t2.methods) end metatableMethods.__add = function(a, b) return t2:__add(a, b) end metatableMethods.__sub = function(a, b) return t2:__sub(a, b) end metatableMethods.__mul = function(a, b) return t2:__mul(a, b) end metatableMethods.__div = function(a, b) return t2:__div(a, b) end metatableMethods.__mod = function(a, b) return t2:__mod(a, b) end metatableMethods.__pow = function(a, b) return t2:__pow(a, b) end metatableMethods.__unm = function(a, b) return t2:__unm(a, b) end -- Custom metamethods metatableMethods.__tonumber = function(a) return t2:__tonumber(a) end metatableMethods.__type = function(a, b) return t2:__type(a, b) end metatableMethods.__unpack = function(a, b) return t2:__type(a, b) end metatableMethods.__toboolean = function(a) return t2:__toboolean(a, b) end metatableMethods.__totable = function(a, b, c, d) return t2:__totable(a, b, c, d) end metatableMethods.__tconcat = function(a, b, c, d) return t2:__tconcat(a, b, c, d) end for k, v in pairs(metatableMethods) do		if methodExists(k) then mt[k] = v		end end mt.__metatable = { __isClass = true, __constructorCalled = false, }	return setmetatable(t2, mt) end

function p.test(...) local Class = p.makeClass{ '$', constructor = function error end } Class.prototype.test = function return '$' end Class.prototype.__tostring = function error end return Class end

return p