Difference between revisions of "Simple Educative Class Library"

(Add common.instance)
Line 128: Line 128:
 
</source>
 
</source>
  
 +
{{notice|It's likely versions below this are bugged. Sorry about that!}}
 
==Advanced version==
 
==Advanced version==
 
<source lang="lua">
 
<source lang="lua">

Revision as of 10:00, 1 August 2014

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.