middleclass/MiddleClass.lua

116 lines
4.2 KiB
Lua
Raw Normal View History

2009-10-31 01:14:20 +00:00
-----------------------------------------------------------------------------------
-- MiddleClass.lua
-- Enrique Garc<72>a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 19 Oct 2009
-- Based on YaciCode, from Julien Patte and LuaObject, from S<>bastien Rocca-Serra
-----------------------------------------------------------------------------------
do
2009-11-03 09:02:22 +00:00
local function getSuper(superclass, symbol)
return function(self, ...)
print(superclass.name .. '.' .. symbol)
return superclass[symbol](self, ...)
end
end
local function addSuper(superclass, symbol, method)
local fenv = getfenv(method)
fenv.super = getSuper(superclass, symbol)
--return setfenv(method, setmetatable(
-- {super = getSuper(superclass, symbol)},
-- {__index = fenv, __newindex = fenv}))
2009-10-31 01:14:20 +00:00
end
-- The 'Object' class
Object = {
name = "Object",
2009-11-03 09:02:22 +00:00
superclass = nil,
subclassOf = function(class, other) return false end, -- Object inherits from nothing
__tostring = function(instance) return ("instance of ".. instance.class.name) end,
2009-10-31 01:14:20 +00:00
2009-11-03 09:02:22 +00:00
-- Inverse of instance.instanceOf(class). This never fails - class.made(1) will return false,
-- but 1.instanceOf(class) will return an error
2009-10-31 01:14:20 +00:00
made = function(class, obj)
if type(obj)~="table" or type(obj.class)~="table" then return false end
local c = obj.class
if c==class then return true end
2009-11-03 09:02:22 +00:00
if type(c)~="table" or type(c.subclassOf)~="function" then return false end
return c:subclassOf(class)
2009-10-31 01:14:20 +00:00
end,
2009-11-03 09:02:22 +00:00
-- create a new instance
2009-10-31 01:14:20 +00:00
new = function (class, ...)
2009-11-03 09:02:22 +00:00
local instance = setmetatable({ class = class }, class) -- the class is the instance's metatable
2009-10-31 01:14:20 +00:00
instance:init(...)
return instance
end,
2009-11-03 09:02:22 +00:00
-- creates a subclass
subclass = function(superclass, name)
2009-10-31 01:14:20 +00:00
if type(name)~="string" then name = "Unnamed" end
local theClass = {
name = name,
2009-11-03 09:02:22 +00:00
superclass = superclass,
subclassOf = function(class, other) return (superclass==other or superclass:subclassOf(other)) end
2009-10-31 01:14:20 +00:00
}
2009-11-03 09:02:22 +00:00
-- This may sound weird. Since:
-- a) the class is the instances' metatable (so it must have an __index for looking up the methods) and
-- b) The instance methods are in theClass, then ...
theClass.__index = theClass
2009-10-31 01:14:20 +00:00
2009-11-03 09:02:22 +00:00
-- additionally, set the metatable for theClass
2009-10-31 01:14:20 +00:00
setmetatable(theClass, {
2009-11-03 09:02:22 +00:00
__index = superclass, -- classes look up methods on their superclass
__newindex = function(class, methodName, method) -- when adding new methods, include a "super" function
2009-10-31 01:14:20 +00:00
if type(method) == 'function' then
2009-11-03 09:02:22 +00:00
print('adding super to ' .. class.name .. '.' .. methodName)
local superFunc = function(self, ...)
print(superclass.name .. '.' ..methodName)
return superclass[methodName](self, ...)
end
2009-10-31 01:14:20 +00:00
local fenv = getfenv(method)
2009-11-03 09:02:22 +00:00
local newenv = setmetatable({super = superFunc}, {__index = fenv, __newindex = fenv})
method = setfenv(method, newenv)
2009-10-31 01:14:20 +00:00
end
2009-11-03 09:02:22 +00:00
rawset(class, methodName, method)
2009-10-31 01:14:20 +00:00
end,
__tostring = function() return ("class ".. name) end,
2009-11-03 09:02:22 +00:00
__call = theClass.new
2009-10-31 01:14:20 +00:00
})
2009-11-03 09:02:22 +00:00
-- instance methods go after the setmetatable, so we can use "super"
theClass.init = function(instance,...) super(self) end
theClass.instanceOf = function(instance, class) return class:made(instance) end
2009-10-31 01:14:20 +00:00
return theClass
end,
2009-11-03 09:02:22 +00:00
-- Mixin extension function - simulates very basically ruby's include(module)
-- module is a lua table of functions. The functions will be copied to the class
-- if present in the module, the included() method will be called
includes = function(self, module)
for methodName,method in pairs(module) do
if methodName ~="included" then self[methodName] = method end
end
if type(module.included)=="function" then module:included(self) end
end,
2009-10-31 01:14:20 +00:00
2009-11-03 09:02:22 +00:00
-- end of the init() call chain
2009-10-31 01:14:20 +00:00
init = function(instance, ...) end
}
setmetatable(Object, {
__tostring = function() return ("class Object") end,
__call = newInstance
})
2009-11-03 09:02:22 +00:00
2009-10-31 01:14:20 +00:00
function class(name, baseClass)
baseClass = baseClass or Object
2009-11-03 09:02:22 +00:00
return baseClass:subclass(name)
2009-10-31 01:14:20 +00:00
end
end