1local class = require 'middleclass' 2local Object = class.Object 3 4local function is_lua_5_2_compatible() 5 return type(rawlen) == 'function' 6end 7 8describe('Metamethods', function() 9 10 describe('Custom Metamethods', function() 11 local Vector= class('Vector') 12 function Vector.initialize(a,x,y,z) a.x, a.y, a.z = x,y,z end 13 function Vector.__tostring(a) return a.class.name .. '[' .. a.x .. ',' .. a.y .. ',' .. a.z .. ']' end 14 function Vector.__eq(a,b) return a.x==b.x and a.y==b.y and a.z==b.z end 15 function Vector.__lt(a,b) return a() < b() end 16 function Vector.__le(a,b) return a() <= b() end 17 function Vector.__add(a,b) return Vector:new(a.x+b.x, a.y+b.y ,a.z+b.z) end 18 function Vector.__sub(a,b) return Vector:new(a.x-b.x, a.y-b.y, a.z-b.z) end 19 function Vector.__div(a,s) return Vector:new(a.x/s, a.y/s, a.z/s) end 20 function Vector.__unm(a) return Vector:new(-a.x, -a.y, -a.z) end 21 function Vector.__concat(a,b) return a.x*b.x+a.y*b.y+a.z*b.z end 22 function Vector.__call(a) return math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z) end 23 function Vector.__pow(a,b) 24 return Vector:new(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x) 25 end 26 function Vector.__mul(a,b) 27 if type(b)=="number" then return Vector:new(a.x*b, a.y*b, a.z*b) end 28 if type(a)=="number" then return Vector:new(a*b.x, a*b.y, a*b.z) end 29 end 30 function Vector.__len(a) return 3 end 31 function Vector.__pairs(a) 32 local t = {x=a.x,y=a.y,z=a.z} 33 return coroutine.wrap(function() 34 for k,v in pairs(t) do 35 coroutine.yield(k,v) 36 end 37 end) 38 end 39 function Vector.__ipairs(a) 40 local t = {a.x,a.y,a.z} 41 return coroutine.wrap(function() 42 for k,v in ipairs(t) do 43 coroutine.yield(k,v) 44 end 45 end) 46 end 47 48 local a = Vector:new(1,2,3) 49 local b = Vector:new(2,4,6) 50 51 for metamethod,values in pairs({ 52 __tostring = { tostring(a), "Vector[1,2,3]" }, 53 __eq = { a, a}, 54 __lt = { a<b, true }, 55 __le = { a<=b, true }, 56 __add = { a+b, Vector(3,6,9) }, 57 __sub = { b-a, Vector(1,2,3) }, 58 __div = { b/2, Vector(1,2,3) }, 59 __unm = { -a, Vector(-1,-2,-3) }, 60 __concat = { a..b, 28 }, 61 __call = { a(), math.sqrt(14) }, 62 __pow = { a^b, Vector(0,0,0) }, 63 __mul = { 4*a, Vector(4,8,12) } 64 --__index = { b[1], 3 } 65 }) do 66 describe(metamethod, function() 67 it('works as expected', function() 68 assert.equal(values[1], values[2]) 69 end) 70 end) 71 end 72 73 if is_lua_5_2_compatible() then 74 75 describe('__len', function() 76 it('works as expected', function() 77 assert.equal(#a, 3) 78 end) 79 end) 80 81 describe('__pairs', function() 82 it('works as expected',function() 83 local output = {} 84 for k,v in pairs(a) do 85 output[k] = v 86 end 87 assert.are.same(output,{x=1,y=2,z=3}) 88 end) 89 end) 90 91 describe('__ipairs', function() 92 it('works as expected',function() 93 local output = {} 94 for _,i in ipairs(a) do 95 output[#output+1] = i 96 end 97 assert.are.same(output,{1,2,3}) 98 end) 99 end) 100 101 end 102 103 describe('Inherited Metamethods', function() 104 local Vector2= class('Vector2', Vector) 105 function Vector2:initialize(x,y,z) Vector.initialize(self,x,y,z) end 106 107 local c = Vector2:new(1,2,3) 108 local d = Vector2:new(2,4,6) 109 for metamethod,values in pairs({ 110 __tostring = { tostring(c), "Vector2[1,2,3]" }, 111 __eq = { c, c }, 112 __lt = { c<d, true }, 113 __le = { c<=d, true }, 114 __add = { c+d, Vector(3,6,9) }, 115 __sub = { d-c, Vector(1,2,3) }, 116 __div = { d/2, Vector(1,2,3) }, 117 __unm = { -c, Vector(-1,-2,-3) }, 118 __concat = { c..d, 28 }, 119 __call = { c(), math.sqrt(14) }, 120 __pow = { c^d, Vector(0,0,0) }, 121 __mul = { 4*c, Vector(4,8,12) }, 122 }) do 123 describe(metamethod, function() 124 it('works as expected', function() 125 assert.equal(values[1], values[2]) 126 end) 127 end) 128 end 129 130 if is_lua_5_2_compatible() then 131 132 describe('__len', function() 133 it('works as expected', function() 134 assert.equal(#c, 3) 135 end) 136 end) 137 138 describe('__pairs', function() 139 it('works as expected',function() 140 local output = {} 141 for k,v in pairs(c) do 142 output[k] = v 143 end 144 assert.are.same(output,{x=1,y=2,z=3}) 145 end) 146 end) 147 148 describe('__ipairs', function() 149 it('works as expected', function() 150 local output = {} 151 for _,i in ipairs(c) do 152 output[#output+1] = i 153 end 154 assert.are.same(output,{1,2,3}) 155 end) 156 end) 157 158 end 159 160 end) 161 162 end) 163 164 describe('Default Metamethods', function() 165 166 local Peter, peter 167 168 before_each(function() 169 Peter = class('Peter') 170 peter = Peter() 171 end) 172 173 describe('A Class', function() 174 it('has a call metamethod properly set', function() 175 assert.is_true(peter:isInstanceOf(Peter)) 176 end) 177 it('has a tostring metamethod properly set', function() 178 assert.equal(tostring(Peter), 'class Peter') 179 end) 180 end) 181 182 describe('An instance', function() 183 it('has a tostring metamethod, returning a different result from Object.__tostring', function() 184 assert.not_equal(Peter.__tostring, Object.__tostring) 185 assert.equal(tostring(peter), 'instance of class Peter') 186 end) 187 end) 188 end) 189 190end) 191