Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix & test alternate syntaxes for class inheritance #353

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
32 changes: 16 additions & 16 deletions lua/pl/class.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ end

--- initializes an __instance__ upon creation.
-- @function class:_init
-- @param ... parameters passed to the constructor
-- @param ... input parameters passed to the constructor
-- @usage local Cat = class()
-- function Cat:_init(name)
-- --self:super(name) -- call the ancestor initializer if needed
-- self.name = name
-- end
--
-- local pussycat = Cat("pussycat")
-- print(pussycat.name) --> pussycat
-- local pussycat = Cat("sparkles")
-- print(pussycat.name) --> sparkles

--- checks whether an __instance__ is derived from some class.
-- Works the other way around as `class_of`. It has two ways of using;
Expand Down Expand Up @@ -117,32 +117,33 @@ local function _class_tostring (obj)
return str
end

local function tupdate(td,ts,dont_override)
local function populate(td,ts)
for k,v in pairs(ts) do
if not dont_override or td[k] == nil then
if td[k] == nil then
td[k] = v
end
end
end

local function _class(base,c_arg,c)
-- the class `c` will be the metatable for all its objects,
-- and they will look up their methods in it.
-- the input table `c`, if provided, will become the the class object
local mt = {} -- a metatable for the class to support __call and _handler
-- can define class by passing it a plain table of methods
-- alternatively if base is not a class and no input table c is provided
-- then base is used as a template and converted to a class object
local plain = type(base) == 'table' and not getmetatable(base)
if plain then
if type(c) == "table" then error("base is not a class",3) end
c = base
base = c._base
else
c = c or {}
c._base = base
end

if type(base) == 'table' then
-- our new class is a shallow copy of the base class!
-- but be careful not to wipe out any methods we have been given at this point!
tupdate(c,base,plain)
c._base = base
-- Shallow-copy methods from the base class into our target class being
-- careful not to wipe out any methods explicitly passed as input
populate(c,base)
-- inherit the 'not found' handler, if present
if rawget(c,'_handler') then mt.__index = c._handler end
elseif base ~= nil then
Expand All @@ -153,7 +154,6 @@ local function _class(base,c_arg,c)
setmetatable(c,mt)
if not plain then
if base and rawget(base,'_init') then c._parent_with_init = base end -- For super and inherited init
c._init = nil
end

if base and rawget(base,'_class_init') then
Expand Down Expand Up @@ -209,9 +209,9 @@ end
-- The second form creates a variable `Name` in the current environment set
-- to the class, and also sets `_name`.
-- @function class
-- @param base optional base class
-- @param c_arg optional parameter to class constructor
-- @param c optional table to be used as class
-- @param base optional base class to derive from
-- @param c_arg optional parameter passed to class constructor
-- @param c optional table of methods to be used populate the class
local class
class = setmetatable({},{
__call = function(fun,...)
Expand Down
74 changes: 74 additions & 0 deletions tests/test-class-mix.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
_G.___w = false

local class = require("pl.class")
local tablex = require("pl.tablex")

local function subclass_hack (base, model)
return tablex.update(class(base), model)
end

-- print("duck")
_G.___w = "foo"
local foo = class({
-- attr = "original base",
_init = function (self)
print("Init foo class")
self.attr = "foo"
end,
finish = function (self, arg)
print("Finish foo class "..arg.." as "..self.attr.."\n")
end
})

_G.___w = "bar"
local bar = class(foo, nil, {
_init = function (self, arg)
print("Init bar class")
self:super(arg)
self.attr = "bar"
end
})

_G.___w = "baz"
local baz = class(foo)
function baz:_init (arg)
print("Init baz class")
self:super(arg)
end

_G.___w = "qiz"
local qiz = subclass_hack(foo, {
attr = "qiz",
_init = function (self, arg)
print("Init qiz class")
self:super(arg)
end
})

_G.___w = "zar"
local zar = class({
_base = foo,
_init = function (self, arg)
print("Init zar class")
self._base._init(self, arg)
-- self:super(arg)
self.attr = "zar"
end
})

-- Base class works as expected
foo():finish("1st")

-- This does *not* work as expected, it functions as an instance of foo
bar():finish("2nd")

-- This syntax works, its just cumbersome
local c = baz()
c.attr = "baz"
c:finish("3rd")

-- This hack to do what I expected pl.class to do in the bar class
qiz():finish("4th")

-- This gets the job done, but there is no super() function available
zar():finish("5th")
36 changes: 36 additions & 0 deletions tests/test-class5.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- Another syntax for creating a class inheriting methods from a base class
-- while also using a table for input methods.
local class = require('pl.class')

-- From a plain table of methods
local A = class({
info = "foo",
_init = function (self)
self.info = "A"
end
})

-- From a plain table of methods, inherit from a base
local B = class(A, nil, {
_init = function(self)
print("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
-- self._base._init(self)
-- self:super()
self.info = self.info .. "B"
end
})

-- -- From a base plus a plain table
-- local C = class(B, nil, {
-- -- local C = class({
-- -- _base = B,
-- _init = function(self)
-- -- self._base._init(self)
-- -- self:super()
-- self.info = self.info .. "C"
-- end
-- })

local foo = B()
print("DEBUG:"..foo.info)
assert(foo.info == "AB")