@quenty/tie 10.6.0 → 10.7.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 +19 -0
- package/README.md +4 -0
- package/package.json +16 -15
- package/src/Shared/Members/Methods/TieMethodDefinition.lua +44 -0
- package/src/Shared/{Implementation → Members/Methods}/TieMethodImplementation.lua +4 -5
- package/src/Shared/Members/Methods/TieMethodInterfaceUtils.lua +58 -0
- package/src/Shared/{Interface → Members/Properties}/TiePropertyChangedSignalConnection.lua +1 -1
- package/src/Shared/Members/Properties/TiePropertyDefinition.lua +58 -0
- package/src/Shared/{Interface → Members/Properties}/TiePropertyInterface.lua +42 -59
- package/src/Shared/Members/Signals/TieSignalConnection.lua +63 -0
- package/src/Shared/Members/Signals/TieSignalDefinition.lua +38 -0
- package/src/Shared/{Implementation → Members/Signals}/TieSignalImplementation.lua +29 -15
- package/src/Shared/Members/Signals/TieSignalInterface.lua +90 -0
- package/src/Shared/Members/TieMemberDefinition.lua +104 -0
- package/src/Shared/Members/TieMemberInterface.lua +94 -0
- package/src/Shared/Realms/TieRealmUtils.lua +41 -0
- package/src/Shared/{Definition/Types → Realms}/TieRealms.lua +3 -4
- package/src/Shared/Services/TieRealmService.lua +31 -0
- package/src/Shared/TieDefinition.lua +708 -0
- package/src/Shared/TieImplementation.lua +167 -0
- package/src/Shared/TieInterface.lua +129 -0
- package/src/Shared/{Encoding → Utils}/TieUtils.lua +4 -1
- package/test/modules/Server/Action/Action.lua +4 -7
- package/test/modules/Server/Door.lua +5 -10
- package/test/scripts/Server/ServerMain.server.lua +3 -3
- package/src/Shared/Definition/TieDefinition.lua +0 -507
- package/src/Shared/Definition/TieMethodDefinition.lua +0 -61
- package/src/Shared/Definition/TiePropertyDefinition.lua +0 -64
- package/src/Shared/Definition/TieSignalDefinition.lua +0 -59
- package/src/Shared/Definition/Types/TieRealmUtils.lua +0 -60
- package/src/Shared/Implementation/TieImplementation.lua +0 -129
- package/src/Shared/Interface/TieInterface.lua +0 -122
- package/src/Shared/Interface/TieInterfaceUtils.lua +0 -70
- package/src/Shared/Interface/TieMethodInterfaceUtils.lua +0 -43
- package/src/Shared/Interface/TieSignalConnection.lua +0 -89
- package/src/Shared/Interface/TieSignalInterface.lua +0 -61
- /package/src/Shared/{Implementation → Members/Properties}/TiePropertyImplementation.lua +0 -0
- /package/src/Shared/{Implementation → Members/Properties}/TiePropertyImplementationUtils.lua +0 -0
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
Constructs a new interface declaration which allows for interface usage
|
|
3
|
+
between both Roblox API users and OOP users, as well as without accessing a
|
|
4
|
+
[ServiceBag].
|
|
5
|
+
|
|
6
|
+
Also allows for extensibility via implementing interfaces.
|
|
7
|
+
|
|
8
|
+
```lua
|
|
9
|
+
local require = require(script.Parent.loader).load(script)
|
|
10
|
+
|
|
11
|
+
local TieDefinition = require("TieDefinition")
|
|
12
|
+
|
|
13
|
+
return TieDefinition.new("GlobalLeaderboard", {
|
|
14
|
+
-- Modification
|
|
15
|
+
[TieDefinition.Realms.SERVER] = {
|
|
16
|
+
RemoveAllEntries = TieDefinition.Types.METHOD;
|
|
17
|
+
SetEntryValueForUserId = TieDefinition.Types.METHOD;
|
|
18
|
+
IncrementEntryForUserId = TieDefinition.Types.METHOD;
|
|
19
|
+
CreateEntry = TieDefinition.Types.METHOD;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
-- List
|
|
23
|
+
ObserveEntriesBrio = TieDefinition.Types.METHOD;
|
|
24
|
+
GetEntryList = TieDefinition.Types.METHOD;
|
|
25
|
+
|
|
26
|
+
-- Single
|
|
27
|
+
ObserveEntryByUserId = TieDefinition.Types.METHOD;
|
|
28
|
+
GetEntryForUserId = TieDefinition.Types.METHOD;
|
|
29
|
+
|
|
30
|
+
-- Plural
|
|
31
|
+
ObserveEntriesByUserIdBrio = TieDefinition.Types.METHOD;
|
|
32
|
+
GetEntriesForUserId = TieDefinition.Types.METHOD;
|
|
33
|
+
|
|
34
|
+
-- Rendering
|
|
35
|
+
ObserveTopCount = TieDefinition.Types.METHOD;
|
|
36
|
+
ObserveFormatType = TieDefinition.Types.METHOD;
|
|
37
|
+
ObserveTitleTranslationKey = TieDefinition.Types.METHOD;
|
|
38
|
+
ObserveEntryTranslationKey = TieDefinition.Types.METHOD;
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
@class TieDefinition
|
|
43
|
+
]=]
|
|
44
|
+
|
|
45
|
+
local require = require(script.Parent.loader).load(script)
|
|
46
|
+
|
|
47
|
+
local Brio = require("Brio")
|
|
48
|
+
local Maid = require("Maid")
|
|
49
|
+
local Observable = require("Observable")
|
|
50
|
+
local Rx = require("Rx")
|
|
51
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
52
|
+
local RxCollectionServiceUtils = require("RxCollectionServiceUtils")
|
|
53
|
+
local RxInstanceUtils = require("RxInstanceUtils")
|
|
54
|
+
local RxStateStackUtils = require("RxStateStackUtils")
|
|
55
|
+
local String = require("String")
|
|
56
|
+
local Symbol = require("Symbol")
|
|
57
|
+
local Table = require("Table")
|
|
58
|
+
local TieImplementation = require("TieImplementation")
|
|
59
|
+
local TieInterface = require("TieInterface")
|
|
60
|
+
local TieMethodDefinition = require("TieMethodDefinition")
|
|
61
|
+
local TiePropertyDefinition = require("TiePropertyDefinition")
|
|
62
|
+
local TieRealms = require("TieRealms")
|
|
63
|
+
local TieRealmUtils = require("TieRealmUtils")
|
|
64
|
+
local TieSignalDefinition = require("TieSignalDefinition")
|
|
65
|
+
local ValueObject = require("ValueObject")
|
|
66
|
+
|
|
67
|
+
local UNSET_VALUE = Symbol.named("unsetValue")
|
|
68
|
+
|
|
69
|
+
local TieDefinition = {}
|
|
70
|
+
TieDefinition.ClassName = "TieDefinition"
|
|
71
|
+
TieDefinition.__index = TieDefinition
|
|
72
|
+
|
|
73
|
+
TieDefinition.Types = Table.readonly({
|
|
74
|
+
METHOD = Symbol.named("method");
|
|
75
|
+
SIGNAL = Symbol.named("signal");
|
|
76
|
+
PROPERTY = Symbol.named("property"); -- will default to nil
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
TieDefinition.Realms = TieRealms
|
|
80
|
+
|
|
81
|
+
--[=[
|
|
82
|
+
Constructs a new TieDefinition with the given members
|
|
83
|
+
|
|
84
|
+
@param definitionName string
|
|
85
|
+
@param members any
|
|
86
|
+
@return TieDefinition
|
|
87
|
+
]=]
|
|
88
|
+
function TieDefinition.new(definitionName, members)
|
|
89
|
+
local self = setmetatable({}, TieDefinition)
|
|
90
|
+
|
|
91
|
+
self._definitionName = assert(definitionName, "No definitionName")
|
|
92
|
+
self._memberMap = {}
|
|
93
|
+
self._defaultTieRealm = TieRealms.SHARED
|
|
94
|
+
|
|
95
|
+
-- Start in shared world
|
|
96
|
+
self:_addMembers(members, TieRealms.SHARED)
|
|
97
|
+
|
|
98
|
+
self.Server = setmetatable({
|
|
99
|
+
_defaultTieRealm = TieRealms.SERVER
|
|
100
|
+
}, {
|
|
101
|
+
__index = self;
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
self.Client = setmetatable({
|
|
105
|
+
_defaultTieRealm = TieRealms.CLIENT
|
|
106
|
+
}, {
|
|
107
|
+
__index = self;
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
return self
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
function TieDefinition:_addMembers(members, realm)
|
|
114
|
+
for memberName, memberTypeOrDefaultValue in pairs(members) do
|
|
115
|
+
if TieRealmUtils.isTieRealm(memberName) then
|
|
116
|
+
|
|
117
|
+
self:_addMembers(memberTypeOrDefaultValue, memberName)
|
|
118
|
+
elseif type(memberName) == "string" then
|
|
119
|
+
self:_addMember(memberName, memberTypeOrDefaultValue, realm)
|
|
120
|
+
else
|
|
121
|
+
error(string.format("[TieDefinition] - Bad memberName %q, expected either string or TieRealm.", tostring(memberName)))
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
function TieDefinition:_addMember(memberName, memberTypeOrDefaultValue, realm)
|
|
127
|
+
if memberTypeOrDefaultValue == TieDefinition.Types.METHOD then
|
|
128
|
+
self._memberMap[memberName] = TieMethodDefinition.new(self, memberName, realm)
|
|
129
|
+
elseif memberTypeOrDefaultValue == TieDefinition.Types.SIGNAL then
|
|
130
|
+
self._memberMap[memberName] = TieSignalDefinition.new(self, memberName, realm)
|
|
131
|
+
elseif memberTypeOrDefaultValue == TieDefinition.Types.PROPERTY then
|
|
132
|
+
self._memberMap[memberName] = TiePropertyDefinition.new(self, memberName, nil, realm)
|
|
133
|
+
else
|
|
134
|
+
self._memberMap[memberName] = TiePropertyDefinition.new(self, memberName, memberTypeOrDefaultValue, realm)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
--[=[
|
|
139
|
+
Gets all valid interfaces for this adornee
|
|
140
|
+
@param adornee Instance
|
|
141
|
+
@param tieRealm TieRealm?
|
|
142
|
+
@return { TieInterface }
|
|
143
|
+
]=]
|
|
144
|
+
function TieDefinition:GetImplementations(adornee: Instance, tieRealm)
|
|
145
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
146
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
147
|
+
|
|
148
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
149
|
+
|
|
150
|
+
local implementations = {}
|
|
151
|
+
|
|
152
|
+
for _, item in pairs(self:GetImplementationParents(adornee, tieRealm)) do
|
|
153
|
+
table.insert(implementations, TieInterface.new(self, item, nil, tieRealm))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
return implementations
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
function TieDefinition:GetImplClass()
|
|
160
|
+
return "Camera"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
function TieDefinition:GetImplementationParents(adornee, tieRealm)
|
|
164
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
165
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
166
|
+
|
|
167
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
168
|
+
|
|
169
|
+
local validContainerNameSet = self:GetValidContainerNameSet(tieRealm)
|
|
170
|
+
|
|
171
|
+
local implParents = {}
|
|
172
|
+
|
|
173
|
+
for _, implParent in pairs(adornee:GetChildren()) do
|
|
174
|
+
if validContainerNameSet[implParent.Name] then
|
|
175
|
+
if self:IsImplementation(implParent) then
|
|
176
|
+
table.insert(implParents, implParent)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
return implParents
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
--[=[
|
|
185
|
+
Observes all the children implementations for this adornee
|
|
186
|
+
|
|
187
|
+
@param adornee Instance
|
|
188
|
+
@param tieRealm TieRealm?
|
|
189
|
+
@return Observable<Brio<TieInterface>>
|
|
190
|
+
]=]
|
|
191
|
+
function TieDefinition:ObserveChildrenBrio(adornee: Instance, tieRealm)
|
|
192
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
193
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
194
|
+
|
|
195
|
+
return RxInstanceUtils.observeChildrenBrio(adornee):Pipe({
|
|
196
|
+
RxBrioUtils.flatMapBrio(function(child)
|
|
197
|
+
return self:ObserveBrio(child, tieRealm)
|
|
198
|
+
end)
|
|
199
|
+
})
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
--[=[
|
|
203
|
+
Promises the implementation
|
|
204
|
+
|
|
205
|
+
@param adornee Adornee
|
|
206
|
+
@param tieRealm TieRealm?
|
|
207
|
+
@return Promise<TieInterface>
|
|
208
|
+
]=]
|
|
209
|
+
function TieDefinition:Promise(adornee, tieRealm)
|
|
210
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
211
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
212
|
+
|
|
213
|
+
-- TODO: Support cancellation cleanup here.
|
|
214
|
+
|
|
215
|
+
return Rx.toPromise(self:Observe(adornee, tieRealm):Pipe({
|
|
216
|
+
Rx.where(function(value)
|
|
217
|
+
return value ~= nil
|
|
218
|
+
end)
|
|
219
|
+
}))
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
--[=[
|
|
223
|
+
Gets all valid interfaces for this adornee's children
|
|
224
|
+
|
|
225
|
+
@param adornee Instance
|
|
226
|
+
@param tieRealm TieRealm?
|
|
227
|
+
@return { TieInterface }
|
|
228
|
+
]=]
|
|
229
|
+
function TieDefinition:GetChildren(adornee: Instance, tieRealm)
|
|
230
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
231
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
232
|
+
|
|
233
|
+
local implementations = {}
|
|
234
|
+
|
|
235
|
+
-- TODO: Make this faster
|
|
236
|
+
for _, item in pairs(adornee:GetChildren()) do
|
|
237
|
+
for _, option in pairs(self:GetImplementations(item, tieRealm)) do
|
|
238
|
+
table.insert(implementations, option)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
return implementations
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
--[=[
|
|
246
|
+
Finds the implementation on the adornee. Alais for [FindFirstImplementation]
|
|
247
|
+
|
|
248
|
+
@param adornee Adornee
|
|
249
|
+
@param tieRealm TieRealm?
|
|
250
|
+
@return TieInterface | nil
|
|
251
|
+
]=]
|
|
252
|
+
function TieDefinition:Find(adornee: Instance, tieRealm)
|
|
253
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
254
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
255
|
+
|
|
256
|
+
return self:FindFirstImplementation(adornee, tieRealm)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
--[=[
|
|
260
|
+
Observes all implementations that are tagged with the given tag name
|
|
261
|
+
|
|
262
|
+
@param tagName string
|
|
263
|
+
@param tieRealm TieRealm?
|
|
264
|
+
@return TieInterface | nil
|
|
265
|
+
]=]
|
|
266
|
+
function TieDefinition:ObserveAllTaggedBrio(tagName, tieRealm)
|
|
267
|
+
assert(type(tagName) == "string", "Bad tagName")
|
|
268
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
269
|
+
|
|
270
|
+
return RxCollectionServiceUtils.observeTaggedBrio(tagName):Pipe({
|
|
271
|
+
RxBrioUtils.flatMapBrio(function(instance)
|
|
272
|
+
return self:ObserveBrio(instance, tieRealm)
|
|
273
|
+
end)
|
|
274
|
+
})
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
--[=[
|
|
278
|
+
Finds the first valid interfaces for this adornee
|
|
279
|
+
@param adornee Instance
|
|
280
|
+
@param tieRealm TieRealm?
|
|
281
|
+
@return TieInterface
|
|
282
|
+
]=]
|
|
283
|
+
function TieDefinition:FindFirstImplementation(adornee: Instance, tieRealm)
|
|
284
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
285
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
286
|
+
|
|
287
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
288
|
+
|
|
289
|
+
local validContainerNameSet = self:GetValidContainerNameSet(tieRealm)
|
|
290
|
+
for _, item in pairs(adornee:GetChildren()) do
|
|
291
|
+
if validContainerNameSet[item.Name] then
|
|
292
|
+
if self:IsImplementation(item, tieRealm) then
|
|
293
|
+
return TieInterface.new(self, item, nil, tieRealm)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
return nil
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
--[=[
|
|
302
|
+
Returns true if the adornee implements the interface, and false otherwise.
|
|
303
|
+
@param adornee Instance
|
|
304
|
+
@param tieRealm TieRealm?
|
|
305
|
+
@return boolean
|
|
306
|
+
]=]
|
|
307
|
+
function TieDefinition:HasImplementation(adornee: Instance, tieRealm)
|
|
308
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
309
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
310
|
+
|
|
311
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
312
|
+
|
|
313
|
+
-- TODO: Maybe something faster
|
|
314
|
+
for containerName, _ in pairs(self:GetValidContainerNameSet(tieRealm)) do
|
|
315
|
+
local implParent = adornee:FindFirstChild(containerName)
|
|
316
|
+
if not implParent then
|
|
317
|
+
continue
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
if self:IsImplementation(implParent, tieRealm) then
|
|
321
|
+
return true
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
return false
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
--[=[
|
|
329
|
+
Observes whether the adornee implements the interface.
|
|
330
|
+
@param adornee Instance
|
|
331
|
+
@param tieRealm TieRealm?
|
|
332
|
+
@return Observable<boolean>>
|
|
333
|
+
]=]
|
|
334
|
+
function TieDefinition:ObserveIsImplemented(adornee: Instance, tieRealm): boolean
|
|
335
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
336
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
337
|
+
|
|
338
|
+
return self:ObserveLastImplementationBrio(adornee, tieRealm)
|
|
339
|
+
:Pipe({
|
|
340
|
+
RxBrioUtils.map(function(result)
|
|
341
|
+
return result and true or false
|
|
342
|
+
end);
|
|
343
|
+
RxBrioUtils.emitOnDeath(false);
|
|
344
|
+
Rx.defaultsTo(false);
|
|
345
|
+
Rx.distinct();
|
|
346
|
+
})
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
--[=[
|
|
350
|
+
Observes whether the implParent is a valid implementation
|
|
351
|
+
@param implParent Instance
|
|
352
|
+
@param tieRealm TieRealm?
|
|
353
|
+
@return Observable<boolean>>
|
|
354
|
+
]=]
|
|
355
|
+
function TieDefinition:ObserveIsImplementation(implParent: Instance, tieRealm)
|
|
356
|
+
assert(typeof(implParent) == "Instance", "Bad implParent")
|
|
357
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
358
|
+
|
|
359
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
360
|
+
|
|
361
|
+
return self:_observeImplementation(implParent, tieRealm)
|
|
362
|
+
:Pipe({
|
|
363
|
+
RxBrioUtils.map(function(result)
|
|
364
|
+
return result and true or false
|
|
365
|
+
end);
|
|
366
|
+
RxBrioUtils.emitOnDeath(false);
|
|
367
|
+
Rx.defaultsTo(false);
|
|
368
|
+
Rx.distinct();
|
|
369
|
+
})
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
--[=[
|
|
373
|
+
Observes whether the implParent is a valid implementation on the given adornee
|
|
374
|
+
@param implParent Instance
|
|
375
|
+
@param adornee Instance
|
|
376
|
+
@param tieRealm TieRealm?
|
|
377
|
+
@return Observable<boolean>>
|
|
378
|
+
]=]
|
|
379
|
+
function TieDefinition:ObserveIsImplementedOn(implParent: Instance, adornee: Instance, tieRealm)
|
|
380
|
+
assert(typeof(implParent) == "Instance", "Bad implParent")
|
|
381
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
382
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
383
|
+
|
|
384
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
385
|
+
|
|
386
|
+
return RxInstanceUtils.observePropertyBrio(implParent, "Parent", function(parent)
|
|
387
|
+
return parent == adornee
|
|
388
|
+
end):Pipe({
|
|
389
|
+
RxBrioUtils.switchMapBrio(function()
|
|
390
|
+
return self:_observeImplementation(implParent, tieRealm)
|
|
391
|
+
end);
|
|
392
|
+
RxBrioUtils.map(function(result)
|
|
393
|
+
return result and true or false
|
|
394
|
+
end);
|
|
395
|
+
RxBrioUtils.emitOnDeath(false);
|
|
396
|
+
Rx.defaultsTo(false);
|
|
397
|
+
Rx.distinct();
|
|
398
|
+
})
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
--[=[
|
|
402
|
+
Observes a valid implementation wrapped in a brio if it exists.
|
|
403
|
+
|
|
404
|
+
@param adornee Instance
|
|
405
|
+
@param tieRealm TieRealm?
|
|
406
|
+
@return Observable<Brio<TieImplementation<T>>>
|
|
407
|
+
]=]
|
|
408
|
+
function TieDefinition:ObserveBrio(adornee: Instance, tieRealm)
|
|
409
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
410
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
411
|
+
|
|
412
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
413
|
+
|
|
414
|
+
return self:ObserveValidContainerChildrenBrio(adornee, tieRealm)
|
|
415
|
+
:Pipe({
|
|
416
|
+
RxBrioUtils.switchMapBrio(function(implParent)
|
|
417
|
+
return self:_observeImplementation(implParent, tieRealm)
|
|
418
|
+
end);
|
|
419
|
+
RxBrioUtils.onlyLastBrioSurvives();
|
|
420
|
+
})
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
--[=[
|
|
424
|
+
Observes a valid implementation if it exists, or nil
|
|
425
|
+
|
|
426
|
+
@param adornee Instance
|
|
427
|
+
@param tieRealm TieRealm?
|
|
428
|
+
@return Observable<TieImplementation<T> | nil>>
|
|
429
|
+
]=]
|
|
430
|
+
function TieDefinition:Observe(adornee: Instance, tieRealm)
|
|
431
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
432
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
433
|
+
|
|
434
|
+
return self:ObserveBrio(adornee, tieRealm):Pipe({
|
|
435
|
+
RxStateStackUtils.topOfStack();
|
|
436
|
+
})
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
TieDefinition.ObserveLastImplementation = TieDefinition.Observe
|
|
440
|
+
TieDefinition.ObserveLastImplementationBrio = TieDefinition.ObserveBrio
|
|
441
|
+
|
|
442
|
+
--[=[
|
|
443
|
+
Observes valid implementations wrapped in a brio if it exists.
|
|
444
|
+
@param adornee Instance
|
|
445
|
+
@param tieRealm TieRealm?
|
|
446
|
+
@return Observable<Brio<TieImplementation<T>>>
|
|
447
|
+
]=]
|
|
448
|
+
function TieDefinition:ObserveImplementationsBrio(adornee: Instance, tieRealm)
|
|
449
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
450
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
451
|
+
|
|
452
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
453
|
+
|
|
454
|
+
return self:ObserveValidContainerChildrenBrio(adornee, tieRealm)
|
|
455
|
+
:Pipe({
|
|
456
|
+
RxBrioUtils.flatMapBrio(function(implParent)
|
|
457
|
+
return self:_observeImplementation(implParent, tieRealm)
|
|
458
|
+
end)
|
|
459
|
+
})
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
function TieDefinition:ObserveValidContainerChildrenBrio(adornee, tieRealm)
|
|
463
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
464
|
+
assert(TieRealmUtils.isTieRealm(tieRealm), "Bad tieRealm")
|
|
465
|
+
|
|
466
|
+
local validContainerNameSet = self:GetValidContainerNameSet(tieRealm)
|
|
467
|
+
|
|
468
|
+
return RxInstanceUtils.observeChildrenBrio(adornee, function(value)
|
|
469
|
+
-- Just assume our name doesn't change
|
|
470
|
+
return value:IsA(self:GetImplClass()) and validContainerNameSet[value.Name] and true or false
|
|
471
|
+
end)
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
function TieDefinition:_observeImplementation(implParent, tieRealm)
|
|
475
|
+
assert(TieRealmUtils.isTieRealm(tieRealm), "Bad tieRealm")
|
|
476
|
+
|
|
477
|
+
return Observable.new(function(sub)
|
|
478
|
+
-- Bind to all children, instead of individually. This is a
|
|
479
|
+
-- performance gain.
|
|
480
|
+
|
|
481
|
+
local maid = Maid.new()
|
|
482
|
+
|
|
483
|
+
local update
|
|
484
|
+
do
|
|
485
|
+
local isImplemented = maid:Add(ValueObject.new(UNSET_VALUE))
|
|
486
|
+
|
|
487
|
+
maid:GiveTask(isImplemented.Changed:Connect(function()
|
|
488
|
+
maid._brio = nil
|
|
489
|
+
|
|
490
|
+
if not sub:IsPending() then
|
|
491
|
+
return
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
if isImplemented.Value then
|
|
495
|
+
local brio = Brio.new(TieInterface.new(self, implParent, nil, tieRealm))
|
|
496
|
+
sub:Fire(brio)
|
|
497
|
+
maid._brio = brio
|
|
498
|
+
else
|
|
499
|
+
maid._brio = nil
|
|
500
|
+
end
|
|
501
|
+
end))
|
|
502
|
+
|
|
503
|
+
function update()
|
|
504
|
+
isImplemented.Value = self:IsImplementation(implParent, tieRealm)
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
maid:GiveTask(implParent.ChildAdded:Connect(function(child)
|
|
509
|
+
maid[child] = child:GetPropertyChangedSignal("Name"):Connect(update)
|
|
510
|
+
update()
|
|
511
|
+
end))
|
|
512
|
+
|
|
513
|
+
for memberName, member in pairs(self._memberMap) do
|
|
514
|
+
if not member:IsAllowedOnInterface(tieRealm) then
|
|
515
|
+
continue
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
if member.ClassName == "TiePropertyDefinition" then
|
|
519
|
+
maid:GiveTask(implParent:GetAttributeChangedSignal(memberName):Connect(update))
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
maid:GiveTask(implParent.ChildRemoved:Connect(function(child)
|
|
524
|
+
maid[child] = nil
|
|
525
|
+
update()
|
|
526
|
+
end))
|
|
527
|
+
|
|
528
|
+
for _, child in pairs(implParent:GetChildren()) do
|
|
529
|
+
maid[child] = child:GetPropertyChangedSignal("Name"):Connect(update)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
update()
|
|
533
|
+
|
|
534
|
+
return maid
|
|
535
|
+
end)
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
--[=[
|
|
539
|
+
Ensures implementation of the object, binding table values and Lua OOP objects
|
|
540
|
+
to Roblox objects that can be invoked generally.
|
|
541
|
+
|
|
542
|
+
```lua
|
|
543
|
+
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
@param adornee Instance -- Adornee to implement interface on
|
|
547
|
+
@param implementer table? -- Table with all interface values or nil
|
|
548
|
+
@param tieRealm TieRealm?
|
|
549
|
+
@return TieImplementation<T>
|
|
550
|
+
]=]
|
|
551
|
+
function TieDefinition:Implement(adornee: Instance, implementer, tieRealm)
|
|
552
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
553
|
+
assert(type(implementer) == "table" or implementer == nil, "Bad implementer")
|
|
554
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
555
|
+
|
|
556
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
557
|
+
|
|
558
|
+
return TieImplementation.new(self, adornee, implementer, tieRealm)
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
--[=[
|
|
562
|
+
Gets an interface to the tie definition. Not this can be done
|
|
563
|
+
on any Roblox instance. If the instance does not implement the interface,
|
|
564
|
+
invoking interface methods, or querying the interface will result
|
|
565
|
+
in errors.
|
|
566
|
+
|
|
567
|
+
```tip
|
|
568
|
+
Probably use :Find() instead of Get, since this always returns an interface.
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
@param adornee Instance -- Adornee to get interface on
|
|
572
|
+
@param tieRealm TieRealm?
|
|
573
|
+
@return TieInterface<T>
|
|
574
|
+
]=]
|
|
575
|
+
function TieDefinition:Get(adornee: Instance, tieRealm)
|
|
576
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
577
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
578
|
+
|
|
579
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
580
|
+
|
|
581
|
+
return TieInterface.new(self, nil, adornee, tieRealm)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
--[=[
|
|
585
|
+
Gets the name of the definition
|
|
586
|
+
@return string
|
|
587
|
+
]=]
|
|
588
|
+
function TieDefinition:GetName(): string
|
|
589
|
+
return self._definitionName
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
--[=[
|
|
593
|
+
Gets the valid container name set for the tie definition
|
|
594
|
+
|
|
595
|
+
@param tieRealm TieRealm
|
|
596
|
+
@return { [string]: boolean }
|
|
597
|
+
]=]
|
|
598
|
+
function TieDefinition:GetValidContainerNameSet(tieRealm)
|
|
599
|
+
-- TODO: Still generate unique datamodel key here?
|
|
600
|
+
|
|
601
|
+
if tieRealm == TieRealms.CLIENT then
|
|
602
|
+
-- Shared implements both...
|
|
603
|
+
return {
|
|
604
|
+
[self._definitionName .. "Client"] = true;
|
|
605
|
+
[self._definitionName .. "Shared"] = true;
|
|
606
|
+
}
|
|
607
|
+
elseif tieRealm == TieRealms.SERVER then
|
|
608
|
+
return {
|
|
609
|
+
[self._definitionName] = true;
|
|
610
|
+
[self._definitionName .. "Shared"] = true;
|
|
611
|
+
}
|
|
612
|
+
elseif tieRealm == TieRealms.SHARED then
|
|
613
|
+
-- Technically on the implementation shared is very strict,
|
|
614
|
+
-- but we allow any calls here for discovery
|
|
615
|
+
return {
|
|
616
|
+
[self._definitionName] = true;
|
|
617
|
+
[self._definitionName .. "Client"] = true;
|
|
618
|
+
[self._definitionName .. "Shared"] = true;
|
|
619
|
+
}
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
--[=[
|
|
624
|
+
Gets a container name for a new container. See [GetValidContainerNameSet]
|
|
625
|
+
for the full set of valid container names for the tie definition.
|
|
626
|
+
|
|
627
|
+
@param tieRealm TieRealm
|
|
628
|
+
@return string
|
|
629
|
+
]=]
|
|
630
|
+
function TieDefinition:GetNewContainerName(tieRealm): string
|
|
631
|
+
assert(TieRealmUtils.isTieRealm(tieRealm), "Bad tieRealm")
|
|
632
|
+
|
|
633
|
+
-- TODO: Handle server/actor
|
|
634
|
+
|
|
635
|
+
if tieRealm == TieRealms.CLIENT then
|
|
636
|
+
return self._definitionName .. "Client"
|
|
637
|
+
elseif tieRealm == TieRealms.SERVER then
|
|
638
|
+
return self._definitionName
|
|
639
|
+
elseif tieRealm == TieRealms.SHARED then
|
|
640
|
+
-- Shared contains both server and client
|
|
641
|
+
return self._definitionName .. "Shared"
|
|
642
|
+
else
|
|
643
|
+
error("Bad tieRealm")
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
function TieDefinition:GetMemberMap()
|
|
648
|
+
return self._memberMap
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
--[=[
|
|
652
|
+
Returns true if the implParent is an implementation
|
|
653
|
+
|
|
654
|
+
@param implParent Instance
|
|
655
|
+
@param tieRealm TieRealm? -- Optional tie realm
|
|
656
|
+
@return boolean
|
|
657
|
+
]=]
|
|
658
|
+
function TieDefinition:IsImplementation(implParent, tieRealm)
|
|
659
|
+
assert(typeof(implParent) == "Instance", "Bad implParent")
|
|
660
|
+
assert(TieRealmUtils.isTieRealm(tieRealm) or tieRealm == nil, "Bad tieRealm")
|
|
661
|
+
|
|
662
|
+
tieRealm = tieRealm or self._defaultTieRealm
|
|
663
|
+
|
|
664
|
+
local attributes = implParent:GetAttributes()
|
|
665
|
+
local children = {}
|
|
666
|
+
for _, item in pairs(implParent:GetChildren()) do
|
|
667
|
+
children[item.Name] = item
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
for memberName, member in pairs(self._memberMap) do
|
|
671
|
+
if not member:IsRequiredForInterface(tieRealm) then
|
|
672
|
+
continue
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
local found = children[memberName]
|
|
676
|
+
if not found then
|
|
677
|
+
if member.ClassName == "TiePropertyDefinition" then
|
|
678
|
+
if attributes[memberName] == nil then
|
|
679
|
+
return false
|
|
680
|
+
else
|
|
681
|
+
continue
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
return false
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
if member.ClassName == "TieMethodDefinition" then
|
|
689
|
+
if not found:IsA("BindableFunction") then
|
|
690
|
+
return false
|
|
691
|
+
end
|
|
692
|
+
elseif member.ClassName == "TieSignalDefinition" then
|
|
693
|
+
if not found:IsA("BindableEvent") then
|
|
694
|
+
return false
|
|
695
|
+
end
|
|
696
|
+
elseif member.ClassName == "TiePropertyDefinition" then
|
|
697
|
+
if not (found:IsA("BindableFunction") or String.endsWith(found.ClassName, "Value")) then
|
|
698
|
+
return false
|
|
699
|
+
end
|
|
700
|
+
else
|
|
701
|
+
error("[TieDefinition.IsImplementation] - Unknown member type")
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
return true
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
return TieDefinition
|