mirror of
https://github.com/kikito/middleclass.git
synced 2024-11-25 02:44:20 +00:00
Merge branch '3.0'
This commit is contained in:
commit
0a6eb856e6
@ -7,6 +7,6 @@ env:
|
|||||||
install:
|
install:
|
||||||
- sudo apt-get install luajit
|
- sudo apt-get install luajit
|
||||||
- sudo apt-get install luarocks
|
- sudo apt-get install luarocks
|
||||||
- sudo luarocks install telescope
|
- sudo luarocks install busted
|
||||||
|
|
||||||
script: "tsc -f spec/*"
|
script: "busted"
|
||||||
|
23
CHANGELOG.md
Normal file
23
CHANGELOG.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
middleclass changelog
|
||||||
|
====================
|
||||||
|
|
||||||
|
Version 3.0
|
||||||
|
|
||||||
|
* Anything that behaves reasonably like a class can be a class (no internal list of classes)
|
||||||
|
* The `class` global function is now just the return value of `require
|
||||||
|
'middleclass'`. It is a callable table, but works exactly as before.
|
||||||
|
* The global variable `Object` becomes `class.Object`
|
||||||
|
* The global function `instanceOf` becomes `class.Object.isInstanceOf`. Parameter order is reversed.
|
||||||
|
* The global function `subclassOf` becomes `class.Object.static.isSubclassOf`. Parameter order is reversed.
|
||||||
|
* The global function `implements` becomes `class.Object.static.implements`. Parameter order is reversed.
|
||||||
|
* Specs have been translated from telescope to busted
|
||||||
|
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
* Static methods are now separated from instance methods
|
||||||
|
* class.superclass has now become class.super
|
||||||
|
* It's now possible to do class.subclasses
|
||||||
|
* middleclass is now a single file; init.lua has dissapeared
|
||||||
|
* license is changed from BSD to MIT. License included in source FTW
|
||||||
|
|
97
README.md
Normal file
97
README.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
middleclass
|
||||||
|
===========
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/kikito/middleclass.png?branch=master)](https://travis-ci.org/kikito/middleclass)
|
||||||
|
|
||||||
|
A simple OOP library for Lua. It has inheritance, metamethods (operators), class variables and weak mixin support.
|
||||||
|
|
||||||
|
h1. Quick Look
|
||||||
|
|
||||||
|
local class = require 'middleclass'
|
||||||
|
|
||||||
|
local Fruit = class('Fruit') -- 'Fruit' is the class' name
|
||||||
|
|
||||||
|
function Fruit:initialize(sweetness)
|
||||||
|
self.sweetness = sweetness
|
||||||
|
end
|
||||||
|
|
||||||
|
Fruit.static.sweetness_threshold = 5 -- class variable (also admits methods)
|
||||||
|
|
||||||
|
function Fruit:isSweet()
|
||||||
|
return self.sweetness > Fruit.sweetness_threshold
|
||||||
|
end
|
||||||
|
|
||||||
|
local Lemon = class('Lemon', Fruit) -- subclassing
|
||||||
|
|
||||||
|
function Lemon:initialize()
|
||||||
|
Fruit.initialize(self, 1) -- invoking the superclass' initializer
|
||||||
|
end
|
||||||
|
|
||||||
|
local lemon = Lemon:new()
|
||||||
|
|
||||||
|
print(lemon:isSweet()) -- false
|
||||||
|
|
||||||
|
h1. Documentation
|
||||||
|
|
||||||
|
See the "github wiki page":https://github.com/kikito/middleclass/wiki for examples & documentation.
|
||||||
|
|
||||||
|
h1. Installation
|
||||||
|
|
||||||
|
Just copy the middleclass.lua file wherever you want it (for example on a lib/ folder). Then write this in any Lua file where you want to use it:
|
||||||
|
|
||||||
|
local class = require 'middleclass'
|
||||||
|
|
||||||
|
h1. Specs
|
||||||
|
|
||||||
|
This project uses [busted](http://olivinelabs.com/busted/) for its specs. If you want to run the specs, you will have to install it first. Then just execute the following:
|
||||||
|
|
||||||
|
cd /folder/where/the/spec/folder/is
|
||||||
|
busted
|
||||||
|
|
||||||
|
h1. Performance tests
|
||||||
|
|
||||||
|
Middleclass also comes with a small performance test suite. Just run the following command:
|
||||||
|
|
||||||
|
lua performance/run.lua
|
||||||
|
|
||||||
|
h1. Updating from 2.0
|
||||||
|
|
||||||
|
Middleclass used to expose several global variables on the main scope. It does not do that any more.
|
||||||
|
|
||||||
|
`class` is now returned by `require 'middleclass'`, and it is not set globally. So you can do this:
|
||||||
|
|
||||||
|
local class = require 'middleclass'
|
||||||
|
local MyClass = class('MyClass') -- works as before
|
||||||
|
|
||||||
|
`Object` is not a global variable any more. But you can get it from `class.Object`
|
||||||
|
|
||||||
|
local class = require 'middleclass'
|
||||||
|
local Object = class.Object
|
||||||
|
|
||||||
|
print(Object) -- prints 'class Object'
|
||||||
|
|
||||||
|
The public functions `instanceOf`, `subclassOf` and `includes` have been replaced by `Object.isInstanceOf`, `Object.static.isSubclassOf` and `Object.static.includes`.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
instanceOf(MyClass, obj)
|
||||||
|
subclassOf(Object, aClass)
|
||||||
|
includes(aMixin, aClass)
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
obj:isInstanceOf(MyClass)
|
||||||
|
aClass:isSubclassOf(Object)
|
||||||
|
aClass:includes(aMixin)
|
||||||
|
|
||||||
|
The previous code will throw an error if `obj` is not an object, or if `aClass` is not a class (since they will not implement `isInstanceOf`, `isSubclassOf` or `includes`).
|
||||||
|
If you are unsure of wether `obj` and `aClass` are an object or a class, you can use the methods in `Object`. They are prepared to work with random types, not just classes and instances:
|
||||||
|
|
||||||
|
Object.isInstanceOf(obj, MyClass)
|
||||||
|
Object.isSubclassOf(aClass, Object)
|
||||||
|
Object.includes(aClass, aMixin)
|
||||||
|
|
||||||
|
Notice that the parameter order is not the same now as it was in 2.x. Also note the change in naming: `isInstanceOf` instead of `istanceOf`, and `isSubclassOf` instead of `subclassOf`.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
|||||||
h1. MiddleClass
|
|
||||||
|
|
||||||
!https://travis-ci.org/kikito/middleclass.png?branch=master!:https://travis-ci.org/kikito/middleclass
|
|
||||||
|
|
||||||
Lua OOP classes usually end being:
|
|
||||||
|
|
||||||
* multi-file libraries, too difficult to understand
|
|
||||||
* very small libraries, not very powerful
|
|
||||||
|
|
||||||
Middleclass attemps to be a mid-sized library (~140 lines of code, on a single file), with clean, easy to understand code, and yet powerful enough to be used in most cases.
|
|
||||||
|
|
||||||
h1. Documentation
|
|
||||||
|
|
||||||
See the "github wiki page":https://github.com/kikito/middleclass/wiki for examples & documentation.
|
|
||||||
|
|
||||||
h1. Features
|
|
||||||
|
|
||||||
* ~140 lines of code
|
|
||||||
* top-level Object class
|
|
||||||
* all methods are virtual
|
|
||||||
* instance.class returns the instance's class
|
|
||||||
* @Class.name@ returns the class name (a string)
|
|
||||||
* @Class.super@ returns its super class
|
|
||||||
* Class methods can be defined with Class.static.methodname
|
|
||||||
* Subclassing:
|
|
||||||
** @class(name)@ creates a subclass of @Object@
|
|
||||||
** @class(name, Superclass)@ creates a subclass of the class @SuperClass@
|
|
||||||
** @SuperClass:subclass(name)@ also creates a subclass of the class @SuperClass@
|
|
||||||
* Instantiation:
|
|
||||||
** Classes can define an @initialize@ method for initializing new instances. They can accept an arbitrary number of params.
|
|
||||||
** Instances are created by doing @Class:new(params)@ or also @Class(params)@
|
|
||||||
** SuperClass' methods can be used by using this syntax: @SuperClass.initialize(self, params)@.
|
|
||||||
* support for Lua metamethods: just define a method called @__tostring@, @__add@, etc. and your instances will be able to use it.
|
|
||||||
* Mixins:
|
|
||||||
** A very simple mechanism for sharing functionality among a group of classes that are otherwise non-related.
|
|
||||||
** Mixins are just simple lua tables with functions inside them.
|
|
||||||
** @Class:include(mixin)@ will copy the function definitions of @mixin@ to @class@
|
|
||||||
** If @mixin@ contains a function, called @included@, that function will be invoked right after the functions have been copied. It allows for modifying the class more profoundly.
|
|
||||||
* The function @instanceOf(class, instance)@ returns @true@ if @instance@ is an instance of the class @Class@
|
|
||||||
* The function @subclassOf(Superclass, Class)@ returns @true@ if @Class@ is a subclass of @SuperClass@
|
|
||||||
* The function @includes(mixin, Class)@ returns @true@ if @Class@ (or one of its superclasses) includes @mixin@.
|
|
||||||
|
|
||||||
Features left out:
|
|
||||||
|
|
||||||
* metaclasses
|
|
||||||
* classes are not Objects (instances are)
|
|
||||||
* simulating a 'super' keyword (for performance concerns)
|
|
||||||
|
|
||||||
h1. Installation
|
|
||||||
|
|
||||||
Just copy the middleclass.lua file wherever you want it (for example on a lib/ folder). Then write this in any Lua file where you want to use it:
|
|
||||||
|
|
||||||
<pre>require 'middleclass'</pre>
|
|
||||||
|
|
||||||
The @package.path@ variable must be configured so that the folder in which middleclass.lua is copied is available, of course.
|
|
||||||
|
|
||||||
Please make sure that you read the license, too (for your convenience it's now included at the beginning of the middleclass.lua file).
|
|
||||||
|
|
||||||
h1. Specs
|
|
||||||
|
|
||||||
This project uses "telescope":https://github.com/norman/telescope for its specs. If you want to run the specs, you will have to install telescope first. Then just execute the following from the root inspect folder:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
tsc -f spec/*
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
h1. Performance tests
|
|
||||||
|
|
||||||
Middleclass also comes with a small performance test suite. Just run the following command:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
lua performance/run.lua
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
Version 2.0
|
|
||||||
|
|
||||||
* Static methods are now separated from instance methods
|
|
||||||
* class.superclass has now become class.super
|
|
||||||
* It's now possible to do class.subclasses
|
|
||||||
* middleclass is now a single file; init.lua has dissapeared
|
|
||||||
* license is changed from BSD to MIT. License included in source FTW
|
|
||||||
|
|
104
middleclass.lua
104
middleclass.lua
@ -27,71 +27,71 @@ local middleclass = {
|
|||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
|
|
||||||
local function _setClassDictionariesMetatables(klass)
|
local function _setClassDictionariesMetatables(aClass)
|
||||||
local dict = klass.__instanceDict
|
local dict = aClass.__instanceDict
|
||||||
dict.__index = dict
|
dict.__index = dict
|
||||||
|
|
||||||
local super = klass.super
|
local super = aClass.super
|
||||||
if super then
|
if super then
|
||||||
local superStatic = super.static
|
local superStatic = super.static
|
||||||
setmetatable(dict, super.__instanceDict)
|
setmetatable(dict, super.__instanceDict)
|
||||||
setmetatable(klass.static, { __index = function(_,k) return dict[k] or superStatic[k] end })
|
setmetatable(aClass.static, { __index = function(_,k) return dict[k] or superStatic[k] end })
|
||||||
else
|
else
|
||||||
setmetatable(klass.static, { __index = function(_,k) return dict[k] end })
|
setmetatable(aClass.static, { __index = function(_,k) return dict[k] end })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _setClassMetatable(klass)
|
local function _setClassMetatable(aClass)
|
||||||
setmetatable(klass, {
|
setmetatable(aClass, {
|
||||||
__tostring = function() return "class " .. klass.name end,
|
__tostring = function() return "class " .. aClass.name end,
|
||||||
__index = klass.static,
|
__index = aClass.static,
|
||||||
__newindex = klass.__instanceDict,
|
__newindex = aClass.__instanceDict,
|
||||||
__call = function(self, ...) return self:new(...) end
|
__call = function(self, ...) return self:new(...) end
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _createClass(name, super)
|
local function _createClass(name, super)
|
||||||
local klass = { name = name, super = super, static = {}, __mixins = {}, __instanceDict={} }
|
local aClass = { name = name, super = super, static = {}, __mixins = {}, __instanceDict={} }
|
||||||
klass.subclasses = setmetatable({}, {__mode = "k"})
|
aClass.subclasses = setmetatable({}, {__mode = "k"})
|
||||||
|
|
||||||
_setClassDictionariesMetatables(klass)
|
_setClassDictionariesMetatables(aClass)
|
||||||
_setClassMetatable(klass)
|
_setClassMetatable(aClass)
|
||||||
|
|
||||||
return klass
|
return aClass
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _createLookupMetamethod(klass, name)
|
local function _createLookupMetamethod(aClass, name)
|
||||||
return function(...)
|
return function(...)
|
||||||
local method = klass.super[name]
|
local method = aClass.super[name]
|
||||||
assert( type(method)=='function', tostring(klass) .. " doesn't implement metamethod '" .. name .. "'" )
|
assert( type(method)=='function', tostring(aClass) .. " doesn't implement metamethod '" .. name .. "'" )
|
||||||
return method(...)
|
return method(...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _setClassMetamethods(klass)
|
local function _setClassMetamethods(aClass)
|
||||||
for _,m in ipairs(klass.__metamethods) do
|
for _,m in ipairs(aClass.__metamethods) do
|
||||||
klass[m]= _createLookupMetamethod(klass, m)
|
aClass[m]= _createLookupMetamethod(aClass, m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _setDefaultInitializeMethod(klass, super)
|
local function _setDefaultInitializeMethod(aClass, super)
|
||||||
klass.initialize = function(instance, ...)
|
aClass.initialize = function(instance, ...)
|
||||||
return super.initialize(instance, ...)
|
return super.initialize(instance, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _includeMixin(klass, mixin)
|
local function _includeMixin(aClass, mixin)
|
||||||
assert(type(mixin)=='table', "mixin must be a table")
|
assert(type(mixin)=='table', "mixin must be a table")
|
||||||
for name,method in pairs(mixin) do
|
for name,method in pairs(mixin) do
|
||||||
if name ~= "included" and name ~= "static" then klass[name] = method end
|
if name ~= "included" and name ~= "static" then aClass[name] = method end
|
||||||
end
|
end
|
||||||
if mixin.static then
|
if mixin.static then
|
||||||
for name,method in pairs(mixin.static) do
|
for name,method in pairs(mixin.static) do
|
||||||
klass.static[name] = method
|
aClass.static[name] = method
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if type(mixin.included)=="function" then mixin:included(klass) end
|
if type(mixin.included)=="function" then mixin:included(aClass) end
|
||||||
klass.__mixins[mixin] = true
|
aClass.__mixins[mixin] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local Object = _createClass("Object", nil)
|
local Object = _createClass("Object", nil)
|
||||||
@ -100,7 +100,7 @@ Object.static.__metamethods = { '__add', '__call', '__concat', '__div', '__le',
|
|||||||
'__mod', '__mul', '__pow', '__sub', '__tostring', '__unm' }
|
'__mod', '__mul', '__pow', '__sub', '__tostring', '__unm' }
|
||||||
|
|
||||||
function Object.static:allocate()
|
function Object.static:allocate()
|
||||||
assert(self, "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
|
assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
|
||||||
return setmetatable({ class = self }, self.__instanceDict)
|
return setmetatable({ class = self }, self.__instanceDict)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ function Object.static:new(...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Object.static:subclass(name)
|
function Object.static:subclass(name)
|
||||||
assert(self, "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
|
assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
|
||||||
assert(type(name) == "string", "You must provide a name(string) for your class")
|
assert(type(name) == "string", "You must provide a name(string) for your class")
|
||||||
|
|
||||||
local subclass = _createClass(name, self)
|
local subclass = _createClass(name, self)
|
||||||
@ -125,32 +125,48 @@ end
|
|||||||
|
|
||||||
function Object.static:subclassed(other) end
|
function Object.static:subclassed(other) end
|
||||||
|
|
||||||
|
function Object.static:isSubclassOf(other)
|
||||||
|
return type(other) == 'table' and
|
||||||
|
type(self) == 'table' and
|
||||||
|
type(self.super) == 'table' and
|
||||||
|
( self.super == other or
|
||||||
|
type(self.super.isSubclassOf) == 'function' and
|
||||||
|
self.super:isSubclassOf(other)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
function Object.static:include( ... )
|
function Object.static:include( ... )
|
||||||
assert(self, "Make sure you that you are using 'Class:include' instead of 'Class.include'")
|
assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
|
||||||
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
|
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Object.static:includes(mixin)
|
||||||
|
return type(mixin) == 'table' and
|
||||||
|
type(self) == 'table' and
|
||||||
|
type(self.__mixins) == 'table' and
|
||||||
|
( self.__mixins[mixin] or
|
||||||
|
type(self.super) == 'table' and
|
||||||
|
type(self.super.includes) == 'function' and
|
||||||
|
self.super:includes(mixin)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
function Object:initialize() end
|
function Object:initialize() end
|
||||||
|
|
||||||
function Object:__tostring() return "instance of " .. tostring(self.class) end
|
function Object:__tostring() return "instance of " .. tostring(self.class) end
|
||||||
|
|
||||||
function instanceOf(aClass, obj)
|
function Object:isInstanceOf(aClass)
|
||||||
if type(aClass) ~= 'table' or type(obj) ~= 'table' or not obj.class then return false end
|
return type(self) == 'table' and
|
||||||
if obj.class == aClass then return true end
|
type(self.class) == 'table' and
|
||||||
return subclassOf(aClass, obj.class)
|
type(aClass) == 'table' and
|
||||||
|
( aClass == self.class or
|
||||||
|
type(aClass.isSubclassOf) == 'function' and
|
||||||
|
self.class:isSubclassOf(aClass)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function subclassOf(other, aClass)
|
|
||||||
if type(other) ~= 'table' or type(aClass) ~= 'table' or not aClass.super then return false end
|
|
||||||
return aClass.super == other or subclassOf(other, aClass.super)
|
|
||||||
end
|
|
||||||
|
|
||||||
function includes(mixin, aClass)
|
|
||||||
if type(mixin) ~= 'table' or type(aClass) ~= 'table' or not aClass.__mixins then return false end
|
|
||||||
if aClass.__mixins[mixin] then return true end
|
|
||||||
return includes(mixin, aClass.super)
|
|
||||||
end
|
|
||||||
|
|
||||||
function middleclass.class(name, super, ...)
|
function middleclass.class(name, super, ...)
|
||||||
super = super or Object
|
super = super or Object
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
require 'middleclass'
|
local class = require 'middleclass'
|
||||||
|
|
||||||
time = require 'performance/time'
|
time = require 'performance/time'
|
||||||
|
|
||||||
|
@ -1,89 +1,89 @@
|
|||||||
local class = require 'middleclass'
|
local class = require 'middleclass'
|
||||||
local Object = class.Object
|
local Object = class.Object
|
||||||
|
|
||||||
context('Object', function()
|
describe('Object', function()
|
||||||
|
|
||||||
|
|
||||||
context('name', function()
|
describe('name', function()
|
||||||
test('is correctly set', function()
|
it('is correctly set', function()
|
||||||
assert_equal(Object.name, 'Object')
|
assert.equal(Object.name, 'Object')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('tostring', function()
|
describe('tostring', function()
|
||||||
test('returns "class Object"', function()
|
it('returns "class Object"', function()
|
||||||
assert_equal(tostring(Object), 'class Object')
|
assert.equal(tostring(Object), 'class Object')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('()', function()
|
describe('()', function()
|
||||||
test('returns an object, like Object:new()', function()
|
it('returns an object, like Object:new()', function()
|
||||||
local obj = Object()
|
local obj = Object()
|
||||||
assert_true(instanceOf(Object, obj))
|
assert.is_true(obj:isInstanceOf(Object))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('subclass', function()
|
describe('subclass', function()
|
||||||
|
|
||||||
test('throws an error when used without the :', function()
|
it('throws an error when used without the :', function()
|
||||||
assert_error(function() Object.subclass() end)
|
assert.error(function() Object.subclass() end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('throws an error when no name is given', function()
|
it('throws an error when no name is given', function()
|
||||||
assert_error( function() Object:subclass() end)
|
assert.error( function() Object:subclass() end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('when given a class name', function()
|
describe('when given a class name', function()
|
||||||
|
|
||||||
local SubClass
|
local SubClass
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
SubClass = Object:subclass('SubClass')
|
SubClass = Object:subclass('SubClass')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it returns a class with the correct name', function()
|
it('it returns a class with the correct name', function()
|
||||||
assert_equal(SubClass.name, 'SubClass')
|
assert.equal(SubClass.name, 'SubClass')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it returns a class with the correct superclass', function()
|
it('it returns a class with the correct superclass', function()
|
||||||
assert_equal(SubClass.super, Object)
|
assert.equal(SubClass.super, Object)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it includes the subclass in the list of subclasses', function()
|
it('it includes the subclass in the list of subclasses', function()
|
||||||
assert_true(Object.subclasses[SubClass])
|
assert.is_true(Object.subclasses[SubClass])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('instance creation', function()
|
describe('instance creation', function()
|
||||||
|
|
||||||
local SubClass
|
local SubClass
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
SubClass = class('SubClass')
|
SubClass = class('SubClass')
|
||||||
function SubClass:initialize() self.mark=true end
|
function SubClass:initialize() self.mark=true end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('allocate', function()
|
describe('allocate', function()
|
||||||
|
|
||||||
test('allocates instances properly', function()
|
it('allocates instances properly', function()
|
||||||
local instance = SubClass:allocate()
|
local instance = SubClass:allocate()
|
||||||
assert_equal(instance.class, SubClass)
|
assert.equal(instance.class, SubClass)
|
||||||
assert_equal(tostring(instance), "instance of " .. tostring(SubClass))
|
assert.equal(tostring(instance), "instance of " .. tostring(SubClass))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('throws an error when used without the :', function()
|
it('throws an error when used without the :', function()
|
||||||
assert_error(Object.allocate)
|
assert.error(Object.allocate)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('does not call the initializer', function()
|
it('does not call the initializer', function()
|
||||||
local allocated = SubClass:allocate()
|
local allocated = SubClass:allocate()
|
||||||
assert_nil(allocated.mark)
|
assert.is_nil(allocated.mark)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('can be overriden', function()
|
it('can be overriden', function()
|
||||||
|
|
||||||
local previousAllocate = SubClass.static.allocate
|
local previousAllocate = SubClass.static.allocate
|
||||||
|
|
||||||
@ -94,33 +94,249 @@ context('Object', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local allocated = SubClass:allocate()
|
local allocated = SubClass:allocate()
|
||||||
assert_true(allocated.mark)
|
assert.is_true(allocated.mark)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('new', function()
|
describe('new', function()
|
||||||
|
|
||||||
test('initializes instances properly', function()
|
it('initializes instances properly', function()
|
||||||
local instance = SubClass:new()
|
local instance = SubClass:new()
|
||||||
assert_equal(instance.class, SubClass)
|
assert.equal(instance.class, SubClass)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('throws an error when used without the :', function()
|
it('throws an error when used without the :', function()
|
||||||
assert_error(SubClass.new)
|
assert.error(SubClass.new)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('calls the initializer', function()
|
it('calls the initializer', function()
|
||||||
local initialized = SubClass:new()
|
local initialized = SubClass:new()
|
||||||
assert_true(initialized.mark)
|
assert.is_true(initialized.mark)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('isInstanceOf', function()
|
||||||
|
|
||||||
|
describe('nils, integers, strings, tables, and functions', function()
|
||||||
|
local o = Object:new()
|
||||||
|
local primitives = {nil, 1, 'hello', {}, function() end}
|
||||||
|
|
||||||
|
for _,primitive in pairs(primitives) do
|
||||||
|
local theType = type(primitive)
|
||||||
|
describe('A ' .. theType, function()
|
||||||
|
|
||||||
|
local f1 = function() return Object.isInstanceOf(primitive, Object) end
|
||||||
|
local f2 = function() return Object.isInstanceOf(primitive, o) end
|
||||||
|
local f3 = function() return Object.isInstanceOf(primitive, primitive) end
|
||||||
|
|
||||||
|
describe('does not throw errors', function()
|
||||||
|
it('instanceOf(Object, '.. theType ..')', function()
|
||||||
|
assert.not_error(f1)
|
||||||
|
end)
|
||||||
|
it('instanceOf(' .. theType .. ', Object:new())', function()
|
||||||
|
assert.not_error(f2)
|
||||||
|
end)
|
||||||
|
it('instanceOf(' .. theType .. ',' .. theType ..')', function()
|
||||||
|
assert.not_error(f3)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('makes instanceOf return false', function()
|
||||||
|
assert.is_false(f1())
|
||||||
|
assert.is_false(f2())
|
||||||
|
assert.is_false(f3())
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('An instance', function()
|
||||||
|
local Class1 = class('Class1')
|
||||||
|
local Class2 = class('Class2', Class1)
|
||||||
|
local Class3 = class('Class3', Class2)
|
||||||
|
local UnrelatedClass = class('Unrelated')
|
||||||
|
|
||||||
|
local o1, o2, o3 = Class1:new(), Class2:new(), Class3:new()
|
||||||
|
|
||||||
|
it('isInstanceOf(Object)', function()
|
||||||
|
assert.is_true(o1:isInstanceOf(Object))
|
||||||
|
assert.is_true(o2:isInstanceOf(Object))
|
||||||
|
assert.is_true(o3:isInstanceOf(Object))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('isInstanceOf its class', function()
|
||||||
|
assert.is_true(o1:isInstanceOf(Class1))
|
||||||
|
assert.is_true(o2:isInstanceOf(Class2))
|
||||||
|
assert.is_true(o3:isInstanceOf(Class3))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is instanceOf its class\' superclasses', function()
|
||||||
|
assert.is_true(o2:isInstanceOf(Class1))
|
||||||
|
assert.is_true(o3:isInstanceOf(Class1))
|
||||||
|
assert.is_true(o3:isInstanceOf(Class2))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is not instanceOf its class\' subclasses', function()
|
||||||
|
assert.is_false(o1:isInstanceOf(Class2))
|
||||||
|
assert.is_false(o1:isInstanceOf(Class3))
|
||||||
|
assert.is_false(o2:isInstanceOf(Class3))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is not instanceOf an unrelated class', function()
|
||||||
|
assert.is_false(o1:isInstanceOf(UnrelatedClass))
|
||||||
|
assert.is_false(o2:isInstanceOf(UnrelatedClass))
|
||||||
|
assert.is_false(o3:isInstanceOf(UnrelatedClass))
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('isSubclassOf', function()
|
||||||
|
|
||||||
|
describe('nils, integers, etc', function()
|
||||||
|
local primitives = {nil, 1, 'hello', {}, function() end}
|
||||||
|
|
||||||
|
for _,primitive in pairs(primitives) do
|
||||||
|
local theType = type(primitive)
|
||||||
|
describe('A ' .. theType, function()
|
||||||
|
|
||||||
|
local f1 = function() return Object.isSubclassOf(Object, primitive) end
|
||||||
|
local f2 = function() return Object.isSubclassOf(primitive, o) end
|
||||||
|
local f3 = function() return Object.isSubclassOf(primitive, primitive) end
|
||||||
|
|
||||||
|
describe('does not throw errors', function()
|
||||||
|
it('isSubclassOf(Object, '.. theType ..')', function()
|
||||||
|
assert.not_error(f1)
|
||||||
|
end)
|
||||||
|
it('isSubclassOf(' .. theType .. ', Object:new())', function()
|
||||||
|
assert.not_error(f2)
|
||||||
|
end)
|
||||||
|
it('isSubclassOf(' .. theType .. ',' .. theType ..')', function()
|
||||||
|
assert.not_error(f3)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('makes isSubclassOf return false', function()
|
||||||
|
assert.is_false(f1())
|
||||||
|
assert.is_false(f2())
|
||||||
|
assert.is_false(f3())
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('Any class (except Object)', function()
|
||||||
|
local Class1 = class('Class1')
|
||||||
|
local Class2 = class('Class2', Class1)
|
||||||
|
local Class3 = class('Class3', Class2)
|
||||||
|
local UnrelatedClass = class('Unrelated')
|
||||||
|
|
||||||
|
it('isSubclassOf(Object)', function()
|
||||||
|
assert.is_true(Class1:isSubclassOf(Object))
|
||||||
|
assert.is_true(Class2:isSubclassOf(Object))
|
||||||
|
assert.is_true(Class3:isSubclassOf(Object))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is subclassOf its direct superclass', function()
|
||||||
|
assert.is_true(Class2:isSubclassOf(Class1))
|
||||||
|
assert.is_true(Class3:isSubclassOf(Class2))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is subclassOf its ancestors', function()
|
||||||
|
assert.is_true(Class3:isSubclassOf(Class1))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is a subclassOf its class\' subclasses', function()
|
||||||
|
assert.is_true(Class2:isSubclassOf(Class1))
|
||||||
|
assert.is_true(Class3:isSubclassOf(Class1))
|
||||||
|
assert.is_true(Class3:isSubclassOf(Class2))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is not a subclassOf an unrelated class', function()
|
||||||
|
assert.is_false(Class1:isSubclassOf(UnrelatedClass))
|
||||||
|
assert.is_false(Class2:isSubclassOf(UnrelatedClass))
|
||||||
|
assert.is_false(Class3:isSubclassOf(UnrelatedClass))
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('includes', function()
|
||||||
|
|
||||||
|
describe('nils, numbers, etc', function()
|
||||||
|
local o = Object:new()
|
||||||
|
local primitives = {nil, 1, 'hello', {}, function() end}
|
||||||
|
|
||||||
|
for _,primitive in pairs(primitives) do
|
||||||
|
local theType = type(primitive)
|
||||||
|
describe('A ' .. theType, function()
|
||||||
|
|
||||||
|
local f1 = function() return Object.includes(Object, primitive) end
|
||||||
|
local f2 = function() return Object.includes(primitive, o) end
|
||||||
|
local f3 = function() return Object.includes(primitive, primitive) end
|
||||||
|
|
||||||
|
|
||||||
|
describe('don\'t throw errors', function()
|
||||||
|
it('includes(Object, '.. theType ..')', function()
|
||||||
|
assert.not_error(f1)
|
||||||
|
end)
|
||||||
|
it('includes(' .. theType .. ', Object:new())', function()
|
||||||
|
assert.not_error(f2)
|
||||||
|
end)
|
||||||
|
it('includes(' .. theType .. ',' .. theType ..')', function()
|
||||||
|
assert.not_error(f3)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('make includes return false', function()
|
||||||
|
assert.is_false(f1())
|
||||||
|
assert.is_false(f2())
|
||||||
|
assert.is_false(f3())
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
end -- for
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('A class', function()
|
||||||
|
|
||||||
|
local Class1 = class('Class1')
|
||||||
|
local Class2 = class('Class2', Class1)
|
||||||
|
local Class3 = class('Class3', Class2)
|
||||||
|
local UnrelatedClass = class('Unrelated')
|
||||||
|
|
||||||
|
local hasFoo = { foo=function() return 'foo' end }
|
||||||
|
Class1:include(hasFoo)
|
||||||
|
|
||||||
|
it('returns true if it includes a mixin', function()
|
||||||
|
assert.is_true(Class1:includes(hasFoo))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns true if its superclass includes a mixin', function()
|
||||||
|
assert.is_true(Class2:includes(hasFoo))
|
||||||
|
assert.is_true(Class3:includes(hasFoo))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns false otherwise', function()
|
||||||
|
assert.is_false(UnrelatedClass:includes(hasFoo))
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
local class = require 'middleclass'
|
local class = require 'middleclass'
|
||||||
local Object = class.Object
|
local Object = class.Object
|
||||||
|
|
||||||
context('class()', function()
|
describe('class()', function()
|
||||||
|
|
||||||
context('when given no params', function()
|
describe('when given no params', function()
|
||||||
test('it throws an error', function()
|
it('it throws an error', function()
|
||||||
assert_error(class)
|
assert.error(class)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('when given a name', function()
|
describe('when given a name', function()
|
||||||
test('the resulting class has the correct name and Object as its superclass', function()
|
it('the resulting class has the correct name and Object as its superclass', function()
|
||||||
local TheClass = class('TheClass')
|
local TheClass = class('TheClass')
|
||||||
assert_equal(TheClass.name, 'TheClass')
|
assert.equal(TheClass.name, 'TheClass')
|
||||||
assert_equal(TheClass.super, Object)
|
assert.equal(TheClass.super, Object)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('when given a name and a superclass', function()
|
describe('when given a name and a superclass', function()
|
||||||
test('the resulting class has the correct name and superclass', function()
|
it('the resulting class has the correct name and superclass', function()
|
||||||
local TheSuperClass = class('TheSuperClass')
|
local TheSuperClass = class('TheSuperClass')
|
||||||
local TheSubClass = class('TheSubClass', TheSuperClass)
|
local TheSubClass = class('TheSubClass', TheSuperClass)
|
||||||
assert_equal(TheSubClass.name, 'TheSubClass')
|
assert.equal(TheSubClass.name, 'TheSubClass')
|
||||||
assert_equal(TheSubClass.super, TheSuperClass)
|
assert.equal(TheSubClass.super, TheSuperClass)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -1,76 +1,76 @@
|
|||||||
local class = require 'middleclass'
|
local class = require 'middleclass'
|
||||||
|
|
||||||
context('A Class', function()
|
describe('A Class', function()
|
||||||
|
|
||||||
context('Default stuff', function()
|
describe('Default stuff', function()
|
||||||
|
|
||||||
local AClass
|
local AClass
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
AClass = class('AClass')
|
AClass = class('AClass')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('name', function()
|
describe('name', function()
|
||||||
test('is correctly set', function()
|
it('is correctly set', function()
|
||||||
assert_equal(AClass.name, 'AClass')
|
assert.equal(AClass.name, 'AClass')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('tostring', function()
|
describe('tostring', function()
|
||||||
test('returns "class *name*"', function()
|
it('returns "class *name*"', function()
|
||||||
assert_equal(tostring(AClass), 'class AClass')
|
assert.equal(tostring(AClass), 'class AClass')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('()', function()
|
describe('()', function()
|
||||||
test('returns an object, like Class:new()', function()
|
it('returns an object, like Class:new()', function()
|
||||||
local obj = AClass()
|
local obj = AClass()
|
||||||
assert_equal(obj.class, AClass)
|
assert.equal(obj.class, AClass)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('include', function()
|
describe('include', function()
|
||||||
test('throws an error when used without the :', function()
|
it('throws an error when used without the :', function()
|
||||||
assert_error(function() AClass.include() end)
|
assert.error(function() AClass.include() end)
|
||||||
end)
|
end)
|
||||||
test('throws an error when passed a non-table:', function()
|
it('throws an error when passed a non-table:', function()
|
||||||
assert_error(function() AClass:include(1) end)
|
assert.error(function() AClass:include(1) end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('subclass', function()
|
describe('subclass', function()
|
||||||
|
|
||||||
test('throws an error when used without the :', function()
|
it('throws an error when used without the :', function()
|
||||||
assert_error(function() AClass.subclass() end)
|
assert.error(function() AClass.subclass() end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('throws an error when no name is given', function()
|
it('throws an error when no name is given', function()
|
||||||
assert_error( function() AClass:subclass() end)
|
assert.error( function() AClass:subclass() end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('when given a subclass name', function()
|
describe('when given a subclass name', function()
|
||||||
|
|
||||||
local SubClass
|
local SubClass
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
function AClass.static:subclassed(other) self.static.child = other end
|
function AClass.static:subclassed(other) self.static.child = other end
|
||||||
SubClass = AClass:subclass('SubClass')
|
SubClass = AClass:subclass('SubClass')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it returns a class with the correct name', function()
|
it('it returns a class with the correct name', function()
|
||||||
assert_equal(SubClass.name, 'SubClass')
|
assert.equal(SubClass.name, 'SubClass')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it returns a class with the correct superclass', function()
|
it('it returns a class with the correct superclass', function()
|
||||||
assert_equal(SubClass.super, AClass)
|
assert.equal(SubClass.super, AClass)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it invokes the subclassed hook method', function()
|
it('it invokes the subclassed hook method', function()
|
||||||
assert_equal(SubClass, AClass.child)
|
assert.equal(SubClass, AClass.child)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('it includes the subclass in the list of subclasses', function()
|
it('it includes the subclass in the list of subclasses', function()
|
||||||
assert_true(AClass.subclasses[SubClass])
|
assert.is_true(AClass.subclasses[SubClass])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
@ -81,56 +81,56 @@ context('A Class', function()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
context('attributes', function()
|
describe('attributes', function()
|
||||||
|
|
||||||
local A, B
|
local A, B
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
A = class('A')
|
A = class('A')
|
||||||
A.static.foo = 'foo'
|
A.static.foo = 'foo'
|
||||||
|
|
||||||
B = class('B', A)
|
B = class('B', A)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available after being initialized', function()
|
it('are available after being initialized', function()
|
||||||
assert_equal(A.foo, 'foo')
|
assert.equal(A.foo, 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available for subclasses', function()
|
it('are available for subclasses', function()
|
||||||
assert_equal(B.foo, 'foo')
|
assert.equal(B.foo, 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are overridable by subclasses, without affecting the superclasses', function()
|
it('are overridable by subclasses, without affecting the superclasses', function()
|
||||||
B.static.foo = 'chunky bacon'
|
B.static.foo = 'chunky bacon'
|
||||||
assert_equal(B.foo, 'chunky bacon')
|
assert.equal(B.foo, 'chunky bacon')
|
||||||
assert_equal(A.foo, 'foo')
|
assert.equal(A.foo, 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('methods', function()
|
describe('methods', function()
|
||||||
|
|
||||||
local A, B
|
local A, B
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
A = class('A')
|
A = class('A')
|
||||||
function A.static:foo() return 'foo' end
|
function A.static:foo() return 'foo' end
|
||||||
|
|
||||||
B = class('B', A)
|
B = class('B', A)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available after being initialized', function()
|
it('are available after being initialized', function()
|
||||||
assert_equal(A:foo(), 'foo')
|
assert.equal(A:foo(), 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available for subclasses', function()
|
it('are available for subclasses', function()
|
||||||
assert_equal(B:foo(), 'foo')
|
assert.equal(B:foo(), 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are overridable by subclasses, without affecting the superclasses', function()
|
it('are overridable by subclasses, without affecting the superclasses', function()
|
||||||
function B.static:foo() return 'chunky bacon' end
|
function B.static:foo() return 'chunky bacon' end
|
||||||
assert_equal(B:foo(), 'chunky bacon')
|
assert.equal(B:foo(), 'chunky bacon')
|
||||||
assert_equal(A:foo(), 'foo')
|
assert.equal(A:foo(), 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
local class = require 'middleclass'
|
|
||||||
local Object = class.Object
|
|
||||||
|
|
||||||
context('includes', function()
|
|
||||||
|
|
||||||
context('nils, numbers, etc', function()
|
|
||||||
local o = Object:new()
|
|
||||||
local primitives = {nil, 1, 'hello', {}, function() end}
|
|
||||||
|
|
||||||
for _,primitive in pairs(primitives) do
|
|
||||||
local theType = type(primitive)
|
|
||||||
context('A ' .. theType, function()
|
|
||||||
|
|
||||||
local f1 = function() return includes(Object, primitive) end
|
|
||||||
local f2 = function() return includes(primitive, o) end
|
|
||||||
local f3 = function() return includes(primitive, primitive) end
|
|
||||||
|
|
||||||
context('don\'t throw errors', function()
|
|
||||||
test('includes(Object, '.. theType ..')', function()
|
|
||||||
assert_not_error(f1)
|
|
||||||
end)
|
|
||||||
test('includes(' .. theType .. ', Object:new())', function()
|
|
||||||
assert_not_error(f2)
|
|
||||||
end)
|
|
||||||
test('includes(' .. theType .. ',' .. theType ..')', function()
|
|
||||||
assert_not_error(f3)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('make includes return false', function()
|
|
||||||
assert_false(f1())
|
|
||||||
assert_false(f2())
|
|
||||||
assert_false(f3())
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
end -- for
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
context('A class', function()
|
|
||||||
|
|
||||||
local Class1 = class('Class1')
|
|
||||||
local Class2 = class('Class2', Class1)
|
|
||||||
local Class3 = class('Class3', Class2)
|
|
||||||
local UnrelatedClass = class('Unrelated')
|
|
||||||
|
|
||||||
local hasFoo = { foo=function() return 'foo' end }
|
|
||||||
Class1:include(hasFoo)
|
|
||||||
|
|
||||||
test('returns true if it includes a mixin', function()
|
|
||||||
assert_true(includes(hasFoo, Class1))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('returns true if its superclass includes a mixin', function()
|
|
||||||
assert_true(includes(hasFoo, Class2))
|
|
||||||
assert_true(includes(hasFoo, Class3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('returns false otherwise', function()
|
|
||||||
assert_false(includes(hasFoo, UnrelatedClass))
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
|||||||
local class = require 'middleclass'
|
|
||||||
local Object = class.Object
|
|
||||||
|
|
||||||
context('instanceOf', function()
|
|
||||||
|
|
||||||
context('nils, integers, strings, tables, and functions', function()
|
|
||||||
local o = Object:new()
|
|
||||||
local primitives = {nil, 1, 'hello', {}, function() end}
|
|
||||||
|
|
||||||
for _,primitive in pairs(primitives) do
|
|
||||||
local theType = type(primitive)
|
|
||||||
context('A ' .. theType, function()
|
|
||||||
|
|
||||||
local f1 = function() return instanceOf(Object, primitive) end
|
|
||||||
local f2 = function() return instanceOf(primitive, o) end
|
|
||||||
local f3 = function() return instanceOf(primitive, primitive) end
|
|
||||||
|
|
||||||
context('does not throw errors', function()
|
|
||||||
test('instanceOf(Object, '.. theType ..')', function()
|
|
||||||
assert_not_error(f1)
|
|
||||||
end)
|
|
||||||
test('instanceOf(' .. theType .. ', Object:new())', function()
|
|
||||||
assert_not_error(f2)
|
|
||||||
end)
|
|
||||||
test('instanceOf(' .. theType .. ',' .. theType ..')', function()
|
|
||||||
assert_not_error(f3)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('makes instanceOf return false', function()
|
|
||||||
assert_false(f1())
|
|
||||||
assert_false(f2())
|
|
||||||
assert_false(f3())
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
context('An instance', function()
|
|
||||||
local Class1 = class('Class1')
|
|
||||||
local Class2 = class('Class2', Class1)
|
|
||||||
local Class3 = class('Class3', Class2)
|
|
||||||
local UnrelatedClass = class('Unrelated')
|
|
||||||
|
|
||||||
local o1, o2, o3 = Class1:new(), Class2:new(), Class3:new()
|
|
||||||
|
|
||||||
test('is instanceOf(Object)', function()
|
|
||||||
assert_true(instanceOf(Object, o1))
|
|
||||||
assert_true(instanceOf(Object, o2))
|
|
||||||
assert_true(instanceOf(Object, o3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is instanceOf its class', function()
|
|
||||||
assert_true(instanceOf(Class1, o1))
|
|
||||||
assert_true(instanceOf(Class2, o2))
|
|
||||||
assert_true(instanceOf(Class3, o3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is instanceOf its class\' superclasses', function()
|
|
||||||
assert_true(instanceOf(Class1, o2))
|
|
||||||
assert_true(instanceOf(Class1, o3))
|
|
||||||
assert_true(instanceOf(Class2, o3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is not instanceOf its class\' subclasses', function()
|
|
||||||
assert_false(instanceOf(Class2, o1))
|
|
||||||
assert_false(instanceOf(Class3, o1))
|
|
||||||
assert_false(instanceOf(Class3, o2))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is not instanceOf an unrelated class', function()
|
|
||||||
assert_false(instanceOf(UnrelatedClass, o1))
|
|
||||||
assert_false(instanceOf(UnrelatedClass, o2))
|
|
||||||
assert_false(instanceOf(UnrelatedClass, o3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
@ -1,24 +1,24 @@
|
|||||||
local class = require 'middleclass'
|
local class = require 'middleclass'
|
||||||
|
|
||||||
context('An instance', function()
|
describe('An instance', function()
|
||||||
|
|
||||||
context('attributes', function()
|
describe('attributes', function()
|
||||||
|
|
||||||
local Person
|
local Person
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
Person = class('Person')
|
Person = class('Person')
|
||||||
function Person:initialize(name)
|
function Person:initialize(name)
|
||||||
self.name = name
|
self.name = name
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available in the instance after being initialized', function()
|
it('are available in the instance after being initialized', function()
|
||||||
local bob = Person:new('bob')
|
local bob = Person:new('bob')
|
||||||
assert_equal(bob.name, 'bob')
|
assert.equal(bob.name, 'bob')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available in the instance after being initialized by a superclass', function()
|
it('are available in the instance after being initialized by a superclass', function()
|
||||||
local AgedPerson = class('AgedPerson', Person)
|
local AgedPerson = class('AgedPerson', Person)
|
||||||
function AgedPerson:initialize(name, age)
|
function AgedPerson:initialize(name, age)
|
||||||
Person.initialize(self, name)
|
Person.initialize(self, name)
|
||||||
@ -26,17 +26,17 @@ context('An instance', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local pete = AgedPerson:new('pete', 31)
|
local pete = AgedPerson:new('pete', 31)
|
||||||
assert_equal(pete.name, 'pete')
|
assert.equal(pete.name, 'pete')
|
||||||
assert_equal(pete.age, 31)
|
assert.equal(pete.age, 31)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('methods', function()
|
describe('methods', function()
|
||||||
|
|
||||||
local A, B, a, b
|
local A, B, a, b
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
A = class('A')
|
A = class('A')
|
||||||
function A:overridden() return 'foo' end
|
function A:overridden() return 'foo' end
|
||||||
function A:regular() return 'regular' end
|
function A:regular() return 'regular' end
|
||||||
@ -48,16 +48,16 @@ context('An instance', function()
|
|||||||
b = B:new()
|
b = B:new()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are available for any instance', function()
|
it('are available for any instance', function()
|
||||||
assert_equal(a:overridden(), 'foo')
|
assert.equal(a:overridden(), 'foo')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are inheritable', function()
|
it('are inheritable', function()
|
||||||
assert_equal(b:regular(), 'regular')
|
assert.equal(b:regular(), 'regular')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('are overridable', function()
|
it('are overridable', function()
|
||||||
assert_equal(b:overridden(), 'bar')
|
assert.equal(b:overridden(), 'bar')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
local class = require 'middleclass'
|
local class = require 'middleclass'
|
||||||
local Object = class.Object
|
local Object = class.Object
|
||||||
|
|
||||||
context('Metamethods', function()
|
describe('Metamethods', function()
|
||||||
|
|
||||||
context('Custom Metamethods', function()
|
describe('Custom Metamethods', function()
|
||||||
-- Tests all metamethods. Note that __len is missing (lua makes table length unoverridable)
|
-- Tests all metamethods. Note that __len is missing (lua makes table length unoverridable)
|
||||||
-- I'll use a() to note the length of vector "a" (I would have preferred to use #a, but it's not possible)
|
-- I'll use a() to note the length of vector "a" (I would have preferred to use #a, but it's not possible)
|
||||||
-- I'll be using 'a' instead of 'self' on this example since it is shorter
|
-- I'll be using 'a' instead of 'self' on this example since it is shorter
|
||||||
@ -45,12 +45,12 @@ context('Metamethods', function()
|
|||||||
__mul = { 4*a, Vector(4,8,12) }--,
|
__mul = { 4*a, Vector(4,8,12) }--,
|
||||||
--__index = { b[1], 3 }
|
--__index = { b[1], 3 }
|
||||||
}) do
|
}) do
|
||||||
test(metamethod .. ' works as expected', function()
|
it(metamethod .. ' works as expected', function()
|
||||||
assert_equal(values[1], values[2])
|
assert.equal(values[1], values[2])
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
context('Inherited Metamethods', function()
|
describe('Inherited Metamethods', function()
|
||||||
local Vector2= class('Vector2', Vector)
|
local Vector2= class('Vector2', Vector)
|
||||||
function Vector2:initialize(x,y,z) Vector.initialize(self,x,y,z) end
|
function Vector2:initialize(x,y,z) Vector.initialize(self,x,y,z) end
|
||||||
|
|
||||||
@ -70,36 +70,36 @@ context('Metamethods', function()
|
|||||||
__pow = { c^d, Vector(0,0,0) },
|
__pow = { c^d, Vector(0,0,0) },
|
||||||
__mul = { 4*c, Vector(4,8,12) }
|
__mul = { 4*c, Vector(4,8,12) }
|
||||||
}) do
|
}) do
|
||||||
test(metamethod .. ' works as expected', function()
|
it(metamethod .. ' works as expected', function()
|
||||||
assert_equal(values[1], values[2])
|
assert.equal(values[1], values[2])
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('Default Metamethods', function()
|
describe('Default Metamethods', function()
|
||||||
|
|
||||||
local Peter, peter
|
local Peter, peter
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
Peter = class('Peter')
|
Peter = class('Peter')
|
||||||
peter = Peter()
|
peter = Peter()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('A Class', function()
|
describe('A Class', function()
|
||||||
test('has a call metamethod properly set', function()
|
it('has a call metamethod properly set', function()
|
||||||
assert_true(instanceOf(Peter, peter))
|
assert.is_true(peter:isInstanceOf(Peter))
|
||||||
end)
|
end)
|
||||||
test('has a tostring metamethod properly set', function()
|
it('has a tostring metamethod properly set', function()
|
||||||
assert_equal(tostring(Peter), 'class Peter')
|
assert.equal(tostring(Peter), 'class Peter')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context('An instance', function()
|
describe('An instance', function()
|
||||||
test('has a tostring metamethod, returning a different result from Object.__tostring', function()
|
it('has a tostring metamethod, returning a different result from Object.__tostring', function()
|
||||||
assert_not_equal(Peter.__tostring, Object.__tostring)
|
assert.not_equal(Peter.__tostring, Object.__tostring)
|
||||||
assert_equal(tostring(peter), 'instance of class Peter')
|
assert.equal(tostring(peter), 'instance of class Peter')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
local class = require 'middleclass'
|
local class = require 'middleclass'
|
||||||
|
|
||||||
context('A Mixin', function()
|
describe('A Mixin', function()
|
||||||
|
|
||||||
local Mixin1, Mixin2, Class1, Class2
|
local Mixin1, Mixin2, Class1, Class2
|
||||||
|
|
||||||
before(function()
|
before_each(function()
|
||||||
Mixin1, Mixin2 = {},{}
|
Mixin1, Mixin2 = {},{}
|
||||||
|
|
||||||
function Mixin1:included(theClass) theClass.includesMixin1 = true end
|
function Mixin1:included(theClass) theClass.includesMixin1 = true end
|
||||||
@ -23,30 +23,30 @@ context('A Mixin', function()
|
|||||||
function Class2:bar2() return 'bar2' end
|
function Class2:bar2() return 'bar2' end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('invokes the "included" method when included', function()
|
it('invokes the "included" method when included', function()
|
||||||
assert_true(Class1.includesMixin1)
|
assert.is_true(Class1.includesMixin1)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('has all its functions (except "included") copied to its target class', function()
|
it('has all its functions (except "included") copied to its target class', function()
|
||||||
assert_equal(Class1:bar(), 'bar')
|
assert.equal(Class1:bar(), 'bar')
|
||||||
assert_nil(Class1.included)
|
assert.is_nil(Class1.included)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('makes its functions available to subclasses', function()
|
it('makes its functions available to subclasses', function()
|
||||||
assert_equal(Class2:baz(), 'baz')
|
assert.equal(Class2:baz(), 'baz')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('allows overriding of methods in the same class', function()
|
it('allows overriding of methods in the same class', function()
|
||||||
assert_equal(Class2:foo(), 'foo1')
|
assert.equal(Class2:foo(), 'foo1')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('allows overriding of methods on subclasses', function()
|
it('allows overriding of methods on subclasses', function()
|
||||||
assert_equal(Class2:bar2(), 'bar2')
|
assert.equal(Class2:bar2(), 'bar2')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test('makes new static methods available in classes', function()
|
it('makes new static methods available in classes', function()
|
||||||
assert_equal(Class1:bazzz(), 'bazzz')
|
assert.equal(Class1:bazzz(), 'bazzz')
|
||||||
assert_equal(Class2:bazzz(), 'bazzz')
|
assert.equal(Class2:bazzz(), 'bazzz')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
local class = require 'middleclass'
|
|
||||||
local Object = class.Object
|
|
||||||
|
|
||||||
context('subclassOf', function()
|
|
||||||
|
|
||||||
context('nils, integers, etc', function()
|
|
||||||
local primitives = {nil, 1, 'hello', {}, function() end}
|
|
||||||
|
|
||||||
for _,primitive in pairs(primitives) do
|
|
||||||
local theType = type(primitive)
|
|
||||||
context('A ' .. theType, function()
|
|
||||||
|
|
||||||
local f1 = function() return subclassOf(Object, primitive) end
|
|
||||||
local f2 = function() return subclassOf(primitive, o) end
|
|
||||||
local f3 = function() return subclassOf(primitive, primitive) end
|
|
||||||
|
|
||||||
context('does not throw errors', function()
|
|
||||||
test('subclassOf(Object, '.. theType ..')', function()
|
|
||||||
assert_not_error(f1)
|
|
||||||
end)
|
|
||||||
test('subclassOf(' .. theType .. ', Object:new())', function()
|
|
||||||
assert_not_error(f2)
|
|
||||||
end)
|
|
||||||
test('subclassOf(' .. theType .. ',' .. theType ..')', function()
|
|
||||||
assert_not_error(f3)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('makes subclassOf return false', function()
|
|
||||||
assert_false(f1())
|
|
||||||
assert_false(f2())
|
|
||||||
assert_false(f3())
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
context('Any class (except Object)', function()
|
|
||||||
local Class1 = class('Class1')
|
|
||||||
local Class2 = class('Class2', Class1)
|
|
||||||
local Class3 = class('Class3', Class2)
|
|
||||||
local UnrelatedClass = class('Unrelated')
|
|
||||||
|
|
||||||
test('is subclassOf(Object)', function()
|
|
||||||
assert_true(subclassOf(Object, Class1))
|
|
||||||
assert_true(subclassOf(Object, Class2))
|
|
||||||
assert_true(subclassOf(Object, Class3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is subclassOf its direct superclass', function()
|
|
||||||
assert_true(subclassOf(Class1, Class2))
|
|
||||||
assert_true(subclassOf(Class2, Class3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is subclassOf its ancestors', function()
|
|
||||||
assert_true(subclassOf(Class1, Class3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is a subclassOf its class\' subclasses', function()
|
|
||||||
assert_false(subclassOf(Class2, Class1))
|
|
||||||
assert_false(subclassOf(Class3, Class1))
|
|
||||||
assert_false(subclassOf(Class3, Class2))
|
|
||||||
end)
|
|
||||||
|
|
||||||
test('is not a subclassOf an unrelated class', function()
|
|
||||||
assert_false(subclassOf(UnrelatedClass, Class1))
|
|
||||||
assert_false(subclassOf(UnrelatedClass, Class2))
|
|
||||||
assert_false(subclassOf(UnrelatedClass, Class3))
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
end)
|
|
Loading…
Reference in New Issue
Block a user