Skip to content

Commit 100f5d9

Browse files
committed
content
1 parent 0503f34 commit 100f5d9

18 files changed

+1424
-4
lines changed
+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
--!strict
2+
3+
-- Copyright (C) 2023 Tria
4+
-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
5+
-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+
local RunService = game:GetService("RunService")
8+
9+
local Janitor = {}
10+
11+
Janitor.__index = Janitor
12+
Janitor.ClassName = "Janitor"
13+
14+
--[=[
15+
@class Janitor
16+
@tag Advanced Feature
17+
This is an external class which can be referenced with `MapLib:GetFeature("Cleanup").Janitor`
18+
19+
Janitor is destructor based class designed to assist with clearing up connections and events.
20+
:::warning
21+
WARNING! This is an advanced feature.
22+
This page assumes you are familiar, comfortable and can write Luau code.
23+
:::
24+
]=]
25+
--[=[
26+
@within Janitor
27+
@since 0.11
28+
29+
@function new
30+
@param name string?
31+
@return _tasks: {[string]: any}
32+
@return context: string
33+
@return name: string?
34+
@return index: number | string
35+
@return __index: Janitor
36+
37+
Constructs a new Janitor class and is cached for later use. Janitor provides an option in case you want to name your Janitor for easier reference later.
38+
]=]
39+
40+
local function getJanitors()
41+
return require(script.Parent).Janitors :: any
42+
end
43+
44+
function Janitor.new(janitorName: string?)
45+
local self = setmetatable({}, Janitor)
46+
self._tasks = {}
47+
self.context = RunService:IsServer() and "Server" or "Client"
48+
self.name = janitorName
49+
50+
local janitors = getJanitors()
51+
52+
self.index = janitorName or #janitors + 1
53+
janitors[self.index] = self
54+
55+
return self
56+
end
57+
58+
--[=[
59+
@within Janitor
60+
@since 0.11
61+
@function isJanitor
62+
@return boolean
63+
Returns true if the given class is a Janitor, if not it returns false.
64+
]=]
65+
function Janitor.isJanitor(value: any): boolean
66+
return type(value) == "table" and value.ClassName == "Janitor"
67+
end
68+
69+
--[=[
70+
@within Janitor
71+
@since 0.11
72+
@method Give
73+
@param task any
74+
@return nil
75+
76+
**Example:**
77+
```lua
78+
local janitor = MapLib:GetFeature("Cleanup").Janitor.new() -- Constructs new Janitor
79+
80+
local part = Instance.new("Part")
81+
part.Anchored = true
82+
part.Size = Vector3.new(1, 1, 1)
83+
part.Parent = workspace
84+
85+
janitor:Give(part)
86+
87+
task.wait(5)
88+
janitor:Cleanup() -- Destroys the part
89+
```
90+
91+
This method is used to give Janitor tasks to cleanup, these tasks can be anything, some examples include, functions, threads, coroutines or anything with a .Destroy function.
92+
:::tip
93+
Janitor allows for tables to be given in as an argument. If Janitor detects a table it will loop through the table and add anything it finds will be added to the tasks table.
94+
95+
```lua
96+
local janitor = MapLib:GetFeature("Cleanup").Janitor.new() -- Constructs new Janitor
97+
98+
local connection1 = RunService.Heartbeat:Connect(function()
99+
print("Running")
100+
end)
101+
102+
local connection2 = RunService.Heartbeat:Connect(function()
103+
print("Running")
104+
end)
105+
106+
janitor:Give({connection1, connection2})
107+
108+
task.wait(5)
109+
janitor:Cleanup() -- Destroys both connections
110+
```
111+
:::
112+
:::caution
113+
Janitor does not have the ability to completly clear references if they are defined to a variable.
114+
To initate proper garbage collection using Janitor we recommend setting the variable to `reference = janitor:Give(task)` which will set the reference to nil.
115+
116+
```lua
117+
local janitor = MapLib:GetFeature("Cleanup").Janitor.new() -- Constructs new Janitor
118+
119+
local part = Instance.new("Part")
120+
part.Anchored = true
121+
part.Size = Vector3.new(1, 1, 1)
122+
part.Parent = workspace
123+
124+
part = janitor:Give(part)
125+
--Since :Give returns nil we can lose the reference and initate proper garbage collection.
126+
127+
task.wait(5)
128+
janitor:Cleanup() -- Destroys the part and initates garbage collection
129+
```
130+
:::
131+
]=]
132+
133+
function Janitor:Give(task)
134+
local function handleTask(subtask: any)
135+
assert(typeof(task) ~= "boolean", "Task cannot be a boolean")
136+
137+
local taskId = #self._tasks + 1
138+
self._tasks[taskId] = subtask
139+
return subtask
140+
end
141+
142+
--Task which contains multiple subTasks
143+
if typeof(task) == "table" then
144+
if not task.Destroy then
145+
for _, v in pairs(task) do
146+
handleTask(v)
147+
end
148+
return task
149+
else
150+
--Task with .Destroy method
151+
return handleTask(task)
152+
end
153+
end
154+
return handleTask(task)
155+
end
156+
157+
--[=[
158+
@within Janitor
159+
@since 0.11
160+
@method Cleanup
161+
@return nil
162+
163+
Calls for the Janitor to cleanup up all the tasks it was given.
164+
]=]
165+
function Janitor:Cleanup(taskTable: { any }?)
166+
local tasks = taskTable or self._tasks
167+
168+
--Influenced by Quenty's destructer implementation.
169+
170+
for index, task in pairs(tasks) do
171+
if typeof(task) == "RBXScriptConnection" then
172+
tasks[index] = nil
173+
task:Disconnect()
174+
end
175+
end
176+
177+
local index, task = next(tasks)
178+
179+
while task ~= nil do
180+
tasks[index] = nil
181+
182+
if type(task) == "function" then
183+
task()
184+
elseif typeof(task) == "thread" then
185+
coroutine.close(task)
186+
elseif typeof(task) == "RBXScriptConnection" then
187+
task:Disconnect()
188+
elseif task.Destroy then
189+
--Used for Janitor to Destroy other Janitors or a part with Destroy method
190+
task:Destroy()
191+
--cancel any promises given
192+
elseif getmetatable(task).prototype ~= nil then
193+
task:cancel()
194+
end
195+
index, task = next(tasks)
196+
end
197+
end
198+
199+
--[=[
200+
@within Janitor
201+
@since 0.11
202+
@method Destroy
203+
@return nil
204+
205+
Completely destroys Janitor and all references to it. If the Janitor has tasks then those tasks are cleaned up.
206+
]=]
207+
208+
function Janitor:Destroy()
209+
self:Cleanup()
210+
211+
--clears up any save janitors inside the cleanup table
212+
getJanitors()[self.index] = nil
213+
214+
table.clear(self)
215+
setmetatable(self, nil)
216+
end
217+
218+
return Janitor

0 commit comments

Comments
 (0)