Skip to content

Instantly share code, notes, and snippets.

@markandgo
Last active December 18, 2015 06:09
Show Gist options
  • Save markandgo/5738172 to your computer and use it in GitHub Desktop.
Save markandgo/5738172 to your computer and use it in GitHub Desktop.
A class system with a novel constructor
-- simple class
-------------------------------------------------
local mixin_reserved = {
['new'] = true,
['onMixin'] = true,
['__type'] = true,
['__index'] = true,
}
local base = {__type = 'Object'}
base.__index = base
base._G = _G
setmetatable(base,{__index = _G})
function base.__call(class,...)
local obj = {}
setmetatable(obj,class)
if class.new then
local old_env = getfenv(class.new)
setfenv(class.new,obj)
class.new(obj,...)
setfenv(class.new,old_env)
end
return obj
end
function base.new()
end
function base.type(obj)
return obj.__type
end
function base.typeOf(obj,name)
while obj do
if obj.__type == name then return true end
local meta = getmetatable(obj)
obj = meta and meta.__index
end
return false
end
function base:extend(parent)
parent.__call = base.__call
return setmetatable(self,parent)
end
function base:mixin(source,...)
local recur
recur = function(self,source)
local meta = getmetatable(source)
local index = meta and meta.__index
if index then recur(self,index) end
for i,v in pairs(source) do
if not mixin_reserved[i] then self[i] = v end
end
end
recur(self,source)
if source.onMixin then
source:onMixin(self,...)
end
return self
end
function base.init(class,onInit)
setfenv(onInit,class)
onInit(class)
return class
end
local function new(name)
local class = {__type = name}
class.__index = class
return setmetatable(class,base)
end
class = setmetatable({},{__call = function(_,name) return new(name) end})
function class.extend(child,parent)
if type(child) == 'string' then return class(child):extend(parent) end
base.extend(child,parent)
end
function class.mixin(destination,source)
base.mixin(destination,source)
end
require 'class'
-- class definition
person = class 'person' :init(function(self)
-- class method
function getHeight(self)
print(self:type()..' is '..self.height)
end
-- another method
function getWeight(self)
print(self:type()..' is '..self.weight)
end
-- self is by default refers to class "person"
self.weight = 'fat'
-- self is also implied
self.weight = nil
weight = 'fat'
-- optional constructor used when creating a new object
-- self is also implied here and refers to the new object
function new(self,height)
-- self refers to class object
self.height = height
end
end)
shortperson = class 'shortperson'
:extend(person)
:init(function(self)
-- default class height
height = 'short'
function new(self)
-- recursion and environment test
if not _G.shortperson2 then
weight = 'thin'
_G.shortperson2 = true
_G.shortperson2 = shortperson()
end
end
end)
tall = class 'tall' :init(function(self)
function onMixin(self,dest)
dest.height = self:type()
end
end)
person1 = person 'average'
person1:getHeight()
person1:getWeight()
shortperson1 = shortperson()
shortperson1:getHeight()
shortperson1:getWeight()
shortperson2:getHeight()
shortperson2:getWeight()
person2 = person()
person2:mixin(tall)
person2:getHeight()
person2:getWeight()
assert(person:typeOf 'person')
assert(person1:typeOf 'person')
assert(shortperson:typeOf 'shortperson')
assert(shortperson:typeOf 'person')
assert(shortperson1:typeOf 'shortperson')
assert(shortperson1:typeOf 'person')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment