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 @@ + + +

+ :octicons-workflow-24: + insert + + -> Tasks... + +

+ +```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