Skip to content

Commit 534112b

Browse files
authored
tech(alias): Create a container to handle aliased values (#58)
1 parent d2ab63e commit 534112b

File tree

12 files changed

+261
-340
lines changed

12 files changed

+261
-340
lines changed

Sources/CohesionKit/Identity/IdentityStore.swift

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@ public class IdentityMap {
4848

4949
let node = nodeStore(entity: entity, modifiedAt: modifiedAt)
5050

51-
if let alias = named {
52-
refAliases.insert(node, key: alias)
53-
logger?.didRegisterAlias(alias)
51+
if let key = named {
52+
storeAlias(content: entity, key: key, modifiedAt: modifiedAt)
5453
}
5554

5655
return EntityObserver(node: node, registry: registry)
@@ -80,9 +79,8 @@ public class IdentityMap {
8079

8180
let node = nodeStore(entity: entity, modifiedAt: modifiedAt)
8281

83-
if let alias = named {
84-
refAliases.insert(node, key: alias)
85-
logger?.didRegisterAlias(alias)
82+
if let key = named {
83+
storeAlias(content: entity, key: key, modifiedAt: modifiedAt)
8684
}
8785

8886
return EntityObserver(node: node, registry: registry)
@@ -95,9 +93,8 @@ public class IdentityMap {
9593
transaction {
9694
let nodes = entities.map { nodeStore(entity: $0, modifiedAt: modifiedAt) }
9795

98-
if let alias = named {
99-
refAliases.insert(nodes, key: alias)
100-
logger?.didRegisterAlias(alias)
96+
if let key = named {
97+
storeAlias(content: entities, key: key, modifiedAt: modifiedAt)
10198
}
10299

103100
return EntityObserver(nodes: nodes, registry: registry)
@@ -110,9 +107,8 @@ public class IdentityMap {
110107
transaction {
111108
let nodes = entities.map { nodeStore(entity: $0, modifiedAt: modifiedAt) }
112109

113-
if let alias = named {
114-
refAliases.insert(nodes, key: alias)
115-
logger?.didRegisterAlias(alias)
110+
if let key = named {
111+
storeAlias(content: entities, key: key, modifiedAt: modifiedAt)
116112
}
117113

118114
return EntityObserver(nodes: nodes, registry: registry)
@@ -135,17 +131,19 @@ public class IdentityMap {
135131

136132
/// Try to find an entity/aggregate registered under `named` alias
137133
/// - Parameter named: the alias to look for
138-
public func find<T: Identifiable>(named: AliasKey<T>) -> AliasObserver<T> {
134+
public func find<T: Identifiable>(named: AliasKey<T>) -> EntityObserver<T?> {
139135
identityQueue.sync {
140-
AliasObserver(alias: refAliases[named], registry: registry)
136+
let node = refAliases[safe: named]
137+
return EntityObserver(alias: node, registry: registry)
141138
}
142139
}
143140

144141
/// Try to find a collected registered under `named` alias
145142
/// - Returns: an observer returning the alias value. Note that the value will be an Array
146-
public func find<C: Collection>(named: AliasKey<C>) -> AliasObserver<[C.Element]> {
143+
public func find<C: Collection>(named: AliasKey<C>) -> EntityObserver<C?> {
147144
identityQueue.sync {
148-
AliasObserver(alias: refAliases[named], registry: registry)
145+
let node = refAliases[safe: named]
146+
return EntityObserver(alias: node, registry: registry)
149147
}
150148
}
151149

@@ -196,6 +194,20 @@ public class IdentityMap {
196194
return node
197195
}
198196

197+
private func storeAlias<T>(content: T, key: AliasKey<T>, modifiedAt: Stamp?) {
198+
let aliasNode = refAliases[safe: key]
199+
200+
do {
201+
try aliasNode.updateEntity(AliasContainer(key: key, content: content), modifiedAt: modifiedAt)
202+
203+
registry.enqueueChange(for: aliasNode)
204+
logger?.didRegisterAlias(key)
205+
}
206+
catch {
207+
208+
}
209+
}
210+
199211
private func transaction<T>(_ body: () -> T) -> T {
200212
identityQueue.sync(flags: .barrier) {
201213
let returnValue = body()
@@ -260,16 +272,15 @@ extension IdentityMap {
260272
@discardableResult
261273
public func update<T: Identifiable>(named: AliasKey<T>, modifiedAt: Stamp? = nil, update: Update<T>) -> Bool {
262274
transaction {
263-
guard let entity = refAliases[named].value else {
275+
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
264276
return false
265277
}
266278

267-
var value = entity.ref.value
268-
update(&value)
269-
let node = nodeStore(entity: value, modifiedAt: modifiedAt)
279+
update(&content)
270280

271-
// ref might have changed
272-
refAliases.insert(node, key: named)
281+
_ = nodeStore(entity: content, modifiedAt: modifiedAt)
282+
283+
storeAlias(content: content, key: named, modifiedAt: modifiedAt)
273284

274285
return true
275286
}
@@ -282,16 +293,15 @@ extension IdentityMap {
282293
@discardableResult
283294
public func update<T: Aggregate>(named: AliasKey<T>, modifiedAt: Stamp? = nil, update: Update<T>) -> Bool {
284295
transaction {
285-
guard let entity = refAliases[named].value else {
296+
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
286297
return false
287298
}
288299

289-
var value = entity.ref.value
290-
update(&value)
291-
let node = nodeStore(entity: value, modifiedAt: modifiedAt)
300+
update(&content)
301+
302+
_ = nodeStore(entity: content, modifiedAt: modifiedAt)
292303

293-
// ref might have changed
294-
refAliases.insert(node, key: named)
304+
storeAlias(content: content, key: named, modifiedAt: modifiedAt)
295305

296306
return true
297307
}
@@ -302,20 +312,18 @@ extension IdentityMap {
302312
/// the change was applied
303313
/// - Returns: true if entity exists and might be updated, false otherwise. The update might **not** be applied if modifiedAt is too old
304314
@discardableResult
305-
public func update<C: Collection>(named: AliasKey<C>, modifiedAt: Stamp? = nil, update: Update<[C.Element]>)
315+
public func update<C: Collection>(named: AliasKey<C>, modifiedAt: Stamp? = nil, update: Update<C>)
306316
-> Bool where C.Element: Identifiable {
307317
transaction {
308-
guard let entities = refAliases[named].value else {
318+
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
309319
return false
310320
}
311321

312-
var values = entities.map(\.ref.value)
313-
update(&values)
322+
update(&content)
314323

315-
let nodes = values.map { nodeStore(entity: $0, modifiedAt: modifiedAt) }
324+
_ = content.map { nodeStore(entity: $0, modifiedAt: modifiedAt) }
316325

317-
// update alias because `update` may have added/removed entities
318-
refAliases.insert(nodes, key: named)
326+
storeAlias(content: content, key: named, modifiedAt: modifiedAt)
319327

320328
return true
321329
}
@@ -326,20 +334,18 @@ extension IdentityMap {
326334
/// the change was applied
327335
/// - Returns: true if entity exists and might be updated, false otherwise. The update might **not** be applied if modifiedAt is too old
328336
@discardableResult
329-
public func update<C: Collection>(named: AliasKey<C>, modifiedAt: Stamp? = nil, update: Update<[C.Element]>)
337+
public func update<C: Collection>(named: AliasKey<C>, modifiedAt: Stamp? = nil, update: Update<C>)
330338
-> Bool where C.Element: Aggregate {
331339
transaction {
332-
guard let entities = refAliases[named].value else {
340+
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
333341
return false
334342
}
335343

336-
var values = entities.map(\.ref.value)
337-
update(&values)
344+
update(&content)
338345

339-
let nodes = values.map { nodeStore(entity: $0, modifiedAt: modifiedAt) }
346+
_ = content.map { nodeStore(entity: $0, modifiedAt: modifiedAt) }
340347

341-
// update alias because `update` may have added/removed entities
342-
refAliases.insert(nodes, key: named)
348+
storeAlias(content: content, key: named, modifiedAt: modifiedAt)
343349

344350
return true
345351
}
@@ -351,13 +357,13 @@ extension IdentityMap {
351357
extension IdentityMap {
352358
/// Removes an alias from the storage
353359
public func removeAlias<T>(named: AliasKey<T>) {
354-
refAliases.remove(for: named)
360+
refAliases[named] = nil
355361
logger?.didUnregisterAlias(named)
356362
}
357363

358364
/// Removes an alias from the storage
359365
public func removeAlias<C: Collection>(named: AliasKey<C>) {
360-
refAliases.remove(for: named)
366+
refAliases[named] = nil
361367
logger?.didUnregisterAlias(named)
362368
}
363369

Sources/CohesionKit/KeyPath/PartialIdentifiableKeyPath.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ public struct PartialIdentifiableKeyPath<Root> {
5858
}
5959
}
6060

61+
public init<C: MutableCollection>(_ keyPath: WritableKeyPath<Root, C?>) where C.Element: Identifiable, C.Index: Hashable {
62+
self.keyPath = keyPath
63+
self.accept = { parent, root, stamp, visitor in
64+
if let entities = root[keyPath: keyPath] {
65+
visitor.visit(
66+
context: EntityContext(parent: parent, keyPath: keyPath.unwrapped(), stamp: stamp),
67+
entities: entities
68+
)
69+
}
70+
}
71+
}
72+
6173
public init<C: MutableCollection>(_ keyPath: WritableKeyPath<Root, C>) where C.Element: Aggregate, C.Index: Hashable {
6274
self.keyPath = keyPath
6375
self.accept = { parent, root, stamp, visitor in
@@ -68,6 +80,18 @@ public struct PartialIdentifiableKeyPath<Root> {
6880
}
6981
}
7082

83+
public init<C: MutableCollection>(_ keyPath: WritableKeyPath<Root, C?>) where C.Element: Aggregate, C.Index: Hashable {
84+
self.keyPath = keyPath
85+
self.accept = { parent, root, stamp, visitor in
86+
if let entities = root[keyPath: keyPath] {
87+
visitor.visit(
88+
context: EntityContext(parent: parent, keyPath: keyPath.unwrapped(), stamp: stamp),
89+
entities: entities
90+
)
91+
}
92+
}
93+
}
94+
7195
public init<W: EntityEnumWrapper>(wrapper keyPath: WritableKeyPath<Root, W>) {
7296
self.keyPath = keyPath
7397
self.accept = { parent, root, stamp, visitor in

Sources/CohesionKit/Observer/AliasObserver.swift

Lines changed: 0 additions & 85 deletions
This file was deleted.

Sources/CohesionKit/Observer/EntityObserver.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ public struct EntityObserver<T>: Observer {
2222
}
2323
}
2424

25+
init<Wrapped>(alias node: EntityNode<AliasContainer<Wrapped>>, registry: ObserverRegistry)
26+
where T == Optional<Wrapped> {
27+
self.init(value: node.ref.value.content) { onChange in
28+
registry.addObserver(node: node, initial: true, onChange: { container in
29+
onChange(container.content)
30+
})
31+
}
32+
}
33+
34+
init(value: T, createObserver: @escaping (@escaping OnChange) -> Subscription) {
35+
self.value = value
36+
self.createObserver = createObserver
37+
}
38+
2539
public func observe(onChange: @escaping OnChange) -> Subscription {
2640
createObserver(onChange)
2741
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
/// a container to store an aliased object
3+
struct AliasContainer<T>: Identifiable, Aggregate {
4+
var id: String { key.name }
5+
6+
let key: AliasKey<T>
7+
8+
var content: T?
9+
}
10+
11+
extension AliasContainer where T: Aggregate {
12+
var nestedEntitiesKeyPaths: [PartialIdentifiableKeyPath<AliasContainer<T>>] {
13+
[.init(\.content)]
14+
}
15+
}
16+
17+
extension AliasContainer where T: Identifiable {
18+
var nestedEntitiesKeyPaths: [PartialIdentifiableKeyPath<AliasContainer<T>>] {
19+
[.init(\.content)]
20+
}
21+
}
22+
23+
extension AliasContainer where T: MutableCollection, T.Element: Aggregate, T.Index: Hashable {
24+
var nestedEntitiesKeyPaths: [PartialIdentifiableKeyPath<AliasContainer<T>>] {
25+
[.init(\.content)]
26+
}
27+
}
28+
29+
extension AliasContainer where T: MutableCollection, T.Element: Identifiable, T.Index: Hashable {
30+
var nestedEntitiesKeyPaths: [PartialIdentifiableKeyPath<AliasContainer<T>>] {
31+
[.init(\.content)]
32+
}
33+
}
34+
35+
extension AliasContainer {
36+
var nestedEntitiesKeyPaths: [PartialIdentifiableKeyPath<AliasContainer<T>>] {
37+
[]
38+
}
39+
}

0 commit comments

Comments
 (0)