Skip to content

Instantly share code, notes, and snippets.

@znotfireman
Created February 25, 2026 09:25
Show Gist options
  • Select an option

  • Save znotfireman/163823b93a4e091cb7823055d266dbce to your computer and use it in GitHub Desktop.

Select an option

Save znotfireman/163823b93a4e091cb7823055d266dbce to your computer and use it in GitHub Desktop.
Janitor/Maid-like class from Welcome To Hell (MIT License)
export type DestroyableClass = {
[any]: any,
read Destroy: (self: any) -> ()?,
read destroy: (self: any) -> ()?,
read Disconnect: (self: any) -> ()?,
read disconnect: (self: any) -> ()?,
}
-- TODO: unioning to any because Luau sucks
export type Destroyable = any | Instance | RBXScriptConnection | thread | DestroyableClass | { any } | () -> ...any
local DESTRUCTORS = {
"Destroy",
"destroy",
"Disconnect",
"disconnect",
}
local function destroy(x: any)
if typeof(x) == "Instance" then
x:Destroy()
elseif typeof(x) == "RBXScriptConnection" and x.Connected then
x:Disconnect()
elseif type(x) == "thread" then
pcall(task.cancel, x)
elseif type(x) == "function" then
(x :: () -> ())()
elseif type(x) == "table" then
for _, method in DESTRUCTORS do
if type(x[method]) == "function" then
x[method](x)
return
end
end
local len = #(x :: any)
if len ~= 0 then
for index = #(x :: any), 1, -1 do
destroy(x[index])
end
end
end
end
return destroy
local destroy = require("./destroy")
local tables = require("./tables")
local threadpool = require("@libs/threadpool")
local Life = {}
Life.__index = Life
Life.type = "Life"
export type Life = setmetatable<{ destroyed: boolean, destroyables: { destroy.Destroyable } }, typeof(Life)>
function Life.new(): Life
return setmetatable({
destroyed = false,
destroyables = {} :: { destroy.Destroyable },
}, Life)
end
function Life.isLife(x: any)
return typeof(x) == "table" and getmetatable(x) == Life
end
function Life.isDestroyed(self: Life)
return self.destroyed
end
function Life.isAlive(self: Life)
return not self.destroyed
end
function Life.add<T>(self: Life, bound: T): T
if self.destroyed then
return bound
end
table.insert(self.destroyables, bound)
return bound
end
function Life.spawn<T>(self: Life, bound: (...any) -> ...any): thread | (...any) -> ...any
if self.destroyed then
return bound
end
local thread = threadpool.spawn(bound)
table.insert(self.destroyables, function()
task.cancel(thread)
end)
return thread
end
function Life.addMany<T...>(self: Life, ...: T...)
if self.destroyed then
return ...
end
for index = 1, select("#", ...) do
table.insert(self.destroyables, select(index, ...) :: destroy.Destroyable)
end
return ...
end
function Life.remove<T>(self: Life, bound: T): T
if self.destroyed then
return bound
end
tables.findAndRemove(self.destroyables, bound)
return bound
end
function Life.removeMany<T...>(self: Life, ...: T...): T...
if self.destroyed then
return ...
end
for index = 1, select("#", ...) do
tables.findAndRemove(self.destroyables, select(index, ...))
end
return ...
end
function Life.removeAll(self: Life)
table.clear(self.destroyables)
end
function Life.delay<A...>(self: Life, fn: (A...) -> (), time: number, ...: A...)
if self.destroyed then
return
end
local thread = task.delay(time, function(...)
if self:isAlive() then
fn(...)
end
end, ...)
self:add(function()
task.cancel(thread)
end)
end
function Life.connect(self: Life, signal: RBXScriptSignal, fn: (...any) -> ...any): RBXScriptConnection?
if self.destroyed then
return nil
end
return Life.add(self, signal:Connect(fn))
end
function Life.tie(self: Life, signal: RBXScriptSignal): RBXScriptConnection?
if self.destroyed then
return nil
end
return Life.add(
self,
signal:Connect(function()
Life.destroy(self)
end)
)
end
function Life.inner(self: Life)
local inner = Life.new()
self:add(inner)
inner:add(function()
self:remove(inner)
end)
return inner
end
function Life.isEmpty(self: Life)
if self.destroyed then
return true
end
return #self.destroyables == 0
end
function Life.destroy(self: Life)
if self.destroyed then
return
end
self.destroyed = true
for index = #self.destroyables, 1, -1 do
destroy(self.destroyables[index])
end
table.clear(self.destroyables)
end
function Life.__tostring(_: Life)
return "Life"
end
function Life.__len(self: Life)
return #self.destroyables
end
return table.freeze(Life)
@Primiti-ve
Copy link

you forgot a few libs....... (table and threadpool)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment