mirror of
https://github.com/kikito/middleclass.git
synced 2024-10-05 23:24:17 +00:00
fixed issues with metamethods. All tests passing
This commit is contained in:
parent
fad5027340
commit
0ba7409a88
218
inspect.lua
218
inspect.lua
@ -1,218 +0,0 @@
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
-- inspect.lua - v1.1.1 (2011-01)
|
||||
-- Enrique García Cota - enrique.garcia.cota [AT] gmail [DOT] com
|
||||
-- human-readable representations of tables.
|
||||
-- inspired by http://lua-users.org/wiki/TableSerialization
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Apostrophizes the string if it has quotes, but not aphostrophes
|
||||
-- Otherwise, it returns a regular quoted string
|
||||
local function smartQuote(str)
|
||||
if string.match( string.gsub(str,"[^'\"]",""), '^"+$' ) then
|
||||
return "'" .. str .. "'"
|
||||
end
|
||||
return string.format("%q", str )
|
||||
end
|
||||
|
||||
local controlCharsTranslation = {
|
||||
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
|
||||
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\\"] = "\\\\"
|
||||
}
|
||||
|
||||
local function unescapeChar(c) return controlCharsTranslation[c] end
|
||||
|
||||
local function unescape(str)
|
||||
local result, _ = string.gsub( str, "(%c)", unescapeChar )
|
||||
return result
|
||||
end
|
||||
|
||||
local function isIdentifier(str)
|
||||
return string.match( str, "^[_%a][_%a%d]*$" )
|
||||
end
|
||||
|
||||
local function isArrayKey(k, length)
|
||||
return type(k)=='number' and 1 <= k and k <= length
|
||||
end
|
||||
|
||||
local function isDictionaryKey(k, length)
|
||||
return not isArrayKey(k, length)
|
||||
end
|
||||
|
||||
local sortOrdersByType = {
|
||||
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
|
||||
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
|
||||
}
|
||||
|
||||
local function sortKeys(a,b)
|
||||
local ta, tb = type(a), type(b)
|
||||
if ta ~= tb then return sortOrdersByType[ta] < sortOrdersByType[tb] end
|
||||
if ta == 'string' or ta == 'number' then return a < b end
|
||||
return false
|
||||
end
|
||||
|
||||
local function getDictionaryKeys(t)
|
||||
local length = #t
|
||||
local keys = {}
|
||||
for k,_ in pairs(t) do
|
||||
if isDictionaryKey(k, length) then table.insert(keys,k) end
|
||||
end
|
||||
table.sort(keys, sortKeys)
|
||||
return keys
|
||||
end
|
||||
|
||||
local function getToStringResultSafely(t, mt)
|
||||
local __tostring = type(mt) == 'table' and mt.__tostring
|
||||
local string, status
|
||||
if type(__tostring) == 'function' then
|
||||
status, string = pcall(__tostring, t)
|
||||
string = status and string or 'error: ' .. tostring(string)
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
local Inspector = {}
|
||||
|
||||
function Inspector:new(v, depth)
|
||||
local inspector = {
|
||||
buffer = {},
|
||||
depth = depth,
|
||||
level = 0,
|
||||
counters = {
|
||||
['function'] = 0,
|
||||
['userdata'] = 0,
|
||||
['thread'] = 0,
|
||||
['table'] = 0
|
||||
},
|
||||
pools = {
|
||||
['function'] = setmetatable({}, {__mode = "kv"}),
|
||||
['userdata'] = setmetatable({}, {__mode = "kv"}),
|
||||
['thread'] = setmetatable({}, {__mode = "kv"}),
|
||||
['table'] = setmetatable({}, {__mode = "kv"})
|
||||
}
|
||||
}
|
||||
|
||||
setmetatable( inspector, {
|
||||
__index = Inspector,
|
||||
__tostring = function(instance) return table.concat(instance.buffer) end
|
||||
} )
|
||||
return inspector:putValue(v)
|
||||
end
|
||||
|
||||
function Inspector:puts(...)
|
||||
local args = {...}
|
||||
for i=1, #args do
|
||||
table.insert(self.buffer, tostring(args[i]))
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:tabify()
|
||||
self:puts("\n", string.rep(" ", self.level))
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:up()
|
||||
self.level = self.level - 1
|
||||
end
|
||||
|
||||
function Inspector:down()
|
||||
self.level = self.level + 1
|
||||
end
|
||||
|
||||
function Inspector:putComma(comma)
|
||||
if comma then self:puts(',') end
|
||||
return true
|
||||
end
|
||||
|
||||
function Inspector:putTable(t)
|
||||
if self:alreadySeen(t) then
|
||||
self:puts('<table ', self:getOrCreateCounter(t), '>')
|
||||
elseif self.level >= self.depth then
|
||||
self:puts('{...}')
|
||||
else
|
||||
self:puts('<',self:getOrCreateCounter(t),'>{')
|
||||
self:down()
|
||||
|
||||
local length = #t
|
||||
local mt = getmetatable(t)
|
||||
|
||||
local string = getToStringResultSafely(t, mt)
|
||||
if type(string) == 'string' and #string > 0 then
|
||||
self:puts(' -- ', unescape(string))
|
||||
if length >= 1 then self:tabify() end -- tabify the array values
|
||||
end
|
||||
|
||||
local comma = false
|
||||
for i=1, length do
|
||||
comma = self:putComma(comma)
|
||||
self:puts(' '):putValue(t[i])
|
||||
end
|
||||
|
||||
local dictKeys = getDictionaryKeys(t)
|
||||
|
||||
for _,k in ipairs(dictKeys) do
|
||||
comma = self:putComma(comma)
|
||||
self:tabify():putKey(k):puts(' = '):putValue(t[k])
|
||||
end
|
||||
|
||||
if mt then
|
||||
comma = self:putComma(comma)
|
||||
self:tabify():puts('<metatable> = '):putValue(mt)
|
||||
end
|
||||
self:up()
|
||||
|
||||
if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
|
||||
self:tabify()
|
||||
elseif length > 0 then -- array tables have one extra space before closing }
|
||||
self:puts(' ')
|
||||
end
|
||||
self:puts('}')
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:alreadySeen(v)
|
||||
local tv = type(v)
|
||||
return self.pools[tv][v] ~= nil
|
||||
end
|
||||
|
||||
function Inspector:getOrCreateCounter(v)
|
||||
local tv = type(v)
|
||||
local current = self.pools[tv][v]
|
||||
if not current then
|
||||
current = self.counters[tv] + 1
|
||||
self.counters[tv] = current
|
||||
self.pools[tv][v] = current
|
||||
end
|
||||
return current
|
||||
end
|
||||
|
||||
function Inspector:putValue(v)
|
||||
local tv = type(v)
|
||||
|
||||
if tv == 'string' then
|
||||
self:puts(smartQuote(unescape(v)))
|
||||
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
|
||||
self:puts(tostring(v))
|
||||
elseif tv == 'table' then
|
||||
self:putTable(v)
|
||||
else
|
||||
self:puts('<',tv,' ',self:getOrCreateCounter(v),'>')
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:putKey(k)
|
||||
if type(k) == "string" and isIdentifier(k) then
|
||||
return self:puts(k)
|
||||
end
|
||||
return self:puts( "[" ):putValue(k):puts("]")
|
||||
end
|
||||
|
||||
local function inspect(t, depth)
|
||||
depth = depth or 4
|
||||
return tostring(Inspector:new(t, depth))
|
||||
end
|
||||
|
||||
return inspect
|
||||
|
@ -4,7 +4,7 @@
|
||||
-- Based on YaciCode, from Julien Patte and LuaObject, from Sebastien Rocca-Serra
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local _classes = setmetatable({}, {__mode = "k"})
|
||||
local _classes = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local function _setClassDictionariesMetatables(klass)
|
||||
local dict = klass.__instanceDict
|
||||
@ -13,7 +13,6 @@ local function _setClassDictionariesMetatables(klass)
|
||||
local super = klass.super
|
||||
if super then
|
||||
local superStatic = super.static
|
||||
setmetatable(dict, klass.__inherited)
|
||||
setmetatable(klass.static, { __index = function(_,k) return dict[k] or superStatic[k] end })
|
||||
else
|
||||
setmetatable(klass.static, { __index = function(_,k) return dict[k] end })
|
||||
@ -23,7 +22,8 @@ end
|
||||
local function _addInstanceMethodToClass(klass, name, method)
|
||||
klass.__instanceDict[name] = method
|
||||
for subclass, _ in pairs(klass.subclasses) do
|
||||
subclass.__inherited[name] = method
|
||||
local existing = subclass.__instanceDict[name]
|
||||
subclass.__instanceDict[name] = existing or method
|
||||
end
|
||||
end
|
||||
|
||||
@ -38,15 +38,15 @@ end
|
||||
|
||||
local function _copyInheritedMethods(klass, super)
|
||||
for name, method in pairs(super.__instanceDict) do
|
||||
if klass.metamethods[method] then klass.__instanceDict[name] = method
|
||||
else klass.__inherited[name] = method end
|
||||
local existing = klass.__instanceDict[name]
|
||||
klass.__instanceDict[name] = existing or method
|
||||
end
|
||||
end
|
||||
|
||||
local function _createClass(name, super)
|
||||
local klass = {
|
||||
name = name, super = super, static = {}, subclasses = setmetatable({}, {__mode = "k"}),
|
||||
__mixins = {}, __instanceDict={}, __inherited ={}
|
||||
name = name, super = super, static = {}, subclasses = setmetatable({}, { __mode = "k" }),
|
||||
__mixins = {}, __instanceDict = {}
|
||||
}
|
||||
|
||||
_setClassDictionariesMetatables(klass)
|
||||
|
@ -1,7 +1,5 @@
|
||||
require 'middleclass'
|
||||
|
||||
local inspect = require 'inspect'
|
||||
|
||||
context('Metamethods', function()
|
||||
|
||||
context('Custom Metamethods', function()
|
||||
@ -10,6 +8,7 @@ context('Metamethods', function()
|
||||
-- I'll be using 'a' instead of 'self' on this example since it is shorter
|
||||
local Vector= class('Vector')
|
||||
function Vector.initialize(a,x,y,z) a.x, a.y, a.z = x,y,z end
|
||||
|
||||
function Vector.__tostring(a) return a.class.name .. '[' .. a.x .. ',' .. a.y .. ',' .. a.z .. ']' end
|
||||
function Vector.__eq(a,b) return a.x==b.x and a.y==b.y and a.z==b.z end
|
||||
function Vector.__lt(a,b) return a() < b() end
|
||||
@ -50,13 +49,12 @@ context('Metamethods', function()
|
||||
assert_equal(values[1], values[2])
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
context('Inherited Metamethods', function()
|
||||
local Vector2= class('Vector2', Vector)
|
||||
function Vector2:initialize(x,y,z) Vector.initialize(self,x,y,z) end
|
||||
|
||||
|
||||
local c = Vector2:new(1,2,3)
|
||||
print(inspect(c))
|
||||
local d = Vector2:new(2,4,6)
|
||||
for metamethod,values in pairs({
|
||||
__tostring = { tostring(c), "Vector2[1,2,3]" },
|
||||
@ -77,7 +75,7 @@ context('Metamethods', function()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
end)
|
||||
|
||||
context('Default Metamethods', function()
|
||||
@ -99,8 +97,7 @@ context('Metamethods', function()
|
||||
end)
|
||||
|
||||
context('An instance', function()
|
||||
test('has a tostring metamethod, returning a different result from Object.__tostring', function()
|
||||
assert_not_equal(Peter.__tostring, Object.__tostring)
|
||||
test('has a tostring metamethod', function()
|
||||
assert_equal(tostring(peter), 'instance of class Peter')
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user