Difference between revisions of "Simple Educative Class Library"

m (Class keyword)
(No difference)

Revision as of 12:10, 7 March 2015

Also known as SECS (pronounce as 'sex'). This is a simple implementation of classes that works, it's not commented, so it's a nice test of your lua skills.

Remember to check here once in a while, it might have updated.

Basic version

--[[
Copyright (c) 2009 Bart van Strien
 
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.
]]
 
__HAS_SECS_COMPATIBLE_CLASSES__ = true
 
local class_mt = {}
 
function class_mt:__index(key)
	return self.__baseclass[key]
end
 
class = setmetatable({ __baseclass = {} }, class_mt)
 
function class:new(...)
	local c = {}
	c.__baseclass = self
	setmetatable(c, getmetatable(self))
	if c.init then
		c:init(...)
	end
	return c
end

Example

require "class" --this assumes you've saved the code above in class.lua
 
myclass = class:new()
myclass.value = 13
function myclass:setvalue(v)
    self.value = v
end
object = myclass:new()
object:setvalue(128)
--myclass.value is still 13 and object.value is now 128
anotherObject = object:new()
--anotherObject.value is 128

Class Commons enabled basic version

--[[
Copyright (c) 2009-2011 Bart van Strien
 
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.
]]
 
local class_mt = {}
 
function class_mt:__index(key)
    return self.__baseclass[key]
end
 
class = setmetatable({ __baseclass = {} }, class_mt)
 
function class:new(...)
    local c = {}
    c.__baseclass = self
    setmetatable(c, getmetatable(self))
    if c.init then
        c:init(...)
    end
    return c
end
 
if class_commons ~= false then --on by default
	common = {}
 
	function common.class(name, t, parent)
		parent = parent or class
		t.__baseclass = parent
		return setmetatable(t, getmetatable(parent))
	end
 
	function common.instance(class, ...)
		return class:new(...)
	end
end
O.png It's likely versions below this are bugged. Sorry about that!  

Advanced version

--[[
Copyright (c) 2009 Bart van Strien
 
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.
]]
 
__HAS_SECS_COMPATIBLE_CLASSES__ = true
 
local class_mt = {}
 
function class_mt:__index(key)
	if rawget(self, "__baseclass") then
		return self.__baseclass[key]
	end
	return nil
end
 
class = setmetatable({ __baseclass = {} }, class_mt)
 
function class:new(...)
	local c = {}
	c.__baseclass = self
	setmetatable(c, getmetatable(self))
	if c.init then
		c:init(...)
	end
	return c
end
 
function class:convert(t)
	t.__baseclass = self
	setmetatable(t, getmetatable(self))
	return t
end
 
function class:addparent(...)
	if not rawget(self.__baseclass, "__isparenttable") then
		local t = {__isparenttable = true, self.__baseclass, ...}
		local mt = {}
		function mt:__index(key)
			for i, v in pairs(self) do
				if i ~= "__isparenttable" and v[key] then
					return v[key]
				end
			end
			return nil
		end
		self.__baseclass = setmetatable(t, mt)
	else
		for i, v in ipairs{...} do
			table.insert(self.__baseclass, v)
		end
	end
	return self
end
 
function class:setmetamethod(name, value)
	local mt = getmetatable(self)
	local newmt = {}
	for i, v in pairs(mt) do
		newmt[i] = v
	end
	newmt[name] = value
	setmetatable(self, newmt)
end

Example

--NOTE: this example only contains features the basic version doesn't, if you want to see those functions see the basic example
--require it here
 
--create the tables/classes
cl1 = class:new()
cl2 = class:new()
cl3 = {}
 
--fill the tables/classes
cl1.val1 = 12
cl2.val2 = 24
cl3.val3 = 48
 
--create the new merged class
merged = cl1:new()
print(merged.val1, merged.val2, merged.val3) --> 12  nil  nil
merged:addparent(cl2)
print(merged.val1, merged.val2, merged.val3) --> 12  24  nil
--remember cl3 is a normal table, not a class?
class:convert(cl3) --converts the table in place and returns it, so you can use it in expressions
merged:addparent(cl3) --could also be merged:addparent(class:convert(cl3))
print(merged.val1, merged.val2, merged.val3) --> 12  24  48
 
--convert is part of any class, the parent of the converted class is the class where convert is called from
--NOTE: addparent supports multiple parents at once, the way that it's done here is to serve as an example

Full version (AKA overcomplicated)

--[[
Copyright (c) 2009 Bart van Strien
 
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.
]]
 
__HAS_SECS_COMPATIBLE_CLASSES__ = true
 
local class_mt = {}
 
function class_mt:__index(key)
	if rawget(self, "__baseclass") then
		return self.__baseclass[key]
	end
	return nil
end
 
function class_mt:__call(...)
	return self:new(...)
end
 
function class_mt:__add(parent)
	return self:addparent(parent)
end
 
function class_mt:__eq(other)
	if not other.__baseclass or other.__baseclass ~= self.__baseclass then return false end
	for i, v in pairs(self) do
		if not other[i] then
			return false
		end
	end
	for i, v in pairs(other) do
		if not self[i] then
			return false
		end
	end
	return true
end
 
function class_mt:__lt(other)
	if not other.__baseclass then return false end
	if rawget(other.__baseclass, "__isparenttable") then
		for i, v in pairs(other.__baseclass) do
			if self == v or getmetatable(self).__lt(self, v) then return true end
		end
	else
		if self == other.__baseclass or getmetatable(self).__lt(self, other.__baseclass) then return true end
	end
	return false
end
 
function class_mt:__le(other)
	return (self < other or self == other)
end
 
local pt_mt = {}
function pt_mt:__index(key)
	for i, v in pairs(self) do
		if i ~= "__isparenttable" and v[key] then
			return v[key]
		end
	end
	return nil
end
 
class = setmetatable({ __baseclass = {} }, class_mt)
 
function class:new(...)
	local c = {}
	c.__baseclass = self
	setmetatable(c, getmetatable(self))
	if c.init then
		c:init(...)
	end
	return c
end
 
function class:convert(t)
	t.__baseclass = self
	setmetatable(t, getmetatable(self))
	return t
end
 
function class:addparent(...)
	if not rawget(self.__baseclass, "__isparenttable") then
		local t = {__isparenttable = true, self.__baseclass, ...}
		self.__baseclass = setmetatable(t, pt_mt)
	else
		for i, v in ipairs{...} do
			table.insert(self.__baseclass, v)
		end
	end
	return self
end
 
function class:setmetamethod(name, value)
	local mt = getmetatable(self)
	local newmt = {}
	for i, v in pairs(mt) do
		newmt[i] = v
	end
	newmt[name] = value
	setmetatable(self, newmt)
end


Example

--NOTE: This example only contains features the advanced version doesn't have
--require it here
 
cl1 = class() --new is implied
cl2 = class()
 
cl1.val1 = 12
cl2.val2 = 24
 
merged = cl1() + cl2 --yes, we can just add cl2 as if we were doing math
print(merged.val1, merged.val2) --> 12  24
 
if merged > cl1 then
    print("Merged is a derivative of cl1") --which it is, so this is printed
end
if merged > cl2 then
    print("Merged is a derivative of cl2") --it's that as well, so print
end
 
merged2 = cl1() + cl2  --create another derivative with the same parents
merged2.val1 = 36  --we change one value
 
if merged == merged2 then
    print("Merged2 is of the same type as merged") --they are the same type, note this only works when changing, when you add or remove values it is considered as a new class
end

Revised version

Warning, this might update more than the others.

--[[
Copyright (c) 2010 Bart van Strien
 
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.
]]
 
__HAS_SECS_COMPATIBLE_CLASSES__ = true
 
local class_mt = {}
 
function class_mt:__index(key)
	if key == "__private" or (rawget(self, "__private") and self.__private[key]) then
		return nil
	end
	if rawget(self, "__baseclass") then
		return self.__baseclass[key]
	end
	return nil
end
 
function class_mt:__call(...)
	return self:new(...)
end
 
function class_mt:__add(parent)
	return self:addparent(parent)
end
 
function class_mt:__eq(other)
	if not other.__baseclass or other.__baseclass ~= self.__baseclass then return false end
	for i, v in pairs(self) do
		if not other[i] then
			return false
		end
	end
	for i, v in pairs(other) do
		if not self[i] then
			return false
		end
	end
	return true
end
 
function class_mt:__lt(other)
	if not other.__baseclass then return false end
	if rawget(other.__baseclass, "__isparenttable") then
		for i, v in pairs(other.__baseclass) do
			if self == v or getmetatable(self).__lt(self, v) then return true end
		end
	else
		if self == other.__baseclass or getmetatable(self).__lt(self, other.__baseclass) then return true end
	end
	return false
end
 
function class_mt:__le(other)
	return (self < other or self == other)
end
 
local pt_mt = {}
function pt_mt:__index(key)
	for i, v in pairs(self) do
		if i ~= "__isparenttable" and v[key] then
			return v[key]
		end
	end
	return nil
end
 
class = setmetatable({ __baseclass = {} }, class_mt)
 
function class:new(...)
	local c = {}
	c.__baseclass = self
	setmetatable(c, getmetatable(self))
	c:__init(...)
	return c
end
 
function class:__init(...)
	local args = {...}
	if rawget(self, "init") then
		args = {self:init(...) or ...}
	end
	if self.__baseclass.__init then
		self.__baseclass:__init(unpack(args))
	end
end
 
function class:convert(t)
	t.__baseclass = self
	setmetatable(t, getmetatable(self))
	return t
end
 
function class:addparent(...)
	if not rawget(self.__baseclass, "__isparenttable") then
		local t = {__isparenttable = true, self.__baseclass, ...}
		self.__baseclass = setmetatable(t, pt_mt)
	else
		for i, v in ipairs{...} do
			table.insert(self.__baseclass, v)
		end
	end
	return self
end
 
function class:setmetamethod(name, value)
	local mt = getmetatable(self)
	local newmt = {}
	for i, v in pairs(mt) do
		newmt[i] = v
	end
	newmt[name] = value
	setmetatable(self, newmt)
end

Example

Same as full.

See also

Bartbes, creator and maintainer of SECS.