Last active
August 14, 2021 17:16
-
-
Save howmanysmall/c7d72158ffae16d43960221a327e8cf1 to your computer and use it in GitHub Desktop.
for those of you who hold the weeb card
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- WeebMaid | |
-- Original by Validark | |
-- Modifications by pobammer | |
-- roblox-ts support by OverHash and Validark | |
-- LinkToInstance fixed by Elttob. | |
local RunService = game:GetService("RunService") | |
local Promise = require(script.Parent.Promise) | |
local Heartbeat = RunService.Heartbeat | |
local IndicesReference = newproxy(true) | |
getmetatable(IndicesReference).__tostring = function() | |
return "IndicesReference" | |
end | |
local LinkToInstanceIndex = newproxy(true) | |
getmetatable(LinkToInstanceIndex).__tostring = function() | |
return "LinkToInstanceIndex" | |
end | |
local NOT_A_PROMISE = "Invalid argument #1 to 'WeebMaid:AddPromise' (Promise expected, got %s (%s))" | |
local METHOD_NOT_FOUND_ERROR = "Object %s doesn't have method %s, are you sure you want to add it? Traceback: %s" | |
local WeebMaid = { | |
ClassName = "WeebMaid"; | |
__index = { | |
CurrentlyCleaning = true; | |
[IndicesReference] = nil; | |
}; | |
} | |
local TypeDefaults = { | |
["function"] = true; | |
RBXScriptConnection = "Disconnect"; | |
} | |
--[[** | |
Instantiates a new WeebMaid object. | |
@returns [t:WeebMaid] | |
**--]] | |
function WeebMaid.new() | |
return setmetatable({ | |
CurrentlyCleaning = false; | |
[IndicesReference] = nil; | |
}, WeebMaid) | |
end | |
--[[** | |
Determines if the passed object is a WeebMaid. | |
@param [t:any] Object The object you are checking. | |
@returns [t:boolean] Whether or not the object is a WeebMaid. | |
**--]] | |
function WeebMaid.Is(Object) | |
return type(Object) == "table" and getmetatable(Object) == WeebMaid | |
end | |
--[[** | |
Adds an `Object` to WeebMaid for later cleanup, where `MethodName` is the key of the method within `Object` which should be called at cleanup time. If the `MethodName` is `true` the `Object` itself will be called instead. If passed an index it will occupy a namespace which can be `Remove()`d or overwritten. Returns the `Object`. | |
@param [t:any] Object The object you want to clean up. | |
@param [t:string|true?] MethodName The name of the method that will be used to clean up. If not passed, it will first check if the object's type exists in TypeDefaults, and if that doesn't exist, it assumes `Destroy`. | |
@param [t:any?] Index The index that can be used to clean up the object manually. | |
@returns [t:any] The object that was passed. | |
**--]] | |
function WeebMaid.__index:Add(Object, MethodName, Index) | |
if Index then | |
self:Remove(Index) | |
local This = self[IndicesReference] | |
if not This then | |
This = {} | |
self[IndicesReference] = This | |
end | |
This[Index] = Object | |
end | |
MethodName = MethodName or TypeDefaults[typeof(Object)] or "Destroy" | |
if type(Object) ~= "function" and not Object[MethodName] then | |
warn(string.format(METHOD_NOT_FOUND_ERROR, tostring(Object), tostring(MethodName), debug.traceback(nil, 2))) | |
end | |
self[Object] = MethodName | |
return Object | |
end | |
WeebMaid.__index.GiveTask = WeebMaid.__index.Add | |
-- My version of Promise has PascalCase, but I converted it to use lowerCamelCase for this release since obviously that's important to do. | |
--[[** | |
Adds a promise to the WeebMaid. If the WeebMaid is cleaned up and the promise is not completed, the promise will be cancelled. | |
@param [t:Promise] PromiseObject The promise you want to add to the WeebMaid. | |
@returns [t:Promise] | |
**--]] | |
function WeebMaid.__index:AddPromise(PromiseObject) | |
if not Promise.is(PromiseObject) then | |
error(string.format(NOT_A_PROMISE, typeof(PromiseObject), tostring(PromiseObject))) | |
end | |
if PromiseObject:getStatus() == Promise.Status.Started then | |
local Id = newproxy(false) | |
local NewPromise = self:Add(Promise.resolve(PromiseObject), "cancel", Id) | |
NewPromise:finallyCall(self.Remove, self, Id) | |
return NewPromise | |
else | |
return PromiseObject | |
end | |
end | |
WeebMaid.__index.GivePromise = WeebMaid.__index.AddPromise | |
--[[** | |
Cleans up whatever `Object` was set to this namespace by the 3rd parameter of `:Add()`. | |
@param [t:any] Index The index you want to remove. | |
@returns [t:WeebMaid] The same WeebMaid, for chaining reasons. | |
**--]] | |
function WeebMaid.__index:Remove(Index) | |
local This = self[IndicesReference] | |
if This then | |
local Object = This[Index] | |
if Object then | |
local MethodName = self[Object] | |
if MethodName then | |
if MethodName == true then | |
Object() | |
else | |
local ObjectMethod = Object[MethodName] | |
if ObjectMethod then | |
ObjectMethod(Object) | |
end | |
end | |
self[Object] = nil | |
end | |
This[Index] = nil | |
end | |
end | |
return self | |
end | |
--[[** | |
Gets whatever object is stored with the given index, if it exists. This was added since Maid allows getting the task using `__index`. | |
@param [t:any] Index The index that the object is stored under. | |
@returns [t:any?] This will return the object if it is found, but it won't return anything if it doesn't exist. | |
**--]] | |
function WeebMaid.__index:Get(Index) | |
local This = self[IndicesReference] | |
if This then | |
return This[Index] | |
end | |
end | |
WeebMaid.__index.Request = WeebMaid.__index.Get | |
--[[** | |
Calls each Object's `MethodName` (or calls the Object if `MethodName == true`) and removes them from the WeebMaid. Also clears the namespace. This function is also called when you call a WeebMaid Object (so it can be used as a destructor callback). | |
@returns [t:void] | |
**--]] | |
function WeebMaid.__index:Cleanup() | |
if not self.CurrentlyCleaning then | |
self.CurrentlyCleaning = nil | |
for Object, MethodName in next, self do | |
if Object == IndicesReference then | |
continue | |
end | |
if MethodName == true then | |
Object() | |
else | |
local ObjectMethod = Object[MethodName] | |
if ObjectMethod then | |
ObjectMethod(Object) | |
end | |
end | |
self[Object] = nil | |
end | |
local This = self[IndicesReference] | |
if This then | |
for Index in next, This do | |
This[Index] = nil | |
end | |
self[IndicesReference] = {} | |
end | |
self.CurrentlyCleaning = false | |
end | |
end | |
--[[** | |
Calls `:Cleanup()` and renders the WeebMaid unusable. | |
@returns [t:void] | |
**--]] | |
function WeebMaid.__index:Destroy() | |
self:Cleanup() | |
table.clear(self) | |
setmetatable(self, nil) | |
end | |
WeebMaid.__call = WeebMaid.__index.Cleanup | |
--- Makes the WeebMaid clean up when the instance is destroyed | |
-- @param Instance Instance The Instance the WeebMaid will wait for to be Destroyed | |
-- @returns Disconnectable table to stop WeebMaid from being cleaned up upon Instance Destroy (automatically cleaned up by WeebMaid, btw) | |
-- @author Corecii | |
local Disconnect = {Connected = true} | |
Disconnect.__index = Disconnect | |
function Disconnect:Disconnect() | |
if self.Connected then | |
self.Connected = false | |
self.Connection:Disconnect() | |
end | |
end | |
function Disconnect:__tostring() | |
return "Disconnect<" .. tostring(self.Connected) .. ">" | |
end | |
--[[** | |
"Links" this WeebMaid to an Instance, such that the WeebMaid will `Cleanup` when the Instance is `Destroyed()` and garbage collected. A WeebMaid may only be linked to one instance at a time, unless `AllowMultiple` is true. When called with a truthy `AllowMultiple` parameter, the WeebMaid will "link" the Instance without overwriting any previous links, and will also not be overwritable. When called with a falsy `AllowMultiple` parameter, the WeebMaid will overwrite the previous link which was also called with a falsy `AllowMultiple` parameter, if applicable. | |
@param [t:Instance] Object The instance you want to link the WeebMaid to. | |
@param [t:boolean?] AllowMultiple Whether or not to allow multiple links on the same WeebMaid. | |
@returns [t:RbxScriptConnection] A pseudo RBXScriptConnection that can be disconnected. | |
**--]] | |
function WeebMaid.__index:LinkToInstance(Object, AllowMultiple) | |
local Connection | |
local IndexToUse = AllowMultiple and newproxy(false) or LinkToInstanceIndex | |
local IsNilParented = Object.Parent == nil | |
local ManualDisconnect = setmetatable({}, Disconnect) | |
local function ChangedFunction(_DoNotUse, NewParent) | |
if ManualDisconnect.Connected then | |
_DoNotUse = nil | |
IsNilParented = NewParent == nil | |
if IsNilParented then | |
coroutine.wrap(function() | |
Heartbeat:Wait() | |
if not ManualDisconnect.Connected then | |
return | |
elseif not Connection.Connected then | |
self:Cleanup() | |
else | |
while IsNilParented and Connection.Connected and ManualDisconnect.Connected do | |
Heartbeat:Wait() | |
end | |
if ManualDisconnect.Connected and IsNilParented then | |
self:Cleanup() | |
end | |
end | |
end)() | |
end | |
end | |
end | |
Connection = Object.AncestryChanged:Connect(ChangedFunction) | |
ManualDisconnect.Connection = Connection | |
if IsNilParented then | |
ChangedFunction(nil, Object.Parent) | |
end | |
Object = nil | |
return self:Add(ManualDisconnect, "Disconnect", IndexToUse) | |
end | |
--[[** | |
Links several instances to a WeebMaid, which is then returned. | |
@param [t:...Instance] ... All the instances you want linked. | |
@returns [t:WeebMaid] A WeebMaid that can be used to manually disconnect all LinkToInstances. | |
**--]] | |
function WeebMaid.__index:LinkToInstances(...) | |
local ManualCleanup = WeebMaid.new() | |
for _, Object in ipairs({...}) do | |
ManualCleanup:Add(self:LinkToInstance(Object, true), "Disconnect") | |
end | |
return ManualCleanup | |
end | |
return WeebMaid |
Oh wow it works with promises
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So now WeebMaid > Janitor?