@quenty/brio 3.1.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +8 -0
- package/package.json +3 -3
- package/src/Shared/BrioUtils.lua +9 -5
- package/src/Shared/BrioUtils.spec.lua +74 -0
- package/src/Shared/RxBrioUtils.lua +67 -1
- package/src/Shared/RxBrioUtils.spec.lua +109 -0
- package/src/node_modules.project.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.3.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/brio@3.2.0...@quenty/brio@3.3.0) (2021-11-20)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Support MacOS syncing ([#225](https://github.com/Quenty/NevermoreEngine/issues/225)) ([03f9183](https://github.com/Quenty/NevermoreEngine/commit/03f918392c6a5bdd33f8a17c38de371d1e06c67a))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [3.2.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/brio@3.1.2...@quenty/brio@3.2.0) (2021-10-30)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* BrioUtils.first allows no brios to be passed into the brio. This allows BrioUtils.flatten to accept non-brios as a result. ([6cf4bdf](https://github.com/Quenty/NevermoreEngine/commit/6cf4bdf504ace3dfcc37faa717363c4455be52aa))
|
|
23
|
+
* Update RxBrioUtils to allow the transformation of a brio into an observable that emits the value, and then nil, on death. This is like combineLatest. ([86df73f](https://github.com/Quenty/NevermoreEngine/commit/86df73f529740a7785eafa4bcd51639e5e20574c))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
## [3.1.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/brio@3.1.1...@quenty/brio@3.1.2) (2021-10-30)
|
|
7
30
|
|
|
8
31
|
**Note:** Version bump only for package @quenty/brio
|
package/README.md
CHANGED
|
@@ -38,6 +38,14 @@ resources.
|
|
|
38
38
|
|
|
39
39
|
Anything may "kill" a brio by calling :Destroy() or :Kill().
|
|
40
40
|
|
|
41
|
+
## Design philosophy
|
|
42
|
+
|
|
43
|
+
Brios are designed to solve this issue where we emit an object with a lifetime associated with it from an Observable stream. This resource is only valid for some amount of time (for example, while the object is in the Roblox data model).
|
|
44
|
+
|
|
45
|
+
In order to know how long we can keep this object/use it, we wrap the object with a Brio, which denotes the lifetime of the object.
|
|
46
|
+
|
|
47
|
+
Modeling this with pure observables is very tricky because the subscriber will have to also monitor/emit a similar object with less clear conventions. For example an observable that emits the object, and then nil on death.
|
|
48
|
+
|
|
41
49
|
## API Surface
|
|
42
50
|
|
|
43
51
|
### `Brio.isBrio(value)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/brio",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Brios wrap an object and either are alive or dead",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@quenty/loader": "^3.1.1",
|
|
30
30
|
"@quenty/maid": "^2.0.1",
|
|
31
|
-
"@quenty/rx": "^3.
|
|
31
|
+
"@quenty/rx": "^3.3.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "87ab1435a59a05e7dd745db1dfcec26116d1d71d"
|
|
37
37
|
}
|
package/src/Shared/BrioUtils.lua
CHANGED
|
@@ -66,8 +66,10 @@ end
|
|
|
66
66
|
|
|
67
67
|
function BrioUtils.first(brios, ...)
|
|
68
68
|
for _, brio in pairs(brios) do
|
|
69
|
-
if brio
|
|
70
|
-
|
|
69
|
+
if Brio.isBrio(brio) then
|
|
70
|
+
if brio:IsDead() then
|
|
71
|
+
return Brio.DEAD
|
|
72
|
+
end
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
|
|
@@ -75,9 +77,11 @@ function BrioUtils.first(brios, ...)
|
|
|
75
77
|
local topBrio = Brio.new(...)
|
|
76
78
|
|
|
77
79
|
for _, brio in pairs(brios) do
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
if Brio.isBrio(brio) then
|
|
81
|
+
maid:GiveTask(brio:GetDiedSignal():Connect(function()
|
|
82
|
+
topBrio:Kill()
|
|
83
|
+
end))
|
|
84
|
+
end
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
maid:GiveTask(topBrio:GetDiedSignal():Connect(function()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
-- @module RxBrioUtils.spec.lua
|
|
3
|
+
|
|
4
|
+
local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).load(script)
|
|
5
|
+
|
|
6
|
+
local BrioUtils = require("BrioUtils")
|
|
7
|
+
local Brio = require("Brio")
|
|
8
|
+
|
|
9
|
+
return function()
|
|
10
|
+
describe("BrioUtils.flatten({})", function()
|
|
11
|
+
local brio = BrioUtils.flatten({})
|
|
12
|
+
|
|
13
|
+
describe("should return a brio that", function()
|
|
14
|
+
it("is a brio", function()
|
|
15
|
+
expect(brio).to.be.a("table")
|
|
16
|
+
expect(Brio.isBrio(brio)).to.equal(true)
|
|
17
|
+
end)
|
|
18
|
+
|
|
19
|
+
it("is alive", function()
|
|
20
|
+
expect(not brio:IsDead()).to.equal(true)
|
|
21
|
+
end)
|
|
22
|
+
|
|
23
|
+
it("contains a table", function()
|
|
24
|
+
expect(brio:GetValue()).to.be.a("table")
|
|
25
|
+
end)
|
|
26
|
+
|
|
27
|
+
it("contains a table with nothing in it", function()
|
|
28
|
+
expect(next(brio:GetValue())).to.equal(nil)
|
|
29
|
+
end)
|
|
30
|
+
end)
|
|
31
|
+
end)
|
|
32
|
+
|
|
33
|
+
describe("BrioUtils.flatten with out a brio in it", function()
|
|
34
|
+
local brio = BrioUtils.flatten({
|
|
35
|
+
value = 5;
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe("should return a brio that", function()
|
|
39
|
+
it("is a brio", function()
|
|
40
|
+
expect(brio).to.be.a("table")
|
|
41
|
+
expect(Brio.isBrio(brio)).to.equal(true)
|
|
42
|
+
end)
|
|
43
|
+
|
|
44
|
+
it("is alive", function()
|
|
45
|
+
expect(not brio:IsDead()).to.equal(true)
|
|
46
|
+
end)
|
|
47
|
+
|
|
48
|
+
it("contains a table", function()
|
|
49
|
+
expect(brio:GetValue()).to.be.a("table")
|
|
50
|
+
end)
|
|
51
|
+
|
|
52
|
+
it("contains a table with value", function()
|
|
53
|
+
expect(brio:GetValue().value).to.equal(5)
|
|
54
|
+
end)
|
|
55
|
+
end)
|
|
56
|
+
end)
|
|
57
|
+
|
|
58
|
+
describe("BrioUtils.flatten a dead brio in it", function()
|
|
59
|
+
local brio = BrioUtils.flatten({
|
|
60
|
+
value = Brio.DEAD;
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe("should return a brio that", function()
|
|
64
|
+
it("is a brio", function()
|
|
65
|
+
expect(brio).to.be.a("table")
|
|
66
|
+
expect(Brio.isBrio(brio)).to.equal(true)
|
|
67
|
+
end)
|
|
68
|
+
|
|
69
|
+
it("is dead", function()
|
|
70
|
+
expect(brio:IsDead()).to.equal(true)
|
|
71
|
+
end)
|
|
72
|
+
end)
|
|
73
|
+
end)
|
|
74
|
+
end
|
|
@@ -283,7 +283,8 @@ function RxBrioUtils.filter(predicate)
|
|
|
283
283
|
end
|
|
284
284
|
end
|
|
285
285
|
|
|
286
|
-
-- Flattens all the brios in
|
|
286
|
+
-- Flattens all the brios in one brio and combines them. Note that this method leads to
|
|
287
|
+
-- gaps in the lifetime of the brio.
|
|
287
288
|
function RxBrioUtils.combineLatest(observables)
|
|
288
289
|
assert(type(observables) == "table", "Bad observables")
|
|
289
290
|
|
|
@@ -306,6 +307,36 @@ function RxBrioUtils.switchMap(project, resultSelector)
|
|
|
306
307
|
return Rx.switchMap(RxBrioUtils.mapBrio(project), resultSelector)
|
|
307
308
|
end
|
|
308
309
|
|
|
310
|
+
--[[
|
|
311
|
+
Works line combineLatest, but allow the transformation of a brio into an observable
|
|
312
|
+
that emits the value, and then nil, on death.
|
|
313
|
+
|
|
314
|
+
The issue here is this:
|
|
315
|
+
|
|
316
|
+
1. Resources are found with combineLatest()
|
|
317
|
+
2. One resource dies
|
|
318
|
+
3. All resources are invalidated
|
|
319
|
+
4. We still wanted to be able to use most of the resources
|
|
320
|
+
|
|
321
|
+
With this method we are able to do this, as we'll re-emit a table with all resoruces
|
|
322
|
+
except the invalidated one.
|
|
323
|
+
]]
|
|
324
|
+
function RxBrioUtils.flatCombineLatest(observables)
|
|
325
|
+
assert(type(observables) == "table", "Bad observables")
|
|
326
|
+
|
|
327
|
+
local newObservables = {}
|
|
328
|
+
for key, observable in pairs(observables) do
|
|
329
|
+
if Observable.isObservable(observable) then
|
|
330
|
+
newObservables[key] = RxBrioUtils.flattenToValueAndNil(observable)
|
|
331
|
+
else
|
|
332
|
+
newObservables[key] = observable
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
return Rx.combineLatest(newObservables)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
-- Takes in a brio and returns an observable that completes ony
|
|
309
340
|
function RxBrioUtils.mapBrio(project)
|
|
310
341
|
assert(type(project) == "function", "Bad project")
|
|
311
342
|
|
|
@@ -323,6 +354,41 @@ function RxBrioUtils.mapBrio(project)
|
|
|
323
354
|
end
|
|
324
355
|
end
|
|
325
356
|
|
|
357
|
+
-- Transforms the brio into an observable that emits the initial value of the brio, and then another value on death
|
|
358
|
+
function RxBrioUtils.toEmitOnDeathObservable(brio, emitOnDeathValue)
|
|
359
|
+
if not Brio.isBrio(brio) then
|
|
360
|
+
return Rx.of(brio)
|
|
361
|
+
else
|
|
362
|
+
return Observable.new(function(sub)
|
|
363
|
+
if brio:IsDead() then
|
|
364
|
+
sub:Fire(emitOnDeathValue)
|
|
365
|
+
sub:Complete()
|
|
366
|
+
else
|
|
367
|
+
sub:Fire(brio:GetValue())
|
|
368
|
+
|
|
369
|
+
return brio:GetDiedSignal():Connect(function()
|
|
370
|
+
sub:Fire(emitOnDeathValue)
|
|
371
|
+
sub:Complete()
|
|
372
|
+
end)
|
|
373
|
+
end
|
|
374
|
+
end)
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
function RxBrioUtils.mapBrioToEmitOnDeathObservable(emitOnDeathValue)
|
|
379
|
+
return function(brio)
|
|
380
|
+
return RxBrioUtils.toEmitOnDeathObservable(brio, emitOnDeathValue)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
--- Takes in an observable of brios and returns an observable of the inner values that will also output
|
|
385
|
+
-- nil if there is no other value for the brio
|
|
386
|
+
function RxBrioUtils.emitOnDeath(emitOnDeathValue)
|
|
387
|
+
return Rx.switchMap(RxBrioUtils.mapBrioToEmitOnDeathObservable(emitOnDeathValue));
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
RxBrioUtils.flattenToValueAndNil = RxBrioUtils.emitOnDeath(nil)
|
|
391
|
+
|
|
326
392
|
function RxBrioUtils.onlyLastBrioSurvives()
|
|
327
393
|
return function(source)
|
|
328
394
|
return Observable.new(function(sub)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
-- @module RxBrioUtils.spec.lua
|
|
3
|
+
|
|
4
|
+
local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).load(script)
|
|
5
|
+
|
|
6
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
7
|
+
local Brio = require("Brio")
|
|
8
|
+
local Observable = require("Observable")
|
|
9
|
+
|
|
10
|
+
return function()
|
|
11
|
+
describe("RxBrioUtils.combineLatest({})", function()
|
|
12
|
+
it("should execute immediately", function()
|
|
13
|
+
local observe = RxBrioUtils.combineLatest({})
|
|
14
|
+
local brio
|
|
15
|
+
observe:Subscribe(function(result)
|
|
16
|
+
brio = result
|
|
17
|
+
end)
|
|
18
|
+
expect(brio).to.be.ok()
|
|
19
|
+
expect(Brio.isBrio(brio)).to.equal(true)
|
|
20
|
+
expect(brio:IsDead()).to.equal(true)
|
|
21
|
+
end)
|
|
22
|
+
end)
|
|
23
|
+
|
|
24
|
+
describe("RxBrioUtils.combineLatest({ value = Observable(Brio(5)) })", function()
|
|
25
|
+
it("should execute immediately", function()
|
|
26
|
+
local observe = RxBrioUtils.combineLatest({
|
|
27
|
+
value = Observable.new(function(sub)
|
|
28
|
+
sub:Fire(Brio.new(5));
|
|
29
|
+
end);
|
|
30
|
+
otherValue = 25;
|
|
31
|
+
})
|
|
32
|
+
local brio
|
|
33
|
+
|
|
34
|
+
observe:Subscribe(function(result)
|
|
35
|
+
brio = result
|
|
36
|
+
end)
|
|
37
|
+
expect(brio).to.be.ok()
|
|
38
|
+
expect(Brio.isBrio(brio)).to.equal(true)
|
|
39
|
+
expect(not brio:IsDead()).to.equal(true)
|
|
40
|
+
expect(brio:GetValue()).to.be.a("table")
|
|
41
|
+
expect(brio:GetValue().value).to.equal(5)
|
|
42
|
+
end)
|
|
43
|
+
end)
|
|
44
|
+
|
|
45
|
+
describe("RxBrioUtils.flatCombineLatest", function()
|
|
46
|
+
local doFire
|
|
47
|
+
local brio = Brio.new(5)
|
|
48
|
+
local observe = RxBrioUtils.flatCombineLatest({
|
|
49
|
+
value = Observable.new(function(sub)
|
|
50
|
+
sub:Fire(brio);
|
|
51
|
+
doFire = function(...)
|
|
52
|
+
sub:Fire(...)
|
|
53
|
+
end
|
|
54
|
+
end);
|
|
55
|
+
otherValue = 25;
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
local lastResult = nil
|
|
59
|
+
local fireCount = 0
|
|
60
|
+
|
|
61
|
+
it("should execute immediately", function()
|
|
62
|
+
observe:Subscribe(function(result)
|
|
63
|
+
lastResult = result
|
|
64
|
+
fireCount = fireCount + 1
|
|
65
|
+
end)
|
|
66
|
+
expect(fireCount).to.equal(1)
|
|
67
|
+
expect(lastResult).to.be.a("table")
|
|
68
|
+
expect(Brio.isBrio(lastResult)).to.equal(false)
|
|
69
|
+
expect(lastResult.value).to.equal(5)
|
|
70
|
+
expect(lastResult.otherValue).to.equal(25)
|
|
71
|
+
end)
|
|
72
|
+
|
|
73
|
+
it("should reset when the brio is killed", function()
|
|
74
|
+
expect(fireCount).to.equal(1)
|
|
75
|
+
|
|
76
|
+
brio:Kill()
|
|
77
|
+
|
|
78
|
+
expect(fireCount).to.equal(2)
|
|
79
|
+
expect(lastResult).to.be.a("table")
|
|
80
|
+
expect(Brio.isBrio(lastResult)).to.equal(false)
|
|
81
|
+
expect(lastResult.value).to.equal(nil)
|
|
82
|
+
expect(lastResult.otherValue).to.equal(25)
|
|
83
|
+
end)
|
|
84
|
+
|
|
85
|
+
it("should allow a new value", function()
|
|
86
|
+
expect(fireCount).to.equal(2)
|
|
87
|
+
|
|
88
|
+
doFire(Brio.new(70))
|
|
89
|
+
|
|
90
|
+
expect(fireCount).to.equal(3)
|
|
91
|
+
expect(lastResult).to.be.a("table")
|
|
92
|
+
expect(Brio.isBrio(lastResult)).to.equal(false)
|
|
93
|
+
expect(lastResult.value).to.equal(70)
|
|
94
|
+
expect(lastResult.otherValue).to.equal(25)
|
|
95
|
+
end)
|
|
96
|
+
|
|
97
|
+
it("should only fire once if we replace the value", function()
|
|
98
|
+
expect(fireCount).to.equal(3)
|
|
99
|
+
|
|
100
|
+
doFire(Brio.new(75))
|
|
101
|
+
|
|
102
|
+
expect(fireCount).to.equal(4)
|
|
103
|
+
expect(lastResult).to.be.a("table")
|
|
104
|
+
expect(Brio.isBrio(lastResult)).to.equal(false)
|
|
105
|
+
expect(lastResult.value).to.equal(75)
|
|
106
|
+
expect(lastResult.otherValue).to.equal(25)
|
|
107
|
+
end)
|
|
108
|
+
end)
|
|
109
|
+
end
|