diff --git a/.github/workflows/mkdocs-deploy.yml b/.github/workflows/mkdocs-deploy.yml
index 95cd0b638..4e267835f 100644
--- a/.github/workflows/mkdocs-deploy.yml
+++ b/.github/workflows/mkdocs-deploy.yml
@@ -5,8 +5,8 @@ on:
- main
env:
- FUSION_VERSION: 0.3
- FUSION_DEPLOY_TYPE: 'release'
+ FUSION_VERSION: 0.4
+ FUSION_DEPLOY_TYPE: 'dev'
jobs:
deploy:
diff --git a/.gitignore b/.gitignore
index e8b07d336..3487a1c47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,7 @@ desktop.ini
# Ignore sourcemap generated by Rojo
# Regen with `rojo sourcemap -o sourcemap.json .\test-runner.project.json`
-sourcemap.json
\ No newline at end of file
+sourcemap.json
+
+# Ignore MacOS DS_Store
+.DS_Store
diff --git a/docs/api-reference/memory/members/insert.md b/docs/api-reference/memory/members/insert.md
new file mode 100644
index 000000000..9088bcb3b
--- /dev/null
+++ b/docs/api-reference/memory/members/insert.md
@@ -0,0 +1,75 @@
+
+ Memory
+ Members
+ insert
+
+
+
+
+```Lua
+function Fusion.insert(
+ scope: Scope,
+ ...: Tasks...
+): Tasks...
+```
+
+Inserts destruction [tasks](../../types/task) passed in to the
+[scope](../../types/scope). Returns the clean up tasks to be used for variable
+declarations.
+
+
+!!! success "Use scoped() method syntax"
+ This function is intended to be accessed as a method on a scope:
+ ```Lua
+ local conn, ins = scope:insert(
+ RunService.Heartbeat:Connnect(doUpdate),
+ Instance.new("Part", workspace)
+ )
+ ```
+
+-----
+
+## Parameters
+
+
+ scope
+
+ : Scope <unknown>
+
+
+
+The [scope](../../types/scope) which should be used to store
+destruction tasks.
+
+
+ ...
+
+ : Tasks...
+
+
+
+The destruction [tasks](../../types/task) which should be inserted into the
+scope.
+
+-----
+
+
+ Returns
+
+ -> Tasks...
+
+
+
+The destruction [tasks](../../types/task) that has been inserted into the scope.
+
+-----
+
+## Learn More
+
+- [Scopes tutorial](../../../../tutorials/fundamentals/scopes)
diff --git a/docs/tutorials/fundamentals/scopes.md b/docs/tutorials/fundamentals/scopes.md
index 0fc6e1e0a..314e612e9 100644
--- a/docs/tutorials/fundamentals/scopes.md
+++ b/docs/tutorials/fundamentals/scopes.md
@@ -9,7 +9,7 @@ manage.
## Scopes
When you create many objects at once, you often want to destroy them together
-later.
+later.
To make this easier, some people add their objects to an array. Arrays that
group together objects like this are given a special name: *scopes*.
@@ -153,6 +153,16 @@ scope:doCleanup()
This makes it harder to mess up writing scopes. Your code reads more naturally,
too.
+For convenience, Fusion exposes the `insert` method to insert destruction tasks
+into a scope:
+
+```Lua
+local conn, ins = scope:insert(
+ RunService.Heartbeat:Connnect(doUpdate),
+ Instance.new("Part", workspace)
+)
+```
+
### Adding Methods In Bulk
Try passing `Fusion` to `scoped()` - it's a table with functions, too.
@@ -269,15 +279,13 @@ local uiScope = scoped(Fusion)
-- ... create the ui ...
-table.insert(
- uiScope,
+uiScope:insert(
dropdownOpened:Connect(function()
local dropdownScope = uiScope:deriveScope()
-- ... create the dropdown ...
- table.insert(
- dropdownScope,
+ dropdownScope:insert(
dropdownClosed:Connect(function()
dropdownScope:doCleanup()
end)
@@ -298,20 +306,18 @@ To help with this, Fusion provides an `innerScope` method. It works just like
- When the original scope is cleaned up, the 'inner scope' is cleaned up too
- You can still call `doCleanup()` to clean the inner scope up early
-```Lua hl_lines="8"
+```Lua hl_lines="7"
local uiScope = scoped(Fusion)
-- ... create the ui ...
-table.insert(
- uiScope,
+uiScope:insert(
dropdownOpened:Connect(function()
local dropdownScope = uiScope:innerScope()
-- ... create the dropdown ...
- table.insert(
- dropdownScope,
+ dropdownScope:insert(
dropdownClosed:Connect(function()
dropdownScope:doCleanup()
end)
@@ -340,4 +346,4 @@ example, you'll need to create a scope in your main code file to start using
Fusion, and you might want to make a few more in other parts of your code.
However, Fusion manages most of your scopes for you, so for large parts of your
-codebase, you won't have to consider scopes and destruction at all.
\ No newline at end of file
+codebase, you won't have to consider scopes and destruction at all.
diff --git a/mkdocs.yml b/mkdocs.yml
index f6e11b0d8..f3ecff1a3 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -86,7 +86,7 @@ nav:
- Sharing Values: tutorials/best-practices/sharing-values.md
- Error Safety: tutorials/best-practices/error-safety.md
- Optimisation: tutorials/best-practices/optimisation.md
-
+
- Examples:
- Examples: examples/index.md
- Cookbook:
@@ -118,20 +118,22 @@ nav:
- deriveScope: api-reference/memory/members/derivescope.md
- doCleanup: api-reference/memory/members/docleanup.md
- scoped: api-reference/memory/members/scoped.md
+ - innerScope: api-reference/memory/members/innerscope.md
+ - insert: api-reference/memory/members/insert.md
- Graph:
- Types:
- - GraphObject: api-reference/graph/types/graphobject.md
+ - GraphObject: api-reference/graph/types/graphobject.md
- Observer: api-reference/graph/types/observer.md
- Members:
- Observer: api-reference/graph/members/observer.md
- State:
- Types:
- - UsedAs: api-reference/state/types/usedas.md
- - Computed: api-reference/state/types/computed.md
+ - UsedAs: api-reference/state/types/usedas.md
+ - Computed: api-reference/state/types/computed.md
- For: api-reference/state/types/for.md
- - StateObject: api-reference/state/types/stateobject.md
- - Use: api-reference/state/types/use.md
- - Value: api-reference/state/types/value.md
+ - StateObject: api-reference/state/types/stateobject.md
+ - Use: api-reference/state/types/use.md
+ - Value: api-reference/state/types/value.md
- Members:
- Computed: api-reference/state/members/computed.md
- ForKeys: api-reference/state/members/forkeys.md
diff --git a/package.json b/package.json
index b6d5c697d..49b46838b 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,15 @@
-{
- "name": "@dphfox/fusion",
- "version": "0.3.0",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "https://github.com/dphfox/Fusion.git"
- },
- "contributors": [
- "dphfox"
- ],
- "bugs": {
- "url": "https://github.com/dphfox/Fusion/issues"
- }
+{
+ "name": "@dphfox/fusion",
+ "version": "0.3.0",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/dphfox/Fusion.git"
+ },
+ "contributors": [
+ "dphfox"
+ ],
+ "bugs": {
+ "url": "https://github.com/dphfox/Fusion/issues"
+ }
}
\ No newline at end of file
diff --git a/src/Memory/insert.luau b/src/Memory/insert.luau
new file mode 100644
index 000000000..282dfba6d
--- /dev/null
+++ b/src/Memory/insert.luau
@@ -0,0 +1,22 @@
+--!strict
+--!nolint LocalUnused
+--!nolint LocalShadow
+local task = nil -- Disable usage of Roblox's task scheduler
+
+--[[
+ Inserts clean up tasks passed in to the scope.
+]]
+local Package = script.Parent.Parent
+local Types = require(Package.Types)
+
+local function insert(
+ scope: Types.Scope,
+ ...: Tasks...
+): Tasks...
+ for index = 1, select("#", ...) do
+ table.insert(scope, select(index, ...))
+ end
+ return ...
+end
+
+return insert
diff --git a/src/Types.luau b/src/Types.luau
index a11c2db03..ddecd4092 100644
--- a/src/Types.luau
+++ b/src/Types.luau
@@ -252,6 +252,7 @@ export type Fusion = {
scoped: ScopedConstructor,
deriveScope: DeriveScopeConstructor,
innerScope: DeriveScopeConstructor,
+ insert: (scope: Scope, Tasks...) -> Tasks...,
peek: Use,
Value: ValueConstructor,
@@ -288,7 +289,7 @@ export type ExternalProvider = {
logWarn: (
errorString: string
) -> (),
-
+
doTaskImmediate: (
resume: () -> ()
) -> (),
diff --git a/src/init.luau b/src/init.luau
index 64785f3aa..bd52a3b5b 100644
--- a/src/init.luau
+++ b/src/init.luau
@@ -49,8 +49,9 @@ local Fusion: Fusion = table.freeze {
deriveScope = require(script.Memory.deriveScope),
doCleanup = require(script.Memory.doCleanup),
innerScope = require(script.Memory.innerScope),
+ insert = require(script.Memory.insert),
scoped = require(script.Memory.scoped),
-
+
-- Graph
Observer = require(script.Graph.Observer),
diff --git a/test/Spec/Memory/insert.spec.luau b/test/Spec/Memory/insert.spec.luau
new file mode 100644
index 000000000..0e5bef3de
--- /dev/null
+++ b/test/Spec/Memory/insert.spec.luau
@@ -0,0 +1,80 @@
+--!strict
+--!nolint LocalUnused
+local task = nil -- Disable usage of Roblox's task scheduler
+
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local Fusion = ReplicatedStorage.Fusion
+
+local doCleanup = require(Fusion.Memory.doCleanup)
+local insert = require(Fusion.Memory.insert)
+
+return function()
+ local it = getfenv().it
+
+ it("should accept zero tasks", function()
+ local expect = getfenv().expect
+
+ local scope = scoped()
+ insert(scope)
+
+ expect(#scope).to.equal(0)
+ end)
+
+ it("should accept single tasks", function()
+ local expect = getfenv().expect
+
+ local isDestroyed = false
+ local scope = scoped()
+ insert(scope, function()
+ isDestroyed = true
+ end)
+
+ expect(#scope).to.equal(1)
+ doCleanup(scope)
+
+ expect(isDestroyed).to.equal(true)
+ end)
+
+ it("should accept multiple tasks", function()
+ local expect = getfenv().expect
+
+ local counter = 0
+ local scope = scoped()
+
+ insert(scope, function()
+ counter += 1
+ end)
+ insert(scope, function()
+ counter += 1
+ end)
+ insert(scope, {
+ function()
+ counter += 1
+ end
+ })
+
+ expect(#scope).to.equal(3)
+ doCleanup(scope)
+ expect(counter).to.equal(3)
+ end)
+
+ it("should return the given tasks", function()
+ local expect = getfenv().expect
+
+ local counter = 0
+ local function onDestroy()
+ counter += 1
+ end
+ local function onDestroy2()
+ counter += 2
+ end
+ local scope = scoped()
+
+ local returnedDestroy, returnedDestroy2 = insert(scope, onDestroy, onDestroy2)
+ expect(returnedDestroy).to.equal(onDestroy)
+ expect(returnedDestroy2).to.equal(onDestroy2)
+ expect(#scope).to.equal(2)
+ doCleanup(scope)
+ expect(counter).to.equal(3)
+ end)
+end