Skip to content
This repository has been archived by the owner on Jul 31, 2024. It is now read-only.

Commit

Permalink
Merge pull request #69 from Sleitnick/v0.0.14-alpha
Browse files Browse the repository at this point in the history
v0.0.14-alpha
  • Loading branch information
Sleitnick authored Mar 15, 2021
2 parents b498995 + 5f329ef commit 56fd149
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 30 deletions.
17 changes: 13 additions & 4 deletions docs/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ connection:Disconnect()

The Connection object internal to the Signal module also has a Destroy method associated with it, so it will still play nicely with the Maid module.

It is possible to wrap an existing RBXScriptSignal (e.g. `BasePart.Touched`) using `Signal.Proxy`, which is useful when creating abstractions that utilize existing built-in signals:

```lua
local touchTap = Signal.Proxy(UserInputService.TouchTap)
```

--------------------

## [Thread](https://github.com/Sleitnick/Knit/blob/main/src/Knit/Util/Thread.lua)
Expand Down Expand Up @@ -102,6 +108,9 @@ maid:GiveTask(somePart)
maid:GiveTask(something.SomeEvent:Connect(function() end))
maid:GiveTask(function() end)

-- Give promises, which will have 'Cancel' called if the maid is cleaned up:
maid:GivePromise(somePromise)

-- Both Destroy and DoCleaning do the same thing:
maid:Destroy()
maid:DoCleaning()
Expand Down Expand Up @@ -238,7 +247,7 @@ end
function DanceFloor:HeartbeatUpdate(dt)
if (time() > self._nextUpdate) then
-- Set the assigned instance to a random color:
self._instance.Color = Color3.new(
self.Instance.Color = Color3.new(
math.random(),
math.random(),
math.random()
Expand Down Expand Up @@ -316,9 +325,9 @@ Extra functionality for Streamables. For instance, `StreamableUtil.Compound` can
local s1 = Streamable.new(someModel, "SomeChild")
local s2 = Streamable.new(anotherModel, "AnotherChild")

StreamableUtil.Compound({s1, s2}, function(streamables, maid)
local someChild = streamables[1].Instance
local anotherChild = streamables[2].Instance
StreamableUtil.Compound({Stream1 = s1, Stream2 = s2}, function(streamables, maid)
local someChild = streamables.Stream1.Instance
local anotherChild = streamables.Stream2.Instance
maid:GiveTask(function()
-- Cleanup (will be called if ANY streamables are cleaned up)
end)
Expand Down
19 changes: 10 additions & 9 deletions src/Knit/Util/Component.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
return self
end
-- FIELDS AFTER CONSTRUCTOR COMPLETES
MyComponent.Instance: Instance
-- OPTIONAL LIFECYCLE HOOKS
function MyComponent:Init() end -> Called right after constructor
function MyComponent:Deinit() end -> Called right before deconstructor
Expand Down Expand Up @@ -84,6 +87,7 @@ local Players = game:GetService("Players")

local IS_SERVER = RunService:IsServer()
local DEFAULT_WAIT_FOR_TIMEOUT = 60
local ATTRIBUTE_ID_NAME = "ComponentServerId"

-- Components will only work on instances parented under these descendants:
local DESCENDANT_WHITELIST = {workspace, Players}
Expand Down Expand Up @@ -264,13 +268,10 @@ function Component:_instanceAdded(instance)
self._nextId = (self._nextId + 1)
local id = (self._tag .. tostring(self._nextId))
if (IS_SERVER) then
local idStr = Instance.new("StringValue")
idStr.Name = "ServerID"
idStr.Value = id
idStr.Parent = instance
instance:SetAttribute(ATTRIBUTE_ID_NAME, id)
end
local obj = self._class.new(instance)
obj._instance = instance
obj.Instance = instance
obj._id = id
self._instancesToObjects[instance] = obj
table.insert(self._objects, obj)
Expand All @@ -288,12 +289,12 @@ end
function Component:_instanceRemoved(instance)
self._instancesToObjects[instance] = nil
for i,obj in ipairs(self._objects) do
if (obj._instance == instance) then
if (obj.Instance == instance) then
if (self._hasDeinit) then
obj:Deinit()
end
if (IS_SERVER and instance:FindFirstChild("ServerID")) then
instance.ServerID:Destroy()
if (IS_SERVER and instance.Parent and instance:GetAttribute(ATTRIBUTE_ID_NAME) ~= nil) then
instance:SetAttribute(ATTRIBUTE_ID_NAME, nil)
end
self.Removed:Fire(obj)
obj:Destroy()
Expand Down Expand Up @@ -336,7 +337,7 @@ end
function Component:WaitFor(instance, timeout)
local isName = (type(instance) == "string")
local function IsInstanceValid(obj)
return ((isName and obj._instance.Name == instance) or ((not isName) and obj._instance == instance))
return ((isName and obj.Instance.Name == instance) or ((not isName) and obj.Instance == instance))
end
for _,obj in ipairs(self._objects) do
if (IsInstanceValid(obj)) then
Expand Down
32 changes: 24 additions & 8 deletions src/Knit/Util/Maid.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
maid:GiveTask(task)
> task is an event connection, function, or instance/table with a 'Destroy' method
maid:GivePromise(promise)
> Give the maid a promise as a task, which will call 'promise:Cancel()' on cleanup
maid:DoCleaning()
> Alias for Destroy
Expand All @@ -19,18 +22,13 @@
--]]

--- Manages the cleaning of events and other things.
-- Useful for encapsulating state and make deconstructors easy
-- @classmod Maid
-- @see Signal
local Promise = require(script.Parent.Promise)


local Maid = {}
Maid.ClassName = "Maid"


--- Returns a new Maid object
-- @constructor Maid.new()
-- @treturn Maid
function Maid.new()
local self = setmetatable({
_tasks = {};
Expand Down Expand Up @@ -74,6 +72,8 @@ function Maid:__newindex(index, newTask)
oldTask:Disconnect()
elseif (oldTask.Destroy) then
oldTask:Destroy()
elseif (Promise.Is(oldTask)) then
oldTask:Cancel()
end
end
end
Expand All @@ -88,14 +88,28 @@ function Maid:GiveTask(task)
local taskId = (#self._tasks + 1)
self[taskId] = task

if (type(task) == "table" and (not task.Destroy)) then
if (type(task) == "table" and (not task.Destroy) and (not Promise.Is(task))) then
warn("[Maid.GiveTask] - Gave table task without .Destroy\n\n" .. debug.traceback())
end

return taskId
end


function Maid:GivePromise(promise)
assert(Promise.Is(promise), "Expected promise")
if (promise:GetStatus() ~= Promise.Status.Started) then
return promise
end
local newPromise = Promise.Resolve(promise)
local id = self:GiveTask(newPromise)
newPromise:Finally(function()
self[id] = nil
end)
return newPromise
end


--- Cleans up all tasks.
-- @alias Destroy
function Maid:DoCleaning()
Expand All @@ -119,6 +133,8 @@ function Maid:DoCleaning()
task:Disconnect()
elseif (task.Destroy) then
task:Destroy()
elseif (Promise.Is(task)) then
task:Cancel()
end
index, task = next(tasks)
end
Expand Down
37 changes: 34 additions & 3 deletions src/Knit/Util/Signal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@

--[[
signal = Signal.new([maid])
signal = Signal.new([maid: Maid])
signal = Signal.Proxy(rbxSignal: RBXScriptSignal [, maid: Maid])
Signal.Is(object: any): boolean
signal:Fire(...)
signal:Wait()
signal:WaitPromise()
signal:Destroy()
signal:DisconnectAll()
connection = signal:Connect(functionHandler)
connection = signal:Connect((...) -> void)
connection:Disconnect()
connection:IsConnected()
Expand Down Expand Up @@ -80,15 +83,42 @@ function Signal.new(maid)
end


function Signal.Proxy(rbxScriptSignal, maid)
assert(typeof(rbxScriptSignal) == "RBXScriptSignal", "Argument #1 must be of type RBXScriptSignal")
local signal = Signal.new(maid)
signal:_setProxy(rbxScriptSignal)
return signal
end


function Signal.Is(obj)
return (type(obj) == "table" and getmetatable(obj) == Signal)
end


function Signal:_setProxy(rbxScriptSignal)
assert(typeof(rbxScriptSignal) == "RBXScriptSignal", "Argument #1 must be of type RBXScriptSignal")
self:_clearProxy()
self._proxyHandle = rbxScriptSignal:Connect(function(...)
self:Fire(...)
end)
end


function Signal:_clearProxy()
if (self._proxyHandle) then
self._proxyHandle:Disconnect()
self._proxyHandle = nil
end
end


function Signal:Fire(...)
local totalListeners = (#self._connections + self._threads)
if (totalListeners == 0) then return end
local id = self._id
self._id += 1
self._args[id] = {#self._connections + self._threads, {n = select("#", ...), ...}}
self._args[id] = {totalListeners, {n = select("#", ...), ...}}
self._threads = 0
self._bindable:Fire(id)
end
Expand Down Expand Up @@ -140,6 +170,7 @@ end

function Signal:Destroy()
self:DisconnectAll()
self:_clearProxy()
self._bindable:Destroy()
end

Expand Down
10 changes: 5 additions & 5 deletions src/Knit/Util/StreamableUtil.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
local streamable1 = Streamable.new(someModel, "SomeChild")
local streamable2 = Streamable.new(anotherModel, "AnotherChild")
StreamableUtil.Compound({streamable1, streamable2}, function(streamables, maid)
local someChild = streamables[1].Instance
local anotherChild = streamables[2].Instance
StreamableUtil.Compound({S1 = streamable1, S2 = streamable2}, function(streamables, maid)
local someChild = streamables.S1.Instance
local anotherChild = streamables.S2.Instance
maid:GiveTask(function()
-- Cleanup
end)
Expand All @@ -34,7 +34,7 @@ function StreamableUtil.Compound(streamables, handler)
local allAvailable = false
local function Check()
if (allAvailable) then return end
for _,streamable in ipairs(streamables) do
for _,streamable in pairs(streamables) do
if (not streamable.Instance) then
return
end
Expand All @@ -47,7 +47,7 @@ function StreamableUtil.Compound(streamables, handler)
allAvailable = false
observeAllMaid:DoCleaning()
end
for _,streamable in ipairs(streamables) do
for _,streamable in pairs(streamables) do
compoundMaid:GiveTask(streamable:Observe(function(_child, maid)
Check()
maid:GiveTask(function()
Expand Down
2 changes: 1 addition & 1 deletion src/Knit/Version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.13-alpha
0.0.14-alpha

0 comments on commit 56fd149

Please sign in to comment.