@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 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.1.2",
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.1.2"
31
+ "@quenty/rx": "^3.3.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"
35
35
  },
36
- "gitHead": "e9cd4223c7fda7c852db9743ef844baa19cacb13"
36
+ "gitHead": "87ab1435a59a05e7dd745db1dfcec26116d1d71d"
37
37
  }
@@ -66,8 +66,10 @@ end
66
66
 
67
67
  function BrioUtils.first(brios, ...)
68
68
  for _, brio in pairs(brios) do
69
- if brio:IsDead() then
70
- return Brio.DEAD
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
- maid:GiveTask(brio:GetDiedSignal():Connect(function()
79
- topBrio:Kill()
80
- end))
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 the combineLatest
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
@@ -2,6 +2,6 @@
2
2
  "name": "node_modules",
3
3
  "globIgnorePaths": [ "**/.package-lock.json" ],
4
4
  "tree": {
5
- "$path": { "optional": "..\\node_modules" }
5
+ "$path": { "optional": "../node_modules" }
6
6
  }
7
7
  }