diff --git a/CHANGELOG b/CHANGELOG index 451502f..013ed0f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +classtoi: more features than class-toi light; everything is just as fast except class creation. +1.0.0: + - Initial version. + +classtoi-light: only the minimum features. +1.0.0: + - Initial version. + +classtoi-heavy: all the features and the slowness, not recommended anymore. 0.1.4: - :new non-nil custom returns values now replace the usual returned instance - Custom inherited __index metamethods are now called on the correct class instead of the class it is defined in. diff --git a/LICENSE b/LICENSE index b7b4949..5a95835 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2019 Étienne "Reuh" Fildadut +Copyright 2024 Étienne "Reuh" Fildadut Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/classtoi-heavy.lua b/classtoi-heavy.lua new file mode 100644 index 0000000..6144f14 --- /dev/null +++ b/classtoi-heavy.lua @@ -0,0 +1,185 @@ +--- Reuh's class library version 0.1.4. Lua 5.1-5.3 and LuaJit compatible. +-- Objects and classes behavior are identical, so you can consider this to be somewhat prototype-based. +-- Features: +-- * Multiple inheritance with class(parents...) or someclass(newstuff...) +-- * Every metamethods supported +-- * Everything in a class can be redefined (and will be usable in an object) (except __super) +-- * Preserve parents metamethods if already set +-- * Instanciate with class:new(...) +-- * Test inheritance relations with class/object.is(thing, isThis) +-- * Call object:new(...) on instanciation +-- * If object:new(...) returns non-nil values, they will be returned instead of the instance +-- * Call class.__inherit(class, inheritingClass) when creating a class inheriting the previous class. If class.__inherit returns a value, it will +-- be used as the parent table instead of class, allowing some pretty fancy behavior (it's like an inheritance metamethod). +-- * Implements Class Commons +-- * I don't like to do this, but you can redefine every field and metamethod after class creation (except __index and __super). +-- Not features / Things you may want to know: +-- * Will set the metatable of all parent classes/tables if no metatable is set (the table will be its own metatable). +-- * You can't redefine __super (any __super you define will be only avaible by searching in the default __super contents). +-- * Redefining __super or __index after class creation will break everything (though it should be ok with new, is, __call and everything else). +-- * When creating a new class, the methods new, is, __call, __index and __super will always be redefined, so trying to get theses fields +-- will return the default method and not the one you've defined. However, theses defaults will be replaced by yours automatically on instanciation, +-- except __super and __index, but __index should call your __index and act like you expect. __super will however always be the default one +-- and doesn't proxy in any way yours. +-- * __index metamethods will be called with an extra third argument, which is the current class being searched in the inheritance tree. +-- You can safely ignore it. +-- +-- Please also note that the last universal ancestor of the classes (defined here in BaseClass) sets the default __tostring method +-- and __name attribute for nice class-name-printing. Unlike the previoulsy described attributes and methods however, it is done in a normal +-- inheritance-way and can be rewritten without any problem (rewritting __name is especially useful to easily identify your classes). + +-- Lua versions compatibility +local unpack = table.unpack or unpack + +--- All Lua 5.3 metamethods. +local metamethods = { + "__add", "__sub", "__mul", "__div", "__mod", "__pow", "__unm", "__idiv", + "__band", "__bor", "__bxor", "__bnot", "__shl", "__shr", "__tostring", + "__concat", "__len", "__eq", "__lt", "__le", "__index", "__newindex", "__call", "__gc" +} + +local different --- When set, every class __index method will only return a value different from this one. +--- When using a proxied method, contains the last indexed class. +-- This is used for class.is(object); lastIndex will contain class so the is method can react accordingly, without having to be +-- re-set for each class (and therefore doesn't break the "different" mecanism). +local lastIndexed +local makeclass, methods, BaseClass + +--- Classes defaults methods: will be re-set on each class creation. +-- If you overwrite them, you will only be able to call them from an object. +-- Methods starting with a "!" are "proxied methods": they're not present in the class table and will only be called through __index, +-- allowing more control over it (for example having access to lastIndexed). +methods = { + --- Create an object from the class. + -- In pratise, this only subclass the class and call the new method on it, so technically an object is a class. + -- Objects are exaclty like classes, but the __call metamethod will be replaced by one found in the parents, + -- or nil if doesn't exist (so an object is not directly subclassable). + -- (If no __call method is defined in a parent, you won't be able to call the object, but obj.__call will still + -- returns the default (subclassing) method, from one of the parents classes.) + -- The same happens with :new and :is, but since they're not metamethods, if not defined in a parent you won't + -- notice any difference. + -- TL;DR (since I think I'm not really clear): you can redefine __call, :new and :is in parents and use them in objects only. + -- A new object will only be created if calling the method "class:new(...)", if you call for example "class.new(someTable, ...)", it + -- will only execute the constructor defined in the class on someTable. This can be used to execute the parent constructor in a child + -- object, for example. + -- It should also be noted that if the new method returns non-nil value(s), they will be returned instead of the object. + ["!new"] = function(self, ...) + if lastIndexed == self then + local obj, ret = self(), nil + -- Setting class methods to the ones found in parents (we use rawset in order to avoid calling the __newindex metamethod) + different = methods["!new"] rawset(obj, "new", obj:__index("new") or nil) + different = methods["!is"] rawset(obj, "is", obj:__index("is") or nil) + different = methods.__call rawset(obj, "__call", obj:__index("__call") or nil) + different = nil + -- Call constructor + if obj.new ~= methods["!new"] and type(obj.new) == "function" then ret = { obj:new(...) } end + if not ret or #ret == 0 then + return obj + else + return unpack(ret) + end + else + different = methods["!new"] + local new = lastIndexed:__index("new") or nil + different = nil + return new(self, ...) + end + end, + --- Returns true if self is other or a subclass of other. + -- If other is nil, will return true if self is a subclass of the class who called this method. + -- Examples: + -- class.is(a) will return true if a is any class or object + -- (class()):is(class) will return true ((class()) is a subclass of class) + -- (class()).is(class) will return false (class isn't a subclass of (class())) + ["!is"] = function(self, other) + if type(self) ~= "table" then return false end + if other == nil then other = lastIndexed end + if self == other then return true end + for _, t in ipairs(self.__super) do + if t == other then return true end + if t.is == methods["!is"] and t:is(other) then return true end + end + return false + end, + --- Subclass the class: will create a class inheriting self and ... (... will have priority over self). + __call = function(self, ...) + local t = {...} + table.insert(t, self) + return makeclass(unpack(t)) + end, + --- Internal value getting; this follows a precise search order. + -- For example: class(Base1, Base2){stuff} + -- When getting a value from the class, it will be first searched in stuff, then in Base1, then in all Base1 parents, + -- then in Base2, then in Base2 parents. + -- A way to describe this will be search in the latest added tables (from the farthest child to the first parents), from left-to-right. + -- self always refer to the initial table the metamethod was called on, super refers to the class currently being searched for a value. + __index = function(self, k, super) + local proxied = methods["!"..tostring(k)] + if proxied ~= nil and proxied ~= different then -- proxied methods + lastIndexed = self + return proxied + end + for _, t in ipairs((super or self).__super) do -- search in super (will follow __index metamethods) + local val = rawget(t, k) + if val ~= nil and val ~= different then return val end + -- Also covers the case when different search is enabled and the raw t[k] returns an identical value, so the __index metamethod search will be tried for another value. + if getmetatable(t) and getmetatable(t).__index then + val = getmetatable(t).__index(self, k, t) + if val ~= nil and val ~= different then return val end + end + end + end +} + +--- Create a new class width parents ... (left-to-right priority). +function makeclass(...) + local class = { + __super = {} -- parent classes/tables list + } + for k, v in pairs(methods) do -- copy class methods + if k:sub(1, 1) ~= "!" then class[k] = v end -- except proxied methods + end + setmetatable(class, class) + for _, t in ipairs({...}) do -- fill super + if getmetatable(t) == nil then setmetatable(t, t) end -- auto-metatable the table + if type(t.__inherit) == "function" then t = t:__inherit(class) or t end -- call __inherit callback + table.insert(class.__super, t) + end + -- Metamethods query are always raw and thefore don't follow our __index, so we need to manually define thoses. + for _, metamethod in ipairs(metamethods) do + local inSuper = class:__index(metamethod) + if inSuper and rawget(class, metamethod) == nil then + rawset(class, metamethod, inSuper) + end + end + return class +end + +--- The class which will be a parents for all the other classes. +-- We add some pretty-printing default in here. We temporarly remove the metatable in order to avoid a stack overflow. +BaseClass = makeclass { + __name = "class", + __tostring = function(self) + local mt, name = getmetatable(self), self.__name + setmetatable(self, nil) + local str = ("%s (%s)"):format(tostring(name), tostring(self)) + setmetatable(self, mt) + return str + end +} + +--- Class Commons implementation. +-- https://github.com/bartbes/Class-Commons +if common_class and not common then + common = {} + -- class = common.class(name, table, parents...) + function common.class(name, table, ...) + return BaseClass(table, ...){ __name = name, new = table.init } + end + -- instance = common.instance(class, ...) + function common.instance(class, ...) + return class:new(...) + end +end + +return BaseClass diff --git a/classtoi.lua b/classtoi.lua index 6144f14..e6aa342 100644 --- a/classtoi.lua +++ b/classtoi.lua @@ -1,185 +1,169 @@ ---- Reuh's class library version 0.1.4. Lua 5.1-5.3 and LuaJit compatible. --- Objects and classes behavior are identical, so you can consider this to be somewhat prototype-based. --- Features: --- * Multiple inheritance with class(parents...) or someclass(newstuff...) --- * Every metamethods supported --- * Everything in a class can be redefined (and will be usable in an object) (except __super) --- * Preserve parents metamethods if already set --- * Instanciate with class:new(...) --- * Test inheritance relations with class/object.is(thing, isThis) --- * Call object:new(...) on instanciation --- * If object:new(...) returns non-nil values, they will be returned instead of the instance --- * Call class.__inherit(class, inheritingClass) when creating a class inheriting the previous class. If class.__inherit returns a value, it will --- be used as the parent table instead of class, allowing some pretty fancy behavior (it's like an inheritance metamethod). --- * Implements Class Commons --- * I don't like to do this, but you can redefine every field and metamethod after class creation (except __index and __super). --- Not features / Things you may want to know: --- * Will set the metatable of all parent classes/tables if no metatable is set (the table will be its own metatable). --- * You can't redefine __super (any __super you define will be only avaible by searching in the default __super contents). --- * Redefining __super or __index after class creation will break everything (though it should be ok with new, is, __call and everything else). --- * When creating a new class, the methods new, is, __call, __index and __super will always be redefined, so trying to get theses fields --- will return the default method and not the one you've defined. However, theses defaults will be replaced by yours automatically on instanciation, --- except __super and __index, but __index should call your __index and act like you expect. __super will however always be the default one --- and doesn't proxy in any way yours. --- * __index metamethods will be called with an extra third argument, which is the current class being searched in the inheritance tree. --- You can safely ignore it. +--- classtoi v2: finding a sweet spot between classtoi-light and classtoi-heavy +-- aka getlost v2 -- --- Please also note that the last universal ancestor of the classes (defined here in BaseClass) sets the default __tostring method --- and __name attribute for nice class-name-printing. Unlike the previoulsy described attributes and methods however, it is done in a normal --- inheritance-way and can be rewritten without any problem (rewritting __name is especially useful to easily identify your classes). +-- usage: +-- +-- local class = require("class") +-- local Vehicle = class { +-- type = "vehicle", -- class name, optional +-- +-- stability_threshold = 3, -- class variable, also availabe in instances +-- wheel_count = nil, -- doesn't do anything, but i like to keep track of variables that will need to be defined later in a subclass or a constructor +-- +-- init = false, -- abstract class, can't be instanciated +-- +-- is_stable = function(self) -- method, available both in class and instances +-- return self.wheel_count > self.stability_threshold +-- end +-- } +-- +-- local Car = Vehicle { -- subclassing by calling the parent class; multiple inheritance possible by either chaining calls or passing several tables as arguments +-- type = "car", +-- wheel_count = 4, +-- color = nil, +-- init = function(self, color) -- constructor +-- self.color = color +-- end +-- } +-- local car = Car:new("red") -- instancing +-- print(car:is_stable(), car.color) -- true, "red" +-- +-- the default class returned by require("class") contains a few other default methods that will be inherited by all subclasses +-- see line 99 and further for details & documentation +-- +-- design philosophy: +-- do not add feature until we need it +-- what we want to be fast: instance creation, class & instance method call & property acces +-- do not care: class creation +-- +-- and if you're wondering, no i'm not using either classtoi-heavy nor classtoi-light in any current project anymore. --- Lua versions compatibility -local unpack = table.unpack or unpack +--# helper functions #-- ---- All Lua 5.3 metamethods. -local metamethods = { - "__add", "__sub", "__mul", "__div", "__mod", "__pow", "__unm", "__idiv", - "__band", "__bor", "__bxor", "__bnot", "__shl", "__shr", "__tostring", - "__concat", "__len", "__eq", "__lt", "__le", "__index", "__newindex", "__call", "__gc" -} - -local different --- When set, every class __index method will only return a value different from this one. ---- When using a proxied method, contains the last indexed class. --- This is used for class.is(object); lastIndex will contain class so the is method can react accordingly, without having to be --- re-set for each class (and therefore doesn't break the "different" mecanism). -local lastIndexed -local makeclass, methods, BaseClass - ---- Classes defaults methods: will be re-set on each class creation. --- If you overwrite them, you will only be able to call them from an object. --- Methods starting with a "!" are "proxied methods": they're not present in the class table and will only be called through __index, --- allowing more control over it (for example having access to lastIndexed). -methods = { - --- Create an object from the class. - -- In pratise, this only subclass the class and call the new method on it, so technically an object is a class. - -- Objects are exaclty like classes, but the __call metamethod will be replaced by one found in the parents, - -- or nil if doesn't exist (so an object is not directly subclassable). - -- (If no __call method is defined in a parent, you won't be able to call the object, but obj.__call will still - -- returns the default (subclassing) method, from one of the parents classes.) - -- The same happens with :new and :is, but since they're not metamethods, if not defined in a parent you won't - -- notice any difference. - -- TL;DR (since I think I'm not really clear): you can redefine __call, :new and :is in parents and use them in objects only. - -- A new object will only be created if calling the method "class:new(...)", if you call for example "class.new(someTable, ...)", it - -- will only execute the constructor defined in the class on someTable. This can be used to execute the parent constructor in a child - -- object, for example. - -- It should also be noted that if the new method returns non-nil value(s), they will be returned instead of the object. - ["!new"] = function(self, ...) - if lastIndexed == self then - local obj, ret = self(), nil - -- Setting class methods to the ones found in parents (we use rawset in order to avoid calling the __newindex metamethod) - different = methods["!new"] rawset(obj, "new", obj:__index("new") or nil) - different = methods["!is"] rawset(obj, "is", obj:__index("is") or nil) - different = methods.__call rawset(obj, "__call", obj:__index("__call") or nil) - different = nil - -- Call constructor - if obj.new ~= methods["!new"] and type(obj.new) == "function" then ret = { obj:new(...) } end - if not ret or #ret == 0 then - return obj - else - return unpack(ret) - end - else - different = methods["!new"] - local new = lastIndexed:__index("new") or nil - different = nil - return new(self, ...) - end - end, - --- Returns true if self is other or a subclass of other. - -- If other is nil, will return true if self is a subclass of the class who called this method. - -- Examples: - -- class.is(a) will return true if a is any class or object - -- (class()):is(class) will return true ((class()) is a subclass of class) - -- (class()).is(class) will return false (class isn't a subclass of (class())) - ["!is"] = function(self, other) - if type(self) ~= "table" then return false end - if other == nil then other = lastIndexed end - if self == other then return true end - for _, t in ipairs(self.__super) do - if t == other then return true end - if t.is == methods["!is"] and t:is(other) then return true end - end - return false - end, - --- Subclass the class: will create a class inheriting self and ... (... will have priority over self). - __call = function(self, ...) - local t = {...} - table.insert(t, self) - return makeclass(unpack(t)) - end, - --- Internal value getting; this follows a precise search order. - -- For example: class(Base1, Base2){stuff} - -- When getting a value from the class, it will be first searched in stuff, then in Base1, then in all Base1 parents, - -- then in Base2, then in Base2 parents. - -- A way to describe this will be search in the latest added tables (from the farthest child to the first parents), from left-to-right. - -- self always refer to the initial table the metamethod was called on, super refers to the class currently being searched for a value. - __index = function(self, k, super) - local proxied = methods["!"..tostring(k)] - if proxied ~= nil and proxied ~= different then -- proxied methods - lastIndexed = self - return proxied - end - for _, t in ipairs((super or self).__super) do -- search in super (will follow __index metamethods) - local val = rawget(t, k) - if val ~= nil and val ~= different then return val end - -- Also covers the case when different search is enabled and the raw t[k] returns an identical value, so the __index metamethod search will be tried for another value. - if getmetatable(t) and getmetatable(t).__index then - val = getmetatable(t).__index(self, k, t) - if val ~= nil and val ~= different then return val end - end - end - end -} - ---- Create a new class width parents ... (left-to-right priority). -function makeclass(...) - local class = { - __super = {} -- parent classes/tables list - } - for k, v in pairs(methods) do -- copy class methods - if k:sub(1, 1) ~= "!" then class[k] = v end -- except proxied methods - end - setmetatable(class, class) - for _, t in ipairs({...}) do -- fill super - if getmetatable(t) == nil then setmetatable(t, t) end -- auto-metatable the table - if type(t.__inherit) == "function" then t = t:__inherit(class) or t end -- call __inherit callback - table.insert(class.__super, t) - end - -- Metamethods query are always raw and thefore don't follow our __index, so we need to manually define thoses. - for _, metamethod in ipairs(metamethods) do - local inSuper = class:__index(metamethod) - if inSuper and rawget(class, metamethod) == nil then - rawset(class, metamethod, inSuper) - end - end - return class +-- tostring that ignore __tostring methamethod +local function rawtostring(v) + local mt = getmetatable(v) + setmetatable(v, nil) + local str = tostring(v) + setmetatable(v, mt) + return str end ---- The class which will be a parents for all the other classes. --- We add some pretty-printing default in here. We temporarly remove the metatable in order to avoid a stack overflow. -BaseClass = makeclass { - __name = "class", +-- deep table copy, preserve metatable +local function copy(t, cache) + if cache == nil then cache = {} end + if cache[t] then return cache[t] end + local r = {} + cache[t] = r + for k, v in pairs(t) do + r[k] = type(v) == "table" and copy(v, cache) or v + end + return setmetatable(r, getmetatable(t)) +end + +-- add val to set +local function add_to_set(set, val) + if not set[val] then + table.insert(set, val) + set[val] = true + end +end + +--# class creation logic #-- +local new_class, class_mt + +new_class = function(...) + local class = {} + local include = {...} + for i=1, #include do + local parent = include[i] + parent = parent.__included ~= nil and parent:__included(class) or parent + for k, v in pairs(parent) do + class[k] = v + end + end + class.__index = class + setmetatable(class, class_mt) + return class.__created ~= nil and class:__created() or class +end + +class_mt = { + __call = new_class, __tostring = function(self) - local mt, name = getmetatable(self), self.__name - setmetatable(self, nil) - local str = ("%s (%s)"):format(tostring(name), tostring(self)) - setmetatable(self, mt) - return str + local name = self.type and ("class %q"):format(self.type) or "class" + return rawtostring(self):gsub("^table", name) end } +class_mt.__index = class_mt ---- Class Commons implementation. --- https://github.com/bartbes/Class-Commons -if common_class and not common then - common = {} - -- class = common.class(name, table, parents...) - function common.class(name, table, ...) - return BaseClass(table, ...){ __name = name, new = table.init } +--# base class and its contents #-- +-- feel free to redefine these as needed in your own classes; all of these are also optional and can be deleted. +return new_class { + --- instanciate. arguments are passed to the (eventual) constructor :init. + -- behavior undefined when called on an object. + -- set to false to make class non-instanciable (will give unhelpful error on instanciation attempt). + -- obj = class:new(...) + new = function(self, ...) + local obj = setmetatable({}, self) + return obj.init ~= nil and obj:init(...) or obj + end, + --- constructor. arguments are passed from :new. if :init returns a value, it will be returned by :new instead of the self object. + -- set to false to make class abstract (will give unhelpful error on instanciation attempt), redefine in subclass to make non-abstract again. + -- init = function(self, ...) content... end + init = nil, + --- check if the object is an instance of this class. + -- class:is(obj) + -- obj:is(class) + is = function(self, other) -- class:is(obj) + if getmetatable(self) == class_mt then + return getmetatable(other) == self + else + return other:is(self) + end + end, + --- check if the object is an instance of this class or of a class that inherited this class. + -- parentclass:issub(obj) + -- parentclass:issub(class) + -- obj:issub(parentclass) + issub = function(self, other) + if getmetatable(self) == class_mt then + return other.__parents and other.__parents[self] or self:is(other) + else + return other:issub(self) + end + end, + --- check if self is a class + -- class:isclass() + isclass = function(self) + return getmetatable(self) == class_mt + end, + --- called when included in a new class. if it returns a value, it will be used as the included table instead of the self table. + -- default function tracks parent classes and is needed for :issub to work, and returns a deep copy of the included table. + __included = function(self, into) + -- add to parents + if not into.__parents then + into.__parents = {} + end + local __parents = self.__parents + if __parents then + for i=1, #__parents do + add_to_set(into.__parents, __parents[i]) + end + end + add_to_set(into.__parents, self) + -- create copied table + local copied = copy(self) + copied.__parents = nil -- prevent __parents being overwritten + return copied + end, + -- automatically created by __included and needed for :issub to work + -- list and set of classes that are parents of this class: { parent_a, [parent_a] = true, parent_b, [parent_b] = true, ... } + __parents = nil, + --- called on the class when it is created. if it returns a value, it will be returned as the new class instead of the self class. + __created = nil, + --- pretty printing. type is used as the name of the class. + type = "object", + __tostring = function(self) + return rawtostring(self):gsub("^table", self.type) end - -- instance = common.instance(class, ...) - function common.instance(class, ...) - return class:new(...) - end -end - -return BaseClass +} diff --git a/performance/performance.lua b/performance/performance.lua index 30fb6f5..52d61d0 100644 --- a/performance/performance.lua +++ b/performance/performance.lua @@ -1,60 +1,88 @@ -local class = dofile(arg[1] or "../classtoi.lua") +-- can be used to compare different versions: lua performance/performance.lua classtoi-light.lua classtoi.lua classtoi-heavy.lua -local function time(title, f) +-- load libs to test +if not arg[1] then arg[1] = "../classtoi.lua" end + +local totest = {} +local referencesource = arg[1] +for i=1, #arg do + totest[arg[i]] = dofile(arg[i]) +end + +-- setup results +local results = {} +local function time(source, title, f) collectgarbage() local start = os.clock() for i=0, 5e4 do f() end - print(title, os.clock() - start) + local result = os.clock() - start + + if not results[title] then results[title] = {} end + results[title][source] = result end -do - time("class creation", function() +-- perform benchmark +for source, class in pairs(totest) do + do + time(source, "class creation", function() + local A = class() + end) + end + + do local A = class() - end) -end -do - local A = class() + time(source, "instance creation", function() + local a = A:new() + end) + end + + do + local A = class { + foo = function(self) + return 1 + end + } - time("instance creation", function() local a = A:new() - end) + + time(source, "instance method invocation", function() + a:foo() + end) + + time(source, "class method invocation", function() + A:foo() + end) + end + + do + local A = class { + foo = function(self) + return 1 + end + } + + local B = A() + + local b = B:new() + + time(source, "inherited instance method invocation", function() + b:foo() + end) + + time(source, "inherited class method invocation", function() + B:foo() + end) + end end -do - local A = class { - foo = function(self) - return 1 - end - } - - local a = A:new() - - time("instance method invocation", function() - a:foo() - end) - - time("class method invocation", function() - A:foo() - end) -end - -do - local A = class { - foo = function(self) - return 1 - end - } - - local B = A() - - local b = B:new() - - time("inherited instance method invocation", function() - b:foo() - end) - - time("inherited class method invocation", function() - B:foo() - end) +-- display results +for test, sources in pairs(results) do + print(test..":") + for i=1, #arg do + local source = arg[i] + local result = sources[source] + local ratio = math.floor(result/sources[referencesource]*1000)/1000 + print(("\t%s: %ss (x%s)"):format(source, result, ratio)) + end end diff --git a/test/test.lua b/test/test.lua index d804fe6..87b00eb 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1,8 +1,10 @@ +-- note: this will test again the classtoi-heavy featureset only + local T = require("knife-test") -- luacheck: ignore T T("Given the base class", function(T) - local class = dofile(arg[1] or "../classtoi.lua") + local class = dofile(arg[1] or "../classtoi-heavy.lua") -- Inheritance T("When subclassed with an attribute", function(T)