-
-
Notifications
You must be signed in to change notification settings - Fork 123
/
Maid.lua
132 lines (110 loc) · 3.1 KB
/
Maid.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
--- Manages the cleaning of events and other things.
-- Useful for encapsulating state and make deconstructors easy
-- @classmod Maid
-- @see Signal
local Maid = {}
Maid.ClassName = "Maid"
--- Returns a new Maid object
-- @constructor Maid.new()
-- @treturn Maid
function Maid.new()
return setmetatable({
_tasks = {}
}, Maid)
end
function Maid.isMaid(value)
return type(value) == "table" and value.ClassName == "Maid"
end
--- Returns Maid[key] if not part of Maid metatable
-- @return Maid[key] value
function Maid:__index(index)
if Maid[index] then
return Maid[index]
else
return self._tasks[index]
end
end
--- Add a task to clean up. Tasks given to a maid will be cleaned when
-- maid[index] is set to a different value.
-- @usage
-- Maid[key] = (function) Adds a task to perform
-- Maid[key] = (event connection) Manages an event connection
-- Maid[key] = (Maid) Maids can act as an event connection, allowing a Maid to have other maids to clean up.
-- Maid[key] = (Object) Maids can cleanup objects with a `Destroy` method
-- Maid[key] = nil Removes a named task. If the task is an event, it is disconnected. If it is an object,
-- it is destroyed.
function Maid:__newindex(index, newTask)
if Maid[index] ~= nil then
error(("'%s' is reserved"):format(tostring(index)), 2)
end
local tasks = self._tasks
local oldTask = tasks[index]
if oldTask == newTask then
return
end
tasks[index] = newTask
if oldTask then
if type(oldTask) == "function" then
oldTask()
elseif typeof(oldTask) == "RBXScriptConnection" then
oldTask:Disconnect()
elseif oldTask.Destroy then
oldTask:Destroy()
end
end
end
--- Same as indexing, but uses an incremented number as a key.
-- @param task An item to clean
-- @treturn number taskId
function Maid:GiveTask(task)
if not task then
error("Task cannot be false or nil", 2)
end
local taskId = #self._tasks+1
self[taskId] = task
if type(task) == "table" and (not task.Destroy) then
warn("[Maid.GiveTask] - Gave table task without .Destroy\n\n" .. debug.traceback())
end
return taskId
end
function Maid:GivePromise(promise)
if not promise:IsPending() then
return promise
end
local newPromise = promise.resolved(promise)
local id = self:GiveTask(newPromise)
-- Ensure GC
newPromise:Finally(function()
self[id] = nil
end)
return newPromise
end
--- Cleans up all tasks.
-- @alias Destroy
function Maid:DoCleaning()
local tasks = self._tasks
-- Disconnect all events first as we know this is safe
for index, task in pairs(tasks) do
if typeof(task) == "RBXScriptConnection" then
tasks[index] = nil
task:Disconnect()
end
end
-- Clear out tasks table completely, even if clean up tasks add more tasks to the maid
local index, task = next(tasks)
while task ~= nil do
tasks[index] = nil
if type(task) == "function" then
task()
elseif typeof(task) == "RBXScriptConnection" then
task:Disconnect()
elseif task.Destroy then
task:Destroy()
end
index, task = next(tasks)
end
end
--- Alias for DoCleaning()
-- @function Destroy
Maid.Destroy = Maid.DoCleaning
return Maid