@quenty/valueobject 13.7.0 → 13.8.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 +11 -0
- package/package.json +9 -9
- package/src/Shared/ValueObject.lua +78 -44
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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
|
+
# [13.8.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/valueobject@13.7.0...@quenty/valueobject@13.8.0) (2024-10-04)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Performance Improvements
|
|
10
|
+
|
|
11
|
+
* ValueObject uses no Maid, reducing table construction. Also defers construction of GoodSignal until needed ([5701538](https://github.com/Quenty/NevermoreEngine/commit/5701538fbc8c8be5dc4dad94023acb6b7be5477e))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [13.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/valueobject@13.6.0...@quenty/valueobject@13.7.0) (2024-09-25)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package @quenty/valueobject
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/valueobject",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.8.0",
|
|
4
4
|
"description": "To work like value objects in Roblox and track a single item with .Changed events",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"Quenty"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@quenty/brio": "^14.
|
|
30
|
-
"@quenty/ducktype": "^5.
|
|
31
|
-
"@quenty/loader": "^10.
|
|
32
|
-
"@quenty/maid": "^3.
|
|
33
|
-
"@quenty/rx": "^13.
|
|
34
|
-
"@quenty/signal": "^7.
|
|
35
|
-
"@quenty/valuebaseutils": "^13.
|
|
29
|
+
"@quenty/brio": "^14.8.0",
|
|
30
|
+
"@quenty/ducktype": "^5.6.0",
|
|
31
|
+
"@quenty/loader": "^10.6.0",
|
|
32
|
+
"@quenty/maid": "^3.4.0",
|
|
33
|
+
"@quenty/rx": "^13.8.0",
|
|
34
|
+
"@quenty/signal": "^7.7.0",
|
|
35
|
+
"@quenty/valuebaseutils": "^13.8.0"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "035abfa088c854a73e1c65b350267eaa17669646"
|
|
41
41
|
}
|
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
local require = require(script.Parent.loader).load(script)
|
|
8
8
|
|
|
9
|
-
local
|
|
9
|
+
local Brio = require("Brio")
|
|
10
|
+
local DuckTypeUtils = require("DuckTypeUtils")
|
|
10
11
|
local Maid = require("Maid")
|
|
12
|
+
local MaidTaskUtils = require("MaidTaskUtils")
|
|
11
13
|
local Observable = require("Observable")
|
|
12
|
-
local ValueBaseUtils = require("ValueBaseUtils")
|
|
13
14
|
local RxValueBaseUtils = require("RxValueBaseUtils")
|
|
14
|
-
local
|
|
15
|
-
local
|
|
15
|
+
local Signal = require("Signal")
|
|
16
|
+
local ValueBaseUtils = require("ValueBaseUtils")
|
|
16
17
|
|
|
17
18
|
local EMPTY_FUNCTION = function() end
|
|
18
19
|
|
|
@@ -22,35 +23,36 @@ ValueObject.ClassName = "ValueObject"
|
|
|
22
23
|
--[=[
|
|
23
24
|
Constructs a new value object
|
|
24
25
|
@param baseValue T
|
|
25
|
-
@param checkType string | nil
|
|
26
|
+
@param checkType string | nil | (value: T) -> (boolean, string)
|
|
26
27
|
@return ValueObject
|
|
27
28
|
]=]
|
|
28
29
|
function ValueObject.new(baseValue, checkType)
|
|
29
|
-
local self = {
|
|
30
|
+
local self = setmetatable({
|
|
30
31
|
_value = baseValue;
|
|
31
32
|
_checkType = checkType;
|
|
32
|
-
|
|
33
|
-
}
|
|
33
|
+
}, ValueObject)
|
|
34
34
|
|
|
35
|
-
if checkType
|
|
36
|
-
|
|
35
|
+
if type(checkType) == "stirng" then
|
|
36
|
+
if typeof(baseValue) ~= checkType then
|
|
37
|
+
error(string.format("Expected value of type %q, got %q instead", checkType, typeof(baseValue)))
|
|
38
|
+
end
|
|
39
|
+
elseif type(checkType) == "function" then
|
|
40
|
+
assert(checkType(baseValue))
|
|
37
41
|
end
|
|
38
42
|
|
|
43
|
+
return self
|
|
44
|
+
end
|
|
45
|
+
|
|
39
46
|
--[=[
|
|
40
47
|
Event fires when the value's object value change
|
|
41
48
|
@prop Changed Signal<T> -- fires with oldValue, newValue, ...
|
|
42
49
|
@within ValueObject
|
|
43
50
|
]=]
|
|
44
|
-
self.Changed = self._maid:Add(GoodSignal.new()) -- :Fire(newValue, oldValue, ...)
|
|
45
|
-
|
|
46
|
-
return setmetatable(self, ValueObject)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
51
|
|
|
50
52
|
--[=[
|
|
51
53
|
Returns the current check type, if any
|
|
52
54
|
|
|
53
|
-
@return string | nil
|
|
55
|
+
@return string | nil | (value: T) -> (boolean, string)
|
|
54
56
|
]=]
|
|
55
57
|
function ValueObject:GetCheckType()
|
|
56
58
|
return rawget(self, "_checkType")
|
|
@@ -96,6 +98,7 @@ function ValueObject:_toMountableObservable(value)
|
|
|
96
98
|
|
|
97
99
|
return nil
|
|
98
100
|
end
|
|
101
|
+
|
|
99
102
|
--[=[
|
|
100
103
|
Mounts the value to the observable. Overrides the last mount.
|
|
101
104
|
|
|
@@ -105,29 +108,21 @@ end
|
|
|
105
108
|
function ValueObject:Mount(value)
|
|
106
109
|
local observable = self:_toMountableObservable(value)
|
|
107
110
|
if observable then
|
|
108
|
-
self
|
|
109
|
-
|
|
110
|
-
local maid = Maid.new()
|
|
111
|
+
self:_cleanupLastMountedSub()
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
local sub = observable:Subscribe(function(...)
|
|
113
114
|
self:SetValue(...)
|
|
114
|
-
end))
|
|
115
|
-
|
|
116
|
-
maid:GiveTask(function()
|
|
117
|
-
if self._maid._mount == maid then
|
|
118
|
-
self._maid._mount = nil
|
|
119
|
-
end
|
|
120
115
|
end)
|
|
121
116
|
|
|
122
|
-
self
|
|
117
|
+
rawset(self, "_lastMountedSub", sub)
|
|
123
118
|
|
|
124
119
|
return function()
|
|
125
|
-
if self
|
|
126
|
-
self
|
|
120
|
+
if rawget(self, "_lastMountedSub") == sub then
|
|
121
|
+
self:_cleanupLastMountedSub()
|
|
127
122
|
end
|
|
128
123
|
end
|
|
129
124
|
else
|
|
130
|
-
self
|
|
125
|
+
self:_cleanupLastMountedSub()
|
|
131
126
|
|
|
132
127
|
self:SetValue(value)
|
|
133
128
|
|
|
@@ -135,12 +130,25 @@ function ValueObject:Mount(value)
|
|
|
135
130
|
end
|
|
136
131
|
end
|
|
137
132
|
|
|
133
|
+
function ValueObject:_cleanupLastMountedSub()
|
|
134
|
+
local lastSub = rawget(self, "_lastMountedSub")
|
|
135
|
+
if lastSub then
|
|
136
|
+
rawset(self, "_lastMountedSub", nil)
|
|
137
|
+
MaidTaskUtils.doTask(lastSub)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
138
141
|
--[=[
|
|
139
142
|
Observes the current value of the ValueObject
|
|
140
143
|
@return Observable<T>
|
|
141
144
|
]=]
|
|
142
145
|
function ValueObject:Observe()
|
|
143
|
-
|
|
146
|
+
local found = rawget(self, "_observable")
|
|
147
|
+
if found then
|
|
148
|
+
return found
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
local created = Observable.new(function(sub)
|
|
144
152
|
if not self.Destroy then
|
|
145
153
|
warn("[ValueObject.observeValue] - Connecting to dead ValueObject")
|
|
146
154
|
-- No firing, we're dead
|
|
@@ -148,21 +156,24 @@ function ValueObject:Observe()
|
|
|
148
156
|
return
|
|
149
157
|
end
|
|
150
158
|
|
|
151
|
-
local connection
|
|
152
|
-
|
|
153
|
-
connection = self.Changed:Connect(function(newValue, _, ...)
|
|
159
|
+
local connection = self.Changed:Connect(function(newValue, _, ...)
|
|
154
160
|
sub:Fire(newValue, ...)
|
|
155
161
|
end)
|
|
156
162
|
|
|
157
163
|
local args = rawget(self, "_lastEventContext")
|
|
164
|
+
local value = rawget(self, "_value")
|
|
158
165
|
if args then
|
|
159
|
-
sub:Fire(
|
|
166
|
+
sub:Fire(value, table.unpack(args, 1, args.n))
|
|
160
167
|
else
|
|
161
|
-
sub:Fire(
|
|
168
|
+
sub:Fire(value)
|
|
162
169
|
end
|
|
163
170
|
|
|
164
171
|
return connection
|
|
165
172
|
end)
|
|
173
|
+
|
|
174
|
+
-- We use a lot of these so let's cache the result which reduces the number of tables we have here
|
|
175
|
+
rawset(self, "_observable", created)
|
|
176
|
+
return created
|
|
166
177
|
end
|
|
167
178
|
|
|
168
179
|
--[=[
|
|
@@ -228,8 +239,12 @@ function ValueObject:SetValue(value, ...)
|
|
|
228
239
|
local previous = rawget(self, "_value")
|
|
229
240
|
local checkType = rawget(self, "_checkType")
|
|
230
241
|
|
|
231
|
-
if checkType
|
|
232
|
-
|
|
242
|
+
if type(checkType) == "string" then
|
|
243
|
+
if typeof(value) ~= checkType then
|
|
244
|
+
error(string.format("Expected value of type %q, got %q instead", checkType, typeof(value)))
|
|
245
|
+
end
|
|
246
|
+
elseif typeof(checkType) == "function" then
|
|
247
|
+
assert(checkType(value))
|
|
233
248
|
end
|
|
234
249
|
|
|
235
250
|
if previous ~= value then
|
|
@@ -240,8 +255,10 @@ function ValueObject:SetValue(value, ...)
|
|
|
240
255
|
end
|
|
241
256
|
|
|
242
257
|
rawset(self, "_value", value)
|
|
243
|
-
|
|
244
|
-
|
|
258
|
+
local changed = rawget(self, "Changed")
|
|
259
|
+
if changed then
|
|
260
|
+
changed:Fire(value, previous, ...)
|
|
261
|
+
end
|
|
245
262
|
end
|
|
246
263
|
end
|
|
247
264
|
|
|
@@ -251,10 +268,19 @@ end
|
|
|
251
268
|
@within ValueObject
|
|
252
269
|
]=]
|
|
253
270
|
function ValueObject:__index(index)
|
|
254
|
-
if index
|
|
255
|
-
return self._value
|
|
256
|
-
elseif ValueObject[index] then
|
|
271
|
+
if ValueObject[index] then
|
|
257
272
|
return ValueObject[index]
|
|
273
|
+
elseif index == "Value" then
|
|
274
|
+
return self._value
|
|
275
|
+
elseif index == "Changed" then
|
|
276
|
+
-- Defer construction of Changed event until something needs it, since a lot
|
|
277
|
+
-- of times we don't need it
|
|
278
|
+
|
|
279
|
+
local signal = Signal.new() -- :Fire(newValue, oldValue, ...)
|
|
280
|
+
|
|
281
|
+
rawset(self, "Changed", signal)
|
|
282
|
+
|
|
283
|
+
return signal
|
|
258
284
|
elseif index == "LastEventContext" then
|
|
259
285
|
local args = rawget(self, "_lastEventContext")
|
|
260
286
|
if args then
|
|
@@ -287,7 +313,15 @@ end
|
|
|
287
313
|
]=]
|
|
288
314
|
function ValueObject:Destroy()
|
|
289
315
|
rawset(self, "_value", nil)
|
|
290
|
-
|
|
316
|
+
|
|
317
|
+
self:_cleanupLastMountedSub()
|
|
318
|
+
|
|
319
|
+
-- Avoid using a maid here because we make a LOT of ValueObjects
|
|
320
|
+
local changed = rawget(self, "Changed")
|
|
321
|
+
if changed then
|
|
322
|
+
changed:Destroy()
|
|
323
|
+
end
|
|
324
|
+
|
|
291
325
|
setmetatable(self, nil)
|
|
292
326
|
end
|
|
293
327
|
|