Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NormalizedStorage] Multiple query #429

Merged
merged 3 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Sources/VergeNormalization/NormalizedStorageType.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

public protocol NormalizedStorageType: Equatable {
public protocol NormalizedStorageType: Equatable, Sendable {

/**
Performs any additional operations for updating.
Expand All @@ -13,6 +13,12 @@ extension NormalizedStorageType {
public func finalizeTransaction(transaction: inout ModifyingTransaction<Self>) {

}

public func table<Selector: TableSelector>(
_ selector: consuming Selector
) -> Selector.Table where Selector.Storage == Self {
selector.select(storage: self)
}
}

extension NormalizedStorageType {
Expand Down
4 changes: 2 additions & 2 deletions Sources/VergeNormalization/Selector.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

public protocol TableSelector: Hashable {
public protocol TableSelector: Hashable, Sendable {
associatedtype Storage: NormalizedStorageType
associatedtype Table: TableType
func select(storage: consuming Storage) -> Table
Expand Down Expand Up @@ -34,7 +34,7 @@ extension TableSelector {
}
}

public protocol StorageSelector: Hashable {
public protocol StorageSelector: Hashable, Sendable {
associatedtype Source: Equatable
associatedtype Storage: NormalizedStorageType

Expand Down
68 changes: 68 additions & 0 deletions Sources/VergeNormalizationDerived/Query.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

extension NormalizedStoragePath {

/**
Make a new Derived of a composed object from the storage.
This is an effective way to resolving relationship entities into a single object. it's like SQLite's view.

```
store.normalizedStorage(.keyPath(\.db)).derived {
MyComposed(
book: $0.book.find(...)
author: $0.author.find(...)
)
}
```

This Derived makes a new composed object if the storage has updated.
There is not filters for entity tables so that Derived possibly makes a new object if not related entity has updated.
*/
public func derived<Composed: Equatable>(query: @escaping @Sendable (Self.Storage) -> Composed) -> Derived<Composed> {
return store.derived(Pipeline(storageSelector: storageSelector, query: query), queue: .passthrough)
}
}
Comment on lines +20 to +23
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new


private struct Pipeline<
_StorageSelector: StorageSelector,
Output
>: PipelineType, Sendable {

typealias Input = Changes<_StorageSelector.Source>
typealias Storage = _StorageSelector.Storage

private let storageSelector: _StorageSelector
private let query: @Sendable (Storage) -> Output

init(
storageSelector: consuming _StorageSelector,
query: @escaping @Sendable (Storage) -> Output
) {
self.storageSelector = storageSelector
self.query = query
}

func yield(_ input: consuming Input) -> Output {

let storage = storageSelector.select(source: input.primitive)
let output = query(storage)

return output

}

func yieldContinuously(_ input: Input) -> Verge.ContinuousResult<Output> {

guard let previous = input.previous else {
return .new(yield(input))
}

// check if the storage has been updated
if NormalizedStorageComparisons<Storage>.StorageComparison()(storageSelector.select(source: input.primitive), storageSelector.select(source: previous.primitive)) {
return .noUpdates
}

return .new(yield(input))

}

}
56 changes: 56 additions & 0 deletions Tests/VergeNormalizationDerivedTests/CombiningTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import XCTest
import VergeNormalizationDerived

final class CombiningTests: XCTestCase {

func test_combine() {

let store = Store<DemoState, Never>(
initialState: .init()
)

struct View: Equatable {
var book: Book?
var author: Author?
}

let derived = store.normalizedStorage(.keyPath(\.db)).derived { storage in
View(
book: storage.book.find(by: .init("1")),
author: storage.author.find(by: .init("1"))
)
}

let exp = expectation(description: "call")
exp.expectedFulfillmentCount = 3

let sub = derived.sinkState { view in
exp.fulfill()
}

store.commit {
$0.db.performBatchUpdates { t in
t.modifying.book.insert(Book(rawID: "1", authorID: .init("1")))
t.modifying.author.insert(Author(rawID: "1", name: "Hiroshi"))
}
}

// no affects
store.commit {
$0.count += 1
}

store.commit {
$0.db.performBatchUpdates { t in
t.modifying.author.insert(Author(rawID: "1", name: "Hiroshi Kimura"))
}
}

wait(for: [exp])

XCTAssertEqual(derived.state.author?.name, "Hiroshi Kimura")

_ = sub
}
}