diff --git a/lua/spec/class_spec.lua b/lua/spec/class_spec.lua index c7ee412a86e..61487869af3 100644 --- a/lua/spec/class_spec.lua +++ b/lua/spec/class_spec.lua @@ -8,6 +8,10 @@ describe('class', function() return 'Animal' end + function Animal:__tostring() + return self:type() + end + function Animal:size() error('abstract') end @@ -32,6 +36,12 @@ describe('class', function() return 4 end + local SpecialCat = Class.new(Cat) + + function SpecialCat:makeSound() + return 'Meow' + end + describe('class operations', function() it('base class', function () local a1 = Animal() @@ -48,6 +58,56 @@ describe('class', function() end) end) + describe('super', function() + it('assert single proxy', function () + local sc1 = SpecialCat(5) + + assert.is_true(sc1:super() == sc1:super()) + end) + + it('access super methods', function () + local c1 = Cat(5) + + assert.equal('Animal', c1:super():type()) + assert.error(function() return c1:super():numLegs() end) + + local sc1 = SpecialCat(5) + + assert.equal('Cat', sc1:super():type()) + assert.equal(4, sc1:super():numLegs()) + assert.error(function() return sc1:super():makeSound() end) + end) + + it('call super metamethods', function () + local c1 = Cat(5) + + assert.equal('Cat', tostring(c1)) + assert.equal('Animal', tostring(c1:super())) + end) + + it('access instance variable from super', function () + local c1 = Cat(5) + + assert.equal(5, c1:super()._size) + end) + + it('set instance variable from super', function () + local c1 = Cat(5) + + c1:super()._size = 20 + + assert.are_not_equal(5, c1._size) + assert.equal(20, c1._size) + assert.equal(20, c1:size()) + end) + + it('error if getting grandparent class', function () + local sc1 = SpecialCat(5) + + assert.error(function() return sc1:super():super() end, 'Cannot create proxy from a super proxy') + end) + end) + describe('instanceOf', function() it('with same class', function () local c1 = Cat(5) @@ -62,5 +122,20 @@ describe('class', function() local a1 = Animal() assert.is_false(Class.instanceOf(a1, Cat)) end) + + it('with super', function () + local c1 = Cat(5) + assert.is_true(Class.instanceOf(c1:super(), Animal)) + assert.is_false(Class.instanceOf(c1:super(), Cat)) + end) + + it('with super of grandchild', function () + local sc1 = SpecialCat(5) + + local superSc1 = sc1:super() + + assert.is_true(Class.instanceOf(superSc1, Cat)) + assert.is_true(Class.instanceOf(superSc1, Animal)) + end) end) end) diff --git a/lua/wikis/commons/Class.lua b/lua/wikis/commons/Class.lua index 6232174d916..93971d0ec75 100644 --- a/lua/wikis/commons/Class.lua +++ b/lua/wikis/commons/Class.lua @@ -30,6 +30,7 @@ Class.PRIVATE_FUNCTION_SPECIFIER = '_' ---@class BaseClass ---@operator call:self ---@field init fun(self, ...) +---@field super fun(self: self): table ---@param base? table ---@param init? fun(self, ...) @@ -51,6 +52,45 @@ function Class.new(base, init) instance.__index = instance + instance.super = function(object) + if object._superProxy then + return object._superProxy + end + local proxy = {} + local proxyMT = { + __index = function (obj, param) + if param == 'super' then + error('Cannot create proxy from a super proxy') + end + local objVal = rawget(object, param) + if objVal then + return objVal + end + return base and base[param] + end, + _base = base, + _isSuperProxy = true, + __newindex = object, + __add = base and base.__add, + __sub = base and base.__sub, + __mul = base and base.__mul, + __div = base and base.__div, + __mod = base and base.__mod, + __pow = base and base.__pow, + __unm = base and base.__unm, + __concat = base and base.__concat, + __eq = base and base.__eq, + __lt = base and base.__lt, + __le = base and base.__le, + __pairs = base and base.__pairs, + __ipairs = base and base.__ipairs, + __tostring = base and base.__tostring, + } + setmetatable(proxy, proxyMT) + object._superProxy = proxy + return proxy + end + local metatable = {} metatable.__call = function(class_tbl, ...)