-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathBeholder.lua
169 lines (131 loc) · 4.84 KB
/
Beholder.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
-----------------------------------------------------------------------------------
-- 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 Beholder')
assert(Invoker~=nil, 'The Beholder module requires the Invoker module in order to work. Please require Invoker before requiring Beholder')
assert(Branchy~=nil, 'The Beholder module requires the Branchy module in order to work. Please require Branchy before requiring Beholder')
--[[ Usage:
require 'middleclass' -- or similar
require 'middleclass-extras.init' -- or 'middleclass-extras'
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'):include(Branchy)
function Node:initialize()
self.objects=setmetatable({}, {__mode='k'})
end
function Node:getOrCreateChild(key)
local child = self.children[key]
if child == nil then
child = self:addChild(Node:new(), key)
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
Invoker.invoke(object, action.method, unpack(params))
end
end
end