diff --git a/Effector/Domain.swift b/Effector/Domain.swift index ce93304..610caf8 100644 --- a/Effector/Domain.swift +++ b/Effector/Domain.swift @@ -1,27 +1,51 @@ -class Domain { +public class Domain { // MARK: Lifecycle - init(name: String, parent: Domain?) { + init(_ name: String, domain: Domain? = nil) { self.name = name - self.parent = parent + self.parent = domain self.graphite = Node(name: name, kind: .domain, priority: .child) + + if let domain { + forward(from: eventCreated, to: domain.eventCreated) + forward(from: storeCreated, to: domain.storeCreated) + forward(from: domainCreated, to: domain.domainCreated) + } + + eventCreated.watch { unit in + if let onCreate = self.onCreateEvent { + onCreate(unit) + } + } + + storeCreated.watch { unit in + if let onCreate = self.onCreateStore { + onCreate(unit) + } + } + + domainCreated.watch { unit in + if let onCreate = self.onCreateDomain { + onCreate(unit) + } + } } // MARK: Public public var graphite: Node public let name: String - public var onCreateEvent: ((_ event: AnyEvent) -> Subscription)? - public var onCreateStore: ((_ store: Store) -> Subscription)? - public var onCreateEffect: ((_ effect: Effect) -> Subscription)? - public var onCreateDomain: ((_ domain: Domain) -> Subscription)? - public let parent: Domain? + public let eventCreated = Event() + public let storeCreated = Event() + public let domainCreated = Event() - public var kind: String { "domain" } + public var onCreateEvent: ((_ event: AnyEvent) -> Void)? + public var onCreateStore: ((_ store: AnyStore) -> Void)? + public var onCreateDomain: ((_ domain: Domain) -> Void)? - // MARK: Internal + public let parent: Domain? - let events = [AnyEvent]() + public var kind: String { "domain" } } diff --git a/Effector/Effect.swift b/Effector/Effect.swift index d0f0f76..c01398c 100644 --- a/Effector/Effect.swift +++ b/Effector/Effect.swift @@ -1,3 +1,5 @@ +public typealias AnyEffect = Effect + enum Tracing { @TaskLocal static var id: Int = next() @@ -9,7 +11,7 @@ public final class Effect: Unit { // MARK: Lifecycle // swiftlint:disable:next function_body_length - public init(name: String = "effect", isDerived: Bool = false, _ handler: @escaping Handler) { + public init(name: String = "effect", isDerived: Bool = false, domain: Domain? = nil, _ handler: @escaping Handler) { self.name = name self.isDerived = isDerived graphite = Node(name: name, kind: .effect, priority: .effect) diff --git a/Effector/Event.swift b/Effector/Event.swift index 6f9fb33..f726ce7 100644 --- a/Effector/Event.swift +++ b/Effector/Event.swift @@ -3,10 +3,14 @@ public typealias AnyEvent = Event public final class Event: Unit { // MARK: Lifecycle - public init(name: String = "event", isDerived: Bool = false) { + public init(name: String = "event", isDerived: Bool = false, domain: Domain? = nil) { self.name = name graphite = Node(name: name, kind: .event, priority: .child) self.isDerived = isDerived + + if let domain { + domain.eventCreated(cast()) + } } // MARK: Public @@ -113,12 +117,16 @@ public final class Event: Unit { return mapped } - public func erase() -> AnyEvent { - let event = Event(name: name, isDerived: isDerived) + public func cast() -> Event { + let event = Event(name: name, isDerived: isDerived) event.graphite = graphite return event } + + public func erase() -> AnyEvent { + cast() + } } public extension Event where Payload == Void { diff --git a/Effector/Node.swift b/Effector/Node.swift index a5cbd30..e5e4e9d 100644 --- a/Effector/Node.swift +++ b/Effector/Node.swift @@ -6,8 +6,7 @@ public final class Node { kind: Kind, priority: PriorityTag, next: [Node] = [], - seq: [Step] = [], - family: Family = Family(type: .regular, links: [], owners: []) + seq: [Step] = [] ) { id = Node.nextID() self.name = name @@ -15,21 +14,6 @@ public final class Node { self.priority = priority self.next = next self.seq = seq - self.family = family - } - - // MARK: Public - - public struct Family { - enum FamilyType { - case regular - case crosslink - case domain - } - - var type: FamilyType - var links: [Node] - var owners: [Node] } // MARK: Internal @@ -59,7 +43,6 @@ public final class Node { let name: String let kind: Kind let priority: PriorityTag - let family: Family private(set) var next: [Node] var seq: [Step] diff --git a/Effector/Store.swift b/Effector/Store.swift index 2c10b98..e7653df 100644 --- a/Effector/Store.swift +++ b/Effector/Store.swift @@ -5,7 +5,7 @@ public typealias AnyStore = Store public final class Store: Unit, ObservableObject { // MARK: Lifecycle - public init(name: String = "store", _ defaultState: State, isDerived: Bool = false) { + public init(name: String = "store", _ defaultState: State, isDerived: Bool = false, domain: Domain? = nil) { self.name = name self.defaultState = defaultState currentState = defaultState @@ -41,6 +41,10 @@ public final class Store: Unit, ObservableObject { if !isDerived { reset(reinit) } + + if let domain = domain { + domain.storeCreated(cast()) + } } // MARK: Public @@ -145,20 +149,24 @@ public final class Store: Unit, ObservableObject { return mapped } - public func erase() -> AnyStore { - let store: AnyStore = Store(name: name, defaultState, isDerived: isDerived) + public func cast() -> Store { + let store = Store(name: name, defaultState as! T, isDerived: isDerived) store.graphite = graphite store.reinit = reinit - store.updates = updates.erase() + store.updates = updates.cast() $currentState - .map { $0 as Any } + .map { $0 as! T } .assign(to: &store.$currentState) return store } + public func erase() -> AnyStore { + cast() + } + // MARK: Private private func onBase(name: String? = nil, _ units: [Unit], _ fn: @escaping (State, Any) -> State) -> Self { diff --git a/EffectorForms/EffectorForm.swift b/EffectorForms/EffectorForm.swift index 1564574..ac9ad26 100644 --- a/EffectorForms/EffectorForm.swift +++ b/EffectorForms/EffectorForm.swift @@ -91,7 +91,7 @@ public final class EffectorForm { isValidFlags.append(field.isValid) isDirtyFlags.append(field.isDirty) isTouchedFlags.append(field.isTouched) - valuesStores.append(field.value.erase()) + valuesStores.append(field.value.cast()) bindChangeEvent( field: field, diff --git a/Tests/EffectorTests/Contains.swift b/Tests/EffectorTests/ContainsTests.swift similarity index 100% rename from Tests/EffectorTests/Contains.swift rename to Tests/EffectorTests/ContainsTests.swift diff --git a/Tests/EffectorTests/DomainTests.swift b/Tests/EffectorTests/DomainTests.swift new file mode 100644 index 0000000..8eaec38 --- /dev/null +++ b/Tests/EffectorTests/DomainTests.swift @@ -0,0 +1,67 @@ +@testable import Effector +import XCTest + +final class DomainTests: XCTestCase { + func testEventWatch() async throws { + let domain = Domain("app") + + var log = [Int]() + + domain.onCreateEvent = { event in + event.watch { value in + log.append(value as! Int) + } + } + + let event = Event(domain: domain) + + event(1) + event(2) + + XCTAssertEqual(log, [1, 2]) + } + + func testStoreReset() async throws { + let domain = Domain("app") + + let reset = Event() + + domain.onCreateStore = { store in + store.reset(reset) + } + + let storeA = Store(0, domain: domain) + let storeB = Store(0, domain: domain) + + storeA.setState(1) + storeB.setState(1) + + XCTAssertEqual(storeA.getState(), 1) + XCTAssertEqual(storeB.getState(), 1) + + reset() + + XCTAssertEqual(storeA.getState(), 0) + XCTAssertEqual(storeB.getState(), 0) + } + + func testSubdomain() async throws { + let app = Domain("app") + let domain = Domain("sub", domain: app) + + var log = [Int]() + + app.onCreateEvent = { event in + event.watch { value in + log.append(value as! Int) + } + } + + let event = Event(domain: domain) + + event(1) + event(2) + + XCTAssertEqual(log, [1, 2]) + } +} diff --git a/Tests/EffectorTests/EventTests.swift b/Tests/EffectorTests/EventTests.swift index db81e77..e4e0e42 100644 --- a/Tests/EffectorTests/EventTests.swift +++ b/Tests/EffectorTests/EventTests.swift @@ -93,7 +93,7 @@ final class EventTests: XCTestCase { func testErase() async throws { let event = Event() - let erased: AnyEvent = event.erase() + let erased: AnyEvent = event.cast() XCTAssertEqual(ObjectIdentifier(event.graphite), ObjectIdentifier(erased.graphite)) } diff --git a/Tests/EffectorTests/StoreTests.swift b/Tests/EffectorTests/StoreTests.swift index f42a5fb..ae00cd6 100644 --- a/Tests/EffectorTests/StoreTests.swift +++ b/Tests/EffectorTests/StoreTests.swift @@ -96,21 +96,23 @@ final class StoreTests: XCTestCase { func testStoreEraseShareTheSameNode() async throws { let store = Store(0) - let erased = store.erase() + let erased: Store = store.cast() XCTAssertEqual(ObjectIdentifier(store.graphite), ObjectIdentifier(erased.graphite)) } func testStoreErase() async throws { - let store = Store(0).debug() - let erased = store.erase().debug() + let store = Store(0) + let erased: Store = store.cast() - let event = Event().debug() + let event = Event() store.on(event) { _, value in value } event(1) - XCTAssertEqual(erased.getState() as! Int, 1) + XCTAssertEqual(erased.getState(), 1) } + + }