From 9fd4ccee7269c0486fc2cf71f363c202dc706128 Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 4 Oct 2010 20:30:14 +0200 Subject: [PATCH] =?UTF-8?q?moved=20mixins=20out=20of=20middleclass,=20and?= =?UTF-8?q?=20into=20P=C3=84SSION?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleclass/mixins/Beholder.lua | 172 ---------------------- middleclass/mixins/Callbacks.lua | 221 ---------------------------- middleclass/mixins/GetterSetter.lua | 38 ----- middleclass/mixins/Sender.lua | 38 ----- spec/Beholder_spec.lua | 77 ---------- spec/Callbacks_spec.lua | 104 ------------- spec/GetterSetter_spec.lua | 38 ----- spec/Sender_spec.lua | 32 ---- 8 files changed, 720 deletions(-) delete mode 100644 middleclass/mixins/Beholder.lua delete mode 100644 middleclass/mixins/Callbacks.lua delete mode 100644 middleclass/mixins/GetterSetter.lua delete mode 100644 middleclass/mixins/Sender.lua delete mode 100644 spec/Beholder_spec.lua delete mode 100644 spec/Callbacks_spec.lua delete mode 100644 spec/GetterSetter_spec.lua delete mode 100644 spec/Sender_spec.lua diff --git a/middleclass/mixins/Beholder.lua b/middleclass/mixins/Beholder.lua deleted file mode 100644 index 10bd6b3..0000000 --- a/middleclass/mixins/Beholder.lua +++ /dev/null @@ -1,172 +0,0 @@ ------------------------------------------------------------------------------------ --- Beholder.lua --- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) - 4 Mar 2010 --- Small framework for event observers ------------------------------------------------------------------------------------ - -assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Callbacks') -assert(Sender~=nil, 'The Beholder module requires the Sender module in order to work. Please require Sender before requiring Beholder') - - ---[[ Usage: - - require 'middleclass.mixins.Beholder' -- or 'middleclass.init' - - MyClass = class('MyClass') - MyClass:includes(Beholder) - function MyClass:foo(x,y) ... end - - local obj = MyClass:new() - - -- when the 'newgame' event is fired, call method foo with parameters 100 and 200 - obj:observe('newgame', 'foo', 100, 200) - - -- you can add more than one callbacks to the same event: - obj:observe('newgame', 'foo', 300, 400) - - -- alternatively, use a function - obj:observe('endgame', function(myself) myself.blah = 0 end) - - -- trigger the event: - Beholder.trigger('newgame') - - -- stop observing an event: - obj:stopObserving('newgame') - - -]] - --------------------------------- --- PRIVATE NODE CLASS --------------------------------- - -local Node = class('Node') - -function Node:initialize() - super.initialize(self) - self.children = {} - self.objects=setmetatable({}, {__mode='k'}) -end - -function Node:getOrCreateChild(key) - local child = self.children[key] - if child == nil then - child = Node:new() - child.parent = self - self.children[key] = child - end - return child -end - -function Node:getOrCreateDescendant(key) - if type(key) ~= 'table' then return self:getOrCreateChild(key) end - local node = self - for _,v in ipairs(key) do node = node:getOrCreateChild(v) end - return node -end - -function Node:getDescendant(key) - if type(key) ~= 'table' then return self.children[key] end - local node = self - for _,v in ipairs(key) do - node = node.children[v] - if node == nil then return nil end - end - return node -end - -function Node:getOrRegisterObject(object) - self.objects[object] = self.objects[object] or {} - return self.objects[object] -end - -function Node:addAction(object, method, ...) - local actions = self:getOrRegisterObject(object) - table.insert(actions, { method = method, params = {...} }) -end - -function Node:removeAction(object, method) - if method == nil then self.objects[object] = nil end - local actions = self.objects[object] - if actions==nil then return end - - local index = 1 - for i,v in ipairs(actions) do - if v == method then index = i break end - end - - if(index~=nil) then table.remove(actions, index) end -end - - --- Private variable storing the list of event callbacks that can be used ---[[ structure: - _root = { -- root node - children = - 'a' = { -- root->a node - children = { - 'b' = { -- root->a->b node - children = {}, - objects = { -- list of objects registered on node root->a->b - obj1 = { -- list of actions to perform on object1 - { method = 'method1', params = {} }, - { method = 'method2', params = {1,2}} - } - } - } - }, - objects = {} -- node root->a does not have any object registered - } - 'b' = { -- root->b node - children = {}, -- no children nor objects - objects = {} - } - } - } -]] -local _root = Node:new() - --- The Beholder module -Beholder = {} - -function Beholder:observe(eventId, methodOrName, ...) - - assert(self~=nil, "self is nil. invoke object:observe instead of object.observe") - assert(eventId~=nil, "eventId can not be nil") - local t = type(methodOrName) - assert(t=='string' or t=='function', 'methodOrName must be a function or string') - - local node = _root:getOrCreateDescendant(eventId) - - node:addAction(self, methodOrName, ...) -end - -function Beholder:stopObserving(eventId, methodOrName) - local node = _root:getDescendant(eventId) - if node==nil then return end - node:removeAction(self, methodOrName) -end - - ---[[ Triggers events - Usage: - Beholder.trigger('passion.update', dt) - All objects that are "observing" passion.update events will get their associated actions called. -]] - -function Beholder.trigger(eventId, ...) - - local node = _root:getDescendant(eventId) - if node==nil then return end - - for object,actions in pairs(node.objects) do - for _,action in ipairs(actions) do - local params = {} - for k,v in ipairs(action.params) do params[k] = v end - for _,v in ipairs({...}) do table.insert(params, v) end - - Sender.send(object, action.method, unpack(params)) - end - end -end - diff --git a/middleclass/mixins/Callbacks.lua b/middleclass/mixins/Callbacks.lua deleted file mode 100644 index 8f67a6d..0000000 --- a/middleclass/mixins/Callbacks.lua +++ /dev/null @@ -1,221 +0,0 @@ ------------------------------------------------------------------------------------ --- Callbacks.lua --- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) --- Mixin that adds callbacks support (i.e. beforeXXX or afterYYY) to classes) ------------------------------------------------------------------------------------ - -assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Callbacks') -assert(Sender~=nil, 'The Callbacks module requires the Sender module in order to work. Please require Sender before requiring Callbacks') - ---[[ Usage: - - MyClass = class('MyClass') - MyClass:include(Callbacks) - - MyClass:addCallbacksAround('foo') -- this defines methods 'beforeFoo' and 'afterFoo' - - MyClass:beforeFoo('bar') -- can use either method names or functions - MyClass:afterFoo(function() print('baz') end) - - function MyClass:foo() print 'foo' end - function MyClass:bar() print 'bar' end - - local obj = MyClass:new() - - obj:foo() -- prints 'bar foo baz' -]] - --------------------------------- --- PRIVATE STUFF --------------------------------- - ---[[ holds all the callbacks entries. - callback entries are just lists of methods to be called before / after some other method is called - - _callbackEntries = { - Actor = { - beforeUpdate = { methods = {m1, m2, m3 } }, -- m1, m2, m3 & m4 can be method names or functions - afterUpdate = { methods = { 'm4' } }, - update = { - before = 'beforeUpdate', - after = 'afterUpdate' - } - } - } - -]] -local _callbackEntries = setmetatable({}, {__mode = "k"}) -- weak table - --- cache for not re-creating methods every time they are needed -local _methodCache = setmetatable({}, {__mode = "k"}) - --- private class methods - -local _getCallbackEntry -local function _getCallbackEntry(theClass, callbackName) - if theClass == nil or callbackName == nil then return nil end - if _callbackEntries[theClass] ~= nil and _callbackEntries[theClass][callbackName] ~= nil then - return _callbackEntries[theClass][callbackName] - end - return _getCallbackEntry(theClass.superclass, callbackName) -end - --- creates one of the "level 2" entries on callbacks, like beforeUpdate or afterupdate, above -local function _getOrCreateCallbackEntry(theClass, callbackName) - if not theClass or not callbackName then return {} end - _callbackEntries[theClass] = _callbackEntries[theClass] or setmetatable({}, {__mode = "k"}) - local classEntries = _callbackEntries[theClass] - classEntries[callbackName] = classEntries[callbackName] or setmetatable({ methods={} }, {__mode = "k"}) - - return classEntries[callbackName] -end - --- returns all the methods that should be called when a callback is invoked, including superclasses -local function _getCallbackEntryChainMethods(theClass, callbackName) - if theClass==nil then return {} end - local methods = _getOrCreateCallbackEntry(theClass, callbackName).methods - local superMethods = _getCallbackEntryChainMethods(theClass.superclass, callbackName) - - local result = {} - for i,method in ipairs(methods) do result[i]=method end - for _,method in ipairs(superMethods) do table.insert(result, method) end - - return result -end - --- defines a callback method. These methods are used to add "methods" to the callback. --- for example, after calling _defineCallbackMethod(Actor, 'afterUpdate') you can then do --- Actor:afterUpdate('removeFromList', 'dance', function(actor) actor:doSomething() end) -local function _defineCallbackMethod(theClass, callbackName) - if callbackName == nil then return nil end - - assert(rawget(theClass.__classDict,callbackName)==nil, "Could not define " .. theClass.name .. '.' .. callbackName .. ": already defined") - - theClass[callbackName] = function(theClass, ...) - local methods = {...} - local existingMethods = _getOrCreateCallbackEntry(theClass, callbackName).methods - for _,method in ipairs(methods) do - table.insert(existingMethods, method) - end - end - - _getOrCreateCallbackEntry(theClass, callbackName) - - return theClass[callbackName] -end - --- private instance methods - --- given a callback entry, obtain all the methods that must be called for that callback and execute them -local function _runCallbackChain(object, entry, before_or_after) - if entry == nil then return true end - callbackName = entry[before_or_after] - if callbackName==nil then return true end - local methods = _getCallbackEntryChainMethods(object.class, callbackName) - for _,method in ipairs(methods) do - if Sender.send(object, method) == false then return false end - end - return true -end - --- given a class and a method, this returns a new version of that method that invokes callbacks --- uses a cache for not calculating the methods every time -function _getChainedMethod(theClass, methodName, method) - local entry = _getCallbackEntry(theClass, methodName) - - if(entry==nil) then return method end - - _methodCache[theClass] = _methodCache[theClass] or setmetatable({}, {__mode = "k"}) - local classCache = _methodCache[theClass] - - local chainedMethod = classCache[methodName] - - if chainedMethod == nil then - chainedMethod = function(self, ...) - if _runCallbackChain(self, entry, 'before') == false then return false end - local result = method(self, ...) - if _runCallbackChain(self, entry, 'after') == false then return false end - return result - end - classCache[methodName] = chainedMethod - end - - return chainedMethod -end - --- helper function used by addCallbacksBefore, after and around -function _addCallbacks(theClass, before_or_after, methodName, callbackMethodName) - assert(type(methodName)=='string', 'methodName must be a string') - assert(before_or_after == 'before' or before_or_after == 'after', 'Parameter must be "before" or "after"') - - local entry = _getOrCreateCallbackEntry(theClass, methodName) - - assert(entry[before_or_after] == nil, 'The "' .. tostring(before_or_after) .. '" callback is already defined as "' .. tostring(entry[before_or_after]) .. '". Use that callback method instead or adding a new one' ) - callbackMethodName = callbackMethodName or before_or_after .. methodName:gsub("^%l", string.upper) - - _defineCallbackMethod(theClass, callbackMethodName) - - entry[before_or_after]= callbackMethodName -end - --------------------------------- --- PUBLIC STUFF --------------------------------- - -Callbacks = {} - -function Callbacks:included(theClass) - - if includes(Callbacks, theClass) then return end - - -- Modify the instances __index metamethod so it adds callback chains to methods with callback entries - - local oldNew = theClass.new - - theClass.new = function(theClass, ...) - local instance = oldNew(theClass, ...) - - local prevIndex = getmetatable(instance).__index - local tIndex = type(prevIndex) - - setmetatable(instance, { - __index = function(instance, methodName) - local method - - if tIndex == 'table' then method = prevIndex[methodName] - elseif tIndex == 'function' then method = prevIndex(instance, methodName) - end - - if type(method) ~= 'function' then return method end - - return _getChainedMethod(theClass, methodName, method) - end - }) - - -- special treatment for afterInitialize callbacks - local entry = _getCallbackEntry(theClass, 'initialize') - if _runCallbackChain(instance, entry, 'after') == false then return false end - - return instance - end - -end - --- usage: Actor:addCallbacksBefore('update') --- callbackMethodName is optional, defaulting to 'beforeUpdate' -function Callbacks.addCallbacksBefore(theClass, methodName, callbackMethodName) - _addCallbacks(theClass, 'before', methodName, callbackMethodName) -end - --- usage: Actor:addCallbacksAfter('initialize') --- callbackMethodName is optional, defaulting to 'afterInitialize' -function Callbacks.addCallbacksAfter(theClass, methodName, callbackMethodName) - _addCallbacks(theClass, 'after', methodName, callbackMethodName) -end - --- usage: Actor:addCallbackAround('update') --- before & afterCallbackMethodName are optional, defaulting to 'beforeUpdate' and 'afterUpdate' -function Callbacks.addCallbacksAround(theClass, methodName, beforeCallbackMethodName, afterCallbackMethodName) - _addCallbacks(theClass, 'before', methodName, beforeCallbackMethodName) - _addCallbacks(theClass, 'after', methodName, afterCallbackMethodName) -end diff --git a/middleclass/mixins/GetterSetter.lua b/middleclass/mixins/GetterSetter.lua deleted file mode 100644 index 7439daa..0000000 --- a/middleclass/mixins/GetterSetter.lua +++ /dev/null @@ -1,38 +0,0 @@ ------------------------------------------------------------------------------------ --- GetterSetter.lua --- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) - 11 Aug 2010 --- Small mixin for classes with getters and setters ------------------------------------------------------------------------------------ - ---[[ Usage: - - require 'middleclass.mixins.GetterSetter' -- or 'middleclass.init' - - MyClass = class('MyClass') - MyClass:include(GetterSetter) - - MyClass:getter('name', 'pete') -- default value - MyClass:setter('age') - MyClass:getterSetter('color', 'blue') -- default value - -]] - -assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using GetterSetter') - -GetterSetter = {} - -function GetterSetter.getterFor(theClass, attr) return 'get' .. attr:gsub("^%l", string.upper) end -function GetterSetter.setterFor(theClass, attr) return 'set' .. attr:gsub("^%l", string.upper) end -function GetterSetter.getter(theClass, attributeName, defaultValue) - theClass[theClass:getterFor(attributeName)] = function(self) - if(self[attributeName]~=nil) then return self[attributeName] end - return defaultValue - end -end -function GetterSetter.setter(theClass, attributeName) - theClass[theClass:setterFor(attributeName)] = function(self, value) self[attributeName] = value end -end -function GetterSetter.getterSetter(theClass, attributeName, defaultValue) - theClass:getter(attributeName, defaultValue) - theClass:setter(attributeName) -end diff --git a/middleclass/mixins/Sender.lua b/middleclass/mixins/Sender.lua deleted file mode 100644 index eaff90b..0000000 --- a/middleclass/mixins/Sender.lua +++ /dev/null @@ -1,38 +0,0 @@ ------------------------------------------------------------------------------------ --- Sender.lua --- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) - 4 Mar 2010 --- Helper function that simplifies method invocation via method names or functions ------------------------------------------------------------------------------------ - ---[[ Usage: - - require 'middleclass.mixins.Sender' -- or 'middleclass.init' - - MyClass = class('MyClass') - MyClass:includes(Sender) - function MyClass:foo(x,y) print('foo executed with params', x, y) end - - local obj = MyClass:new() - - obj:send('foo', 1,2) -- foo executed with params 1 2 - obj:send( function(self, x, y) - print('nameless function executed with params', x, y) - , 3, 4) -- nameless function executed with params 3, 4 - - Note that the function first parameter will allways be self -]] - -assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Beholder') - -Sender = { - - send = function(self, methodOrName, ...) - local method = methodOrName - if(type(methodOrName)=='string') then method = self[methodOrName] end - assert(type(method)=='function', 'Sender:send requires a function or function name') - return method(self, ...) - end - -} - - diff --git a/spec/Beholder_spec.lua b/spec/Beholder_spec.lua deleted file mode 100644 index dec3174..0000000 --- a/spec/Beholder_spec.lua +++ /dev/null @@ -1,77 +0,0 @@ -require('middleclass.init') - -context( 'Beholder', function() - - context('When included by a class', function() - - local MyClass = class('MyClass') - - function MyClass:initialize() - super.initialize(self) - self.counter = 0 - end - - function MyClass:count(increment) - self.counter = self.counter + increment - end - - function MyClass:count2(increment) - self.counter = self.counter + increment - end - - test('Should not throw errors', function() - assert_not_error(function() MyClass:include(Beholder) end) - end) - - context('When starting observing', function() - - test('It should allow calling of methods by name, with parameters', function() - local obj = MyClass:new() - obj:observe('event1', 'count', 1) - Beholder.trigger('event1') - assert_equal(obj.counter, 1) - end) - - test('It should allow calling of methods by function, with parameters', function() - local obj = MyClass:new() - obj:observe('event2', function(myself, increment) myself:count(increment) end, 1) - Beholder.trigger('event2') - assert_equal(obj.counter, 1) - end) - - test('It should allow chaining of calls', function() - local obj = MyClass:new() - obj:observe('event3', 'count', 1) - obj:observe('event3', 'count', 1) - Beholder.trigger('event3') - assert_equal(obj.counter, 2) - end) - - end) - - context('When stopping observing', function() - - test('It should allow completely stopping observing one event', function() - local obj = MyClass:new() - obj:observe('event4', 'count', 1) - Beholder.trigger('event4') - obj:stopObserving('event4') - Beholder.trigger('event4') - assert_equal(obj.counter, 1) - end) - - test('It should allow stopping observing individual actions on one event', function() - local obj = MyClass:new() - obj:observe('event5', 'count', 1) - obj:observe('event5', 'count2', 1) - Beholder.trigger('event5') - obj:stopObserving('event5', 'count') - Beholder.trigger('event5') - assert_equal(obj.counter, 3) - end) - - end) - - end) - -end) diff --git a/spec/Callbacks_spec.lua b/spec/Callbacks_spec.lua deleted file mode 100644 index 7d3f7c9..0000000 --- a/spec/Callbacks_spec.lua +++ /dev/null @@ -1,104 +0,0 @@ -require('middleclass.init') - -context( 'Callbacks', function() - local A - - before(function() - A = class('A') - function A:initialize() - super.initialize(self) - self.calls = {} - end - end) - - local function defineRegularMethods(theClass) - function theClass:foo() table.insert(self.calls, 'foo') end - function theClass:bar() table.insert(self.calls, 'bar') end - function theClass:baz() table.insert(self.calls, 'baz') end - end - - local function addCallbacks(theClass) - theClass:include(Callbacks) - theClass:addCallbacksAround('bar') - theClass:beforeBar('foo') - theClass:afterBar( function(myself) myself:baz() end ) - end - - local function testInstance(theClass) - local obj = theClass:new() - obj:bar() - - assert_equal(obj.calls[1], 'foo') - assert_equal(obj.calls[2], 'bar') - assert_equal(obj.calls[3], 'baz') - end - - test('Should work when declared before the methods', function() - addCallbacks(A) - defineRegularMethods(A) - testInstance(A) - end) - - test('Should work when declared after the methods', function() - defineRegularMethods(A) - addCallbacks(A) - testInstance(A) - end) - - context('When subclassing', function() - local B - before(function() - B = class('B', A) - end) - - test('The subclass should include Callbacks', function() - A:include(Callbacks) - assert_true(includes(Callbacks, B)) - end) - - test('Callbacks in subclasses should work on inherited methods, even if declared before', function() - addCallbacks(B) - defineRegularMethods(A) - testInstance(B) - end) - - test('Callbacks in subclasses should work on inherited methods when declared after', function() - defineRegularMethods(A) - addCallbacks(B) - testInstance(B) - end) - - test('Callbacks should be conserved in subclasses', function() - addCallbacks(A) - defineRegularMethods(A) - testInstance(B) - end) - - test('Callbacks in subclasses can be used as well as in superclasses', function() - addCallbacks(A) - defineRegularMethods(A) - addCallbacks(B) - local obj = B:new() - obj:bar() - - assert_equal(obj.calls[1], 'foo') - assert_equal(obj.calls[2], 'foo') - assert_equal(obj.calls[3], 'bar') - assert_equal(obj.calls[4], 'baz') - assert_equal(obj.calls[5], 'baz') - end) - - end) - - context('When creating an instance', function() - test('afterInitialize should be called', function() - defineRegularMethods(A) - A:include(Callbacks) - A:addCallbacksAfter('initialize') - A:afterInitialize('foo') - assert_equal(A:new().calls[1], 'foo') - end) - end) - - -end) diff --git a/spec/GetterSetter_spec.lua b/spec/GetterSetter_spec.lua deleted file mode 100644 index 57be2de..0000000 --- a/spec/GetterSetter_spec.lua +++ /dev/null @@ -1,38 +0,0 @@ -require('middleclass.init') - -context( 'GetterSetter', function() - - context('When included by a class', function() - - local MyClass = class('MyClass') - - test('Should not throw errors', function() - assert_not_error(function() MyClass:include(GetterSetter) end) - end) - - test('It should include the 3 main methods on the class', function() - MyClass:getter('name', 'pete') - MyClass:setter('age') - MyClass:getterSetter('color', 'blue') - - local obj = MyClass:new() - assert_equal(obj:getName(), 'pete') - obj.name = 'john' - assert_equal(obj:getName(), 'john') - - obj:setAge(14) - assert_equal(obj.age, 14) - - assert_equal(obj:getColor(), 'blue') - obj:setColor('fucsia') - assert_equal(obj:getColor(), 'fucsia') - end) - - test('It should include the 2 secondary methods on the class', function() - assert_equal(MyClass:getterFor('language'), 'getLanguage') - assert_equal(MyClass:setterFor('language'), 'setLanguage') - end) - - end) - -end) diff --git a/spec/Sender_spec.lua b/spec/Sender_spec.lua deleted file mode 100644 index 59959d8..0000000 --- a/spec/Sender_spec.lua +++ /dev/null @@ -1,32 +0,0 @@ -require('middleclass.init') - -context( 'Sender', function() - - local MyClass = class('MyClass') - MyClass:include(Sender) - function MyClass:foo(x,y) return 'foo ' .. tostring(x) .. ', ' .. tostring(y) end - function MyClass:testSelf() return instanceOf(MyClass, self) end - - local obj = MyClass:new() - - test('It should work with method names', function() - assert_equal(obj:send('foo', 1, 2), 'foo 1, 2') - end) - - test('It should work with implicit functions', function() - assert_equal( - obj:send( - function(self, x, y) return 'bar '.. tostring(x) .. ', ' .. tostring(y) end, - 3, - 4 - ), - 'bar 3, 4' - ) - end) - - test('It should use self as implicit parameter in all cases', function() - assert_true(obj:send('testSelf')) - assert_true(obj:send(MyClass.testSelf)) - end) - -end)