Skip to content

Commit

Permalink
Rewrite class system
Browse files Browse the repository at this point in the history
  • Loading branch information
vrld committed Feb 20, 2013
1 parent 5b1b67b commit 9f678bc
Showing 1 changed file with 40 additions and 49 deletions.
89 changes: 40 additions & 49 deletions class.lua
Expand Up @@ -24,70 +24,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
]]--

local function __NULL__() end

-- class "inheritance" by copying functions
local function inherit(class, interface, ...)
if not interface then return end
assert(type(interface) == "table", "Can only inherit from other classes.")
local function include_helper(to, from, seen)
if type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end

-- __index and construct are not overwritten as for them class[name] is defined
for name, func in pairs(interface) do
if not class[name] then
class[name] = func
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if not to[k] then
to[k] = include_helper({}, v, seen)
end
end
for super in pairs(interface.__is_a or {}) do
class.__is_a[super] = true
end
return to
end

return inherit(class, ...)
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end

-- class builder
local function new(args)
local super = {}
local name = '<unnamed class>'
local constructor = args or __NULL__
if type(args) == "table" then
-- nasty hack to check if args.inherits is a table of classes or a class or nil
super = (args.inherits or {}).__is_a and {args.inherits} or args.inherits or {}
name = args.name or name
constructor = args[1] or __NULL__
end
assert(type(constructor) == "function", 'constructor has to be nil or a function')
-- returns a deep copy of `other'
local function clone(other)
return include({}, other)
end

-- build class
local class = {}
local function new(class)
class.__index = class
class.__tostring = function() return ("<instance of %s>"):format(tostring(class)) end
class.construct = constructor or __NULL__
class.inherit = inherit
class.__is_a = {[class] = true}
class.is_a = function(self, other) return not not self.__is_a[other] end
class.init = class.init or function() end
class.include = class.include or include
class.clone = class.clone or clone

-- inherit superclasses (see above)
inherit(class, unpack(super))
-- mixins
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end

for _, other in pairs(inc) do
include(class, other)
end

-- syntactic sugar
local meta = {
__call = function(self, ...)
local obj = {}
setmetatable(obj, self)
self.construct(obj, ...)
return obj
end,
__tostring = function() return name end
}
return setmetatable(class, meta)
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end

-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
local init = prototype.init or (parent or {}).init
return new{name = name, inherits = {prototype, parent}, init}
return include(new(prototype), parent)
end
function common.instance(class, ...)
return class(...)
Expand All @@ -96,5 +87,5 @@ end


-- the module
return setmetatable({new = new, inherit = inherit},
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})

4 comments on commit 9f678bc

@viviicat
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an equivalent to is_a anymore? I really liked that feature.

@vrld
Copy link
Owner Author

@vrld vrld commented on 9f678bc Apr 27, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late reply, but better late than never: There is no equivalent to is_a. But you can easily do it yourself by overwriting the init function along the lines of this (untested code):

function Reflectable(class)
    local init = class.init
    class._is_a = class._is_a or {}
    class.init = function(self, ...)
        self._is_a[class] = true
        return init(self, ...)
    end
    class.is_a = function(self, cls) return self._is_a[cls] end
    return class
end

Foo = Reflectable(class{})
Bar = Reflectable(class{__includes = foo,
    init = function(self) Foo.init(self) end
})

a, b = Foo(), Bar()
print(a:is_a(Foo), a:is_a(Bar)) --> true    nil
print(b:is_a(Foo), b:is_a(Bar)) --> true    true

@cigumo
Copy link

@cigumo cigumo commented on 9f678bc Dec 21, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious. What was the reason to remove the is_a() method?

@vrld
Copy link
Owner Author

@vrld vrld commented on 9f678bc Dec 30, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple: I've never seen myself using it. You could also argue that and is_a() is against OO principles, where you should use polymorphism instead, but I think that's a lame excuse and doesn't really apply in a dynamic language like Lua.

Please sign in to comment.