From 1b26d87709681ce80e15028edc2f239aea3e220e Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Sun, 4 Oct 2020 16:54:07 +0200 Subject: [PATCH 1/9] x --- packages/core/src/event/index.ts | 1 - packages/core/src/runtime/index.ts | 121 +++++------------------- packages/core/src/runtime/job.ts | 30 ++++++ packages/core/src/runtime/observable.ts | 3 + packages/core/src/runtime/worker.ts | 26 +++++ packages/core/src/state/index.ts | 3 +- packages/core/src/state/state.worker.ts | 112 ++++++++++++++++++++++ 7 files changed, 195 insertions(+), 101 deletions(-) create mode 100644 packages/core/src/runtime/job.ts create mode 100644 packages/core/src/runtime/observable.ts create mode 100644 packages/core/src/runtime/worker.ts create mode 100644 packages/core/src/state/state.worker.ts diff --git a/packages/core/src/event/index.ts b/packages/core/src/event/index.ts index 1a4435ea..53a7e502 100644 --- a/packages/core/src/event/index.ts +++ b/packages/core/src/event/index.ts @@ -195,5 +195,4 @@ export class Event { return; } - } diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 13d82622..f69a1c84 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -1,23 +1,15 @@ import { - State, Agile, Computed, SubscriptionContainer, copy, defineConfig } from '../internal'; -import {ComponentSubscriptionContainer} from "./subscription/ComponentSubscriptionContainer"; import {CallbackSubscriptionContainer} from "./subscription/CallbackSubscriptionContainer"; +import {Worker} from "./worker"; +import {Job} from "./job"; +import {Observable} from "./observable"; -export interface JobInterface { - state: State - newStateValue?: any - options?: { - background?: boolean - sideEffects?: boolean - forceRerender?: boolean - } -} export interface JobConfigInterface { perform?: boolean // Should preform the job instantly @@ -30,30 +22,19 @@ export class Runtime { public agileInstance: () => Agile; // Queue system - private currentJob: JobInterface | null = null; - private jobQueue: Array = []; - private jobsToRerender: Array = []; + private currentJob: Job | null = null; + private jobQueue: Array = []; + private jobsToRerender: Array = []; // Used for tracking computed dependencies public trackState: boolean = false; // Check if agile should track states - public foundStates: Set = new Set(); // States which were tracked during the track time - - public internalIngestKey = "This is an Internal Key for ingesting internal stuff!"; + public foundStates: Set = new Set(); // States which were tracked during the track time constructor(agileInstance: Agile) { this.agileInstance = () => agileInstance; } - - //========================================================================================================= - // Ingest - //========================================================================================================= - /** - * @internal - * Creates a Job out of State and new Value and than add it to a job queue - * Note: its not possible to set a state to undefined because undefined is used for internal activities! - */ - public ingest(state: State, newStateValue?: any, options: JobConfigInterface = {}): void { + public ingest(worker: Worker, options: JobConfigInterface): void { // Merge default values into options options = defineConfig(options, { perform: true, @@ -63,34 +44,15 @@ export class Runtime { }); // Create Job - const job: JobInterface = { - state: state, - newStateValue: newStateValue, - options: { - background: options.background, - sideEffects: options.sideEffects, - forceRerender: options.forceRerender - } - }; - - // Grab nextState if newState not passed or compute if needed - if (newStateValue === this.internalIngestKey) { - if (job.state instanceof Computed) - job.newStateValue = job.state.computeValue(); - else - job.newStateValue = job.state.nextState - } - - // Check if state value und newStateValue are the same.. if so return except force Rerender (stringifying because of possible object or array) - if (JSON.stringify(state.value) === JSON.stringify(job.newStateValue) && !options.forceRerender) { - if (this.agileInstance().config.logJobs) - console.warn("Agile: Doesn't perform job because state values are the same! ", job); - return; - } + const job = new Job(worker, { + background: options.background, + sideEffects: options.sideEffects, + forceRerender: options.forceRerender + }); // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Created Job(${job.state.key})`, job); + console.log(`Agile: Created Job(${job.worker.key})`, job); // Push the Job to the Queue (safety.. that no Job get forgotten) this.jobQueue.push(job); @@ -113,28 +75,16 @@ export class Runtime { * @internal * Perform a State Update */ - private perform(job: JobInterface): void { + private perform(job: Job): void { // Set Job to currentJob this.currentJob = job; - // Set Previous State - job.state.previousState = copy(job.state.value); + // Perform Job + job.worker.perform(job); + job.performed = true; - // Write new value into the State - job.state.privateWrite(job.newStateValue); - - // Set isSet - job.state.isSet = job.newStateValue !== job.state.initialState; - - // Set is placeholder to false, because it has got a value - if (job.state.isPlaceholder) - job.state.isPlaceholder = false; - - // Perform SideEffects like watcher functions or state.sideEffects - this.sideEffects(job); - - // Set Job as completed (The deps and subs of completed jobs will be updated) - if (!job.options?.background || job.options?.forceRerender) + // Add to rerender + if (job.rerender) this.jobsToRerender.push(job); // Reset Current Job @@ -142,49 +92,23 @@ export class Runtime { // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Completed Job(${job.state.key})`, job); + console.log(`Agile: Completed Job(${job.worker.key})`, job); // Continue the Loop and perform the next job.. if no job is left update the Subscribers for each completed job if (this.jobQueue.length > 0) { const performJob = this.jobQueue.shift(); if (performJob) this.perform(performJob); - else - console.warn("Agile: Failed to perform Job ", job); } else { // https://stackoverflow.com/questions/9083594/call-settimeout-without-delay setTimeout(() => { // Cause rerender on Subscribers this.updateSubscribers(); - }) + }); } } - //========================================================================================================= - // Side Effect - //========================================================================================================= - /** - * @internal - * SideEffects are sideEffects of the perform function.. for instance the watchers - */ - private sideEffects(job: JobInterface) { - const state = job.state; - - // Call Watchers - for (let watcher in state.watchers) - if (typeof state.watchers[watcher] === 'function') - state.watchers[watcher](state.getPublicValue()); - - // Call State SideEffects - if (typeof state.sideEffects === 'function' && job.options?.sideEffects) - state.sideEffects(); - - // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) - state.dep.deps.forEach((state) => this.ingest(state, this.internalIngestKey, {perform: false})); - } - - //========================================================================================================= // Update Subscribers //========================================================================================================= @@ -196,7 +120,6 @@ export class Runtime { // Check if Agile has an integration because its useless to go trough this process without framework // It won't happen anything because the state has no subs.. but this check here will maybe improve the performance if (!this.agileInstance().integrations.hasIntegration()) { - this.jobsToRerender = []; return; } diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts new file mode 100644 index 00000000..7fa53ff4 --- /dev/null +++ b/packages/core/src/runtime/job.ts @@ -0,0 +1,30 @@ +import {Worker} from "./worker"; +import {defineConfig} from "../utils"; + +export interface JobConfigInterface { + background?: boolean + sideEffects?: boolean + forceRerender?: boolean +} + +export class Job { + + public worker: w; + public config: JobConfigInterface; + public rerender: boolean; + public performed: boolean = false; + + constructor(worker: w, config: JobConfigInterface) { + // Merge default values into options + this.config = defineConfig(config, { + background: false, + sideEffects: true, + forceRerender: false + }); + + this.worker = worker; + this.config = config; + this.rerender = !config?.background || config?.forceRerender || true; + } + +} \ No newline at end of file diff --git a/packages/core/src/runtime/observable.ts b/packages/core/src/runtime/observable.ts new file mode 100644 index 00000000..98dfa439 --- /dev/null +++ b/packages/core/src/runtime/observable.ts @@ -0,0 +1,3 @@ +export class Observable { + +} \ No newline at end of file diff --git a/packages/core/src/runtime/worker.ts b/packages/core/src/runtime/worker.ts new file mode 100644 index 00000000..4dbffc51 --- /dev/null +++ b/packages/core/src/runtime/worker.ts @@ -0,0 +1,26 @@ +import {Agile, StateKey} from "../internal"; +import {Job} from "./job"; + +export type WorkerKey = string | number; + +export class Worker { + + public agileInstance: () => Agile; + public _key?: WorkerKey; + + constructor(agileInstance: Agile) { + this.agileInstance = () => agileInstance; + } + + public set key(value: StateKey | undefined) { + this._key = value; + } + + public get key(): StateKey | undefined { + return this._key; + } + + public perform(job: Job) { + console.warn("Didn't set perform function in Worker ", this.key); + } +} \ No newline at end of file diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index cf0d860d..8ccdbb24 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -8,6 +8,7 @@ import { isValidObject } from '../internal'; import {persistValue, updateValue} from './persist'; +import {Observable} from "../runtime/observable"; export type StateKey = string | number; @@ -17,7 +18,7 @@ export interface PersistSettingsInterface { persistKey?: string | number // Current Persist Key.. for handling twice persisted states } -export class State { +export class State extends Observable{ public agileInstance: () => Agile; public _key?: StateKey; // should be a unique key/name which identifies the state diff --git a/packages/core/src/state/state.worker.ts b/packages/core/src/state/state.worker.ts new file mode 100644 index 00000000..3de0280c --- /dev/null +++ b/packages/core/src/state/state.worker.ts @@ -0,0 +1,112 @@ +import {Agile} from "../agile"; +import {Worker} from "../runtime/worker"; +import {State} from "./index"; +import {copy, defineConfig} from "../utils"; +import {Computed} from "../computed"; +import {JobConfigInterface} from "../runtime"; +import {Job} from "../runtime/job"; + + +export class StateWorker extends Worker { + + public nextStateValue: ValueType; + public state: State; + public internalIngestKey = "This is an Internal Key for ingesting internal stuff!"; + + constructor(agileInstance: Agile, state: State) { + super(agileInstance); + this.state = state; + this.nextStateValue = state.value; + this.key = state.key; + } + + + //========================================================================================================= + // Ingest + //========================================================================================================= + /** + * @internal + * Creates a Job out of State and new Value and than add it to a job queue + * Note: its not possible to set a state to undefined because undefined is used for internal activities! + */ + public ingest(state: State, newStateValue?: any, options: JobConfigInterface = {}): void { + // Merge default values into options + options = defineConfig(options, { + perform: true, + background: false, + sideEffects: true, + forceRerender: false + }); + + // Grab nextState if newState not passed or compute if needed + if (newStateValue === this.internalIngestKey) { + if (state instanceof Computed) + this.nextStateValue = state.computeValue(); + else + this.nextStateValue = state.nextState + } + this.nextStateValue = newStateValue; + + // Check if state value und newStateValue are the same.. if so return except force Rerender (stringifying because of possible object or array) + if (JSON.stringify(state.value) === JSON.stringify(this.nextStateValue) && !options.forceRerender) { + if (this.agileInstance().config.logJobs) + console.warn("Agile: Doesn't created job because state values are the same! "); + return; + } + + // Ingest into runtime + this.agileInstance().runtime.ingest(this, {}); + } + + + //========================================================================================================= + // Perform + //========================================================================================================= + /** + * @internal + * TOD_O + */ + public perform(job: Job) { + const state = job.worker.state; + + // Set Previous State + state.previousState = copy(state.value); + + // Write new value into the State + state.privateWrite(this.nextStateValue); + + // Set isSet + state.isSet = this.nextStateValue !== state.initialState; + + // Set is placeholder to false, because it has got a value + if (state.isPlaceholder) + state.isPlaceholder = false; + + // Perform SideEffects like watcher functions or state.sideEffects + this.sideEffects(job); + } + + + //========================================================================================================= + // Side Effect + //========================================================================================================= + /** + * @internal + * SideEffects are sideEffects of the perform function.. for instance the watchers + */ + private sideEffects(job: Job) { + const state = job.worker.state; + + // Call Watchers + for (let watcher in state.watchers) + if (typeof state.watchers[watcher] === 'function') + state.watchers[watcher](state.getPublicValue()); + + // Call State SideEffects + if (typeof state.sideEffects === 'function' && job.config?.sideEffects) + state.sideEffects(); + + // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) + state.dep.deps.forEach((state) => this.ingest(state, this.internalIngestKey, {perform: false})); + } +} \ No newline at end of file From 2a21757817d71956105f98c911017a822b1afb51 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Sun, 4 Oct 2020 18:24:17 +0200 Subject: [PATCH 2/9] x --- packages/core/src/internal.ts | 2 +- packages/core/src/{state => runtime}/dep.ts | 14 ++++----- packages/core/src/runtime/index.ts | 26 +++++++--------- packages/core/src/runtime/job.ts | 10 +++--- packages/core/src/runtime/observable.ts | 3 -- packages/core/src/runtime/observer.ts | 30 ++++++++++++++++++ .../CallbackSubscriptionContainer.ts | 4 +-- .../ComponentSubscriptionContainer.ts | 7 +++-- packages/core/src/runtime/subscription/sub.ts | 31 ++++++++++--------- packages/core/src/runtime/worker.ts | 26 ---------------- packages/core/src/state/index.ts | 12 +++---- .../{state.worker.ts => state.observer.ts} | 24 +++++++------- 12 files changed, 95 insertions(+), 94 deletions(-) rename packages/core/src/{state => runtime}/dep.ts (80%) delete mode 100644 packages/core/src/runtime/observable.ts create mode 100644 packages/core/src/runtime/observer.ts delete mode 100644 packages/core/src/runtime/worker.ts rename packages/core/src/state/{state.worker.ts => state.observer.ts} (80%) diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index b6b85bc0..2695b49a 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -10,7 +10,7 @@ export * from './agile'; // State export * from './state'; -export * from './state/dep'; +export * from './runtime/dep'; // Computed export {Computed} from './computed'; diff --git a/packages/core/src/state/dep.ts b/packages/core/src/runtime/dep.ts similarity index 80% rename from packages/core/src/state/dep.ts rename to packages/core/src/runtime/dep.ts index 29abe323..526ce39b 100644 --- a/packages/core/src/state/dep.ts +++ b/packages/core/src/runtime/dep.ts @@ -1,17 +1,17 @@ import { - State, SubscriptionContainer } from '../internal'; +import {Observer} from "./observer"; export class Dep { - public deps: Set = new Set(); // Dependencies from the State + public deps: Set = new Set(); // Dependencies from the State public subs: Set = new Set(); // Subscriptions (for instance a component subscribes to a state to get rerendered if the state changes) - constructor(initialDeps?: Array) { + constructor(initialDeps?: Array) { if (!initialDeps) return; // Add Initial Dependencies to Deps - initialDeps.forEach(dep => this.deps.add(dep)); + initialDeps.forEach(observable => this.deps.add(observable)); } @@ -21,9 +21,9 @@ export class Dep { /** * Add new Dependency to the State */ - public depend(state: State) { - if (state.dep !== this && !this.deps.has(state)) - this.deps.add(state); + public depend(observable: Observer) { + if (observable.dep !== this && !this.deps.has(observable)) + this.deps.add(observable); } diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index f69a1c84..9ecc6a71 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -6,9 +6,8 @@ import { defineConfig } from '../internal'; import {CallbackSubscriptionContainer} from "./subscription/CallbackSubscriptionContainer"; -import {Worker} from "./worker"; +import {Observer} from "./observer"; import {Job} from "./job"; -import {Observable} from "./observable"; export interface JobConfigInterface { @@ -28,13 +27,13 @@ export class Runtime { // Used for tracking computed dependencies public trackState: boolean = false; // Check if agile should track states - public foundStates: Set = new Set(); // States which were tracked during the track time + public foundStates: Set = new Set(); // States which were tracked during the track time constructor(agileInstance: Agile) { this.agileInstance = () => agileInstance; } - public ingest(worker: Worker, options: JobConfigInterface): void { + public ingest(worker: Observer, options: JobConfigInterface): void { // Merge default values into options options = defineConfig(options, { perform: true, @@ -52,7 +51,7 @@ export class Runtime { // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Created Job(${job.worker.key})`, job); + console.log(`Agile: Created Job(${job.observable.key})`, job); // Push the Job to the Queue (safety.. that no Job get forgotten) this.jobQueue.push(job); @@ -80,7 +79,7 @@ export class Runtime { this.currentJob = job; // Perform Job - job.worker.perform(job); + job.observable.perform(job); job.performed = true; // Add to rerender @@ -92,7 +91,7 @@ export class Runtime { // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Completed Job(${job.worker.key})`, job); + console.log(`Agile: Completed Job(${job.observable.key})`, job); // Continue the Loop and perform the next job.. if no job is left update the Subscribers for each completed job if (this.jobQueue.length > 0) { @@ -119,9 +118,8 @@ export class Runtime { private updateSubscribers(): void { // Check if Agile has an integration because its useless to go trough this process without framework // It won't happen anything because the state has no subs.. but this check here will maybe improve the performance - if (!this.agileInstance().integrations.hasIntegration()) { + if (!this.agileInstance().integrations.hasIntegration()) return; - } // Subscriptions that has to be updated (Set = For preventing double subscriptions without further checks) const subscriptionsToUpdate: Set = new Set(); @@ -129,7 +127,7 @@ export class Runtime { // Map through Jobs to Rerender this.jobsToRerender.forEach(job => // Map through subs of the current Job State - job.state.dep.subs.forEach(subscriptionContainer => { + job.observable.dep.subs.forEach(subscriptionContainer => { // Check if subscriptionContainer is ready if (!subscriptionContainer.ready) console.warn("Agile: SubscriptionContainer isn't ready yet ", subscriptionContainer); @@ -139,8 +137,8 @@ export class Runtime { let localKey: string | null = null; // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object - for (let key in subscriptionContainer.propStates) - if (subscriptionContainer.propStates[key] === job.state) + for (let key in subscriptionContainer.propObservable) + if (subscriptionContainer.propObservable[key] === job.observable) localKey = key; // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object @@ -183,8 +181,8 @@ export class Runtime { // Build Object subscriptionContainer.propKeysChanged.forEach(changedKey => { - if (subscriptionContainer.propStates) - finalObject[changedKey] = subscriptionContainer.propStates[changedKey].value; + if (subscriptionContainer.propObservable) + finalObject[changedKey] = subscriptionContainer.propObservable[changedKey].value; }); return finalObject; diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts index 7fa53ff4..5c9ccfbf 100644 --- a/packages/core/src/runtime/job.ts +++ b/packages/core/src/runtime/job.ts @@ -1,4 +1,4 @@ -import {Worker} from "./worker"; +import {Observer} from "./observer"; import {defineConfig} from "../utils"; export interface JobConfigInterface { @@ -7,14 +7,14 @@ export interface JobConfigInterface { forceRerender?: boolean } -export class Job { +export class Job { - public worker: w; + public observable: o; public config: JobConfigInterface; public rerender: boolean; public performed: boolean = false; - constructor(worker: w, config: JobConfigInterface) { + constructor(observable: o, config: JobConfigInterface) { // Merge default values into options this.config = defineConfig(config, { background: false, @@ -22,7 +22,7 @@ export class Job { forceRerender: false }); - this.worker = worker; + this.observable = observable; this.config = config; this.rerender = !config?.background || config?.forceRerender || true; } diff --git a/packages/core/src/runtime/observable.ts b/packages/core/src/runtime/observable.ts deleted file mode 100644 index 98dfa439..00000000 --- a/packages/core/src/runtime/observable.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Observable { - -} \ No newline at end of file diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts new file mode 100644 index 00000000..f14302c2 --- /dev/null +++ b/packages/core/src/runtime/observer.ts @@ -0,0 +1,30 @@ +import {Agile, Dep, StateKey} from "../internal"; +import {Job} from "./job"; + +export type ObservableKey = string | number; + +export class Observer { + + public agileInstance: () => Agile; + + public _key?: ObservableKey; + public dep: Dep; + public value: any; // The current value which will be returned for instance if its a prop based subscription + + constructor(agileInstance: Agile) { + this.agileInstance = () => agileInstance; + this.dep = new Dep(); + } + + public set key(value: StateKey | undefined) { + this._key = value; + } + + public get key(): StateKey | undefined { + return this._key; + } + + public perform(job: Job) { + console.warn("Didn't set perform function in Observer ", this.key); + } +} \ No newline at end of file diff --git a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts index 4f2868bf..55f57d7d 100644 --- a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts @@ -1,10 +1,10 @@ -import {State} from "../../internal"; import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; +import {Observer} from "../observer"; export class CallbackSubscriptionContainer extends ComponentSubscriptionContainer { public callback: Function; - constructor(callback: Function, subs?: Set) { + constructor(callback: Function, subs?: Set) { super(null, subs); this.callback = callback; diff --git a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts index 36e1fc74..da627b38 100644 --- a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts @@ -1,17 +1,18 @@ import {State} from "../../internal"; +import {Observer} from "../observer"; export class ComponentSubscriptionContainer { public component: any; // Only needed by object orientated subscriptions public passProps: boolean = false; - public propStates?: { [key: string]: State }; // states which will than be returned as prop object by the integration + public propObservable?: { [key: string]: Observer }; // states which will than be returned as prop object by the integration public propKeysChanged: Array = []; // Used to preserve local keys to update before update is performed, cleared every update public ready: boolean = false; - public subs: Set = new Set([]); // States that are subscribed by this component + public subs: Set = new Set([]); // States that are subscribed by this component - constructor(component: any, subs?: Set) { + constructor(component: any, subs?: Set) { this.component = component if (subs) this.subs = subs; diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.ts index 7cb8137a..b1a0b827 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.ts @@ -1,6 +1,7 @@ -import {Agile, State} from '../../internal'; +import {Agile} from '../../internal'; import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; import {CallbackSubscriptionContainer} from "./CallbackSubscriptionContainer"; +import {Observer} from "../observer"; //========================================================================================================= @@ -34,26 +35,26 @@ export class SubController { /** * Subscribe to Agile State with a returned object of props this props can than be returned by the component (See react-integration) */ - public subscribeWithSubsObject(subscriptionInstance: any, subs: { [key: string]: State } = {}): { subscriptionContainer: SubscriptionContainer, props: { [key: string]: State['value'] } } { + public subscribeWithSubsObject(subscriptionInstance: any, subs: { [key: string]: Observer } = {}): { subscriptionContainer: SubscriptionContainer, props: { [key: string]: Observer['value'] } } { const subscriptionContainer = this.registerSubscription(subscriptionInstance); - const props: { [key: string]: State } = {}; + const props: { [key: string]: Observer } = {}; subscriptionContainer.passProps = true; - subscriptionContainer.propStates = {...subs}; + subscriptionContainer.propObservable = {...subs}; // Go through subs let localKeys = Object.keys(subs); localKeys.forEach(key => { - const state = subs[key]; + const observable = subs[key]; // Add State to SubscriptionContainer Subs - subscriptionContainer.subs.add(state); + subscriptionContainer.subs.add(observable); // Add SubscriptionContainer to State Subs - state.dep.subscribe(subscriptionContainer); + observable.dep.subscribe(subscriptionContainer); // Add state to props - props[key] = state.value; + props[key] = observable.value; }); return { @@ -69,15 +70,15 @@ export class SubController { /** * Subscribe to Agile State */ - public subscribeWithSubsArray(subscriptionInstance: any, subs: Array = []): SubscriptionContainer { + public subscribeWithSubsArray(subscriptionInstance: any, subs: Array = []): SubscriptionContainer { const subscriptionContainer = this.registerSubscription(subscriptionInstance, subs); - subs.forEach(state => { + subs.forEach(observable => { // Add State to SubscriptionContainer Subs - subscriptionContainer.subs.add(state); + subscriptionContainer.subs.add(observable); // Add SubscriptionContainer to State Dependencies Subs - state.dep.subscribe(subscriptionContainer); + observable.dep.subscribe(subscriptionContainer); }); return subscriptionContainer; @@ -116,7 +117,7 @@ export class SubController { /** * Registers the Component/Callback Subscription and returns a SubscriptionContainer */ - public registerSubscription(integrationInstance: any, subs: Array = []): SubscriptionContainer { + public registerSubscription(integrationInstance: any, subs: Array = []): SubscriptionContainer { if (typeof integrationInstance === 'function') return this.registerCallbackSubscription(integrationInstance, subs); @@ -145,7 +146,7 @@ export class SubController { * Registers Component Subscription * Note: Helper Function */ - private registerComponentSubscription(componentInstance: any, subs: Array = []): ComponentSubscriptionContainer { + private registerComponentSubscription(componentInstance: any, subs: Array = []): ComponentSubscriptionContainer { // Create ComponentSubscriptionContainer const componentContainer = new ComponentSubscriptionContainer(componentInstance, new Set(subs)); @@ -174,7 +175,7 @@ export class SubController { * Registers Callback Subscription * Note: Helper Function */ - private registerCallbackSubscription(callbackFunction: () => void, subs: Array = []): CallbackSubscriptionContainer { + private registerCallbackSubscription(callbackFunction: () => void, subs: Array = []): CallbackSubscriptionContainer { // Create CallbackSubscriptionContainer const callbackContainer = new CallbackSubscriptionContainer(callbackFunction as Function, new Set(subs)); diff --git a/packages/core/src/runtime/worker.ts b/packages/core/src/runtime/worker.ts deleted file mode 100644 index 4dbffc51..00000000 --- a/packages/core/src/runtime/worker.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Agile, StateKey} from "../internal"; -import {Job} from "./job"; - -export type WorkerKey = string | number; - -export class Worker { - - public agileInstance: () => Agile; - public _key?: WorkerKey; - - constructor(agileInstance: Agile) { - this.agileInstance = () => agileInstance; - } - - public set key(value: StateKey | undefined) { - this._key = value; - } - - public get key(): StateKey | undefined { - return this._key; - } - - public perform(job: Job) { - console.warn("Didn't set perform function in Worker ", this.key); - } -} \ No newline at end of file diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 8ccdbb24..e9f163ea 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -8,7 +8,7 @@ import { isValidObject } from '../internal'; import {persistValue, updateValue} from './persist'; -import {Observable} from "../runtime/observable"; +import {StateObserver} from "./state.observer"; export type StateKey = string | number; @@ -18,18 +18,18 @@ export interface PersistSettingsInterface { persistKey?: string | number // Current Persist Key.. for handling twice persisted states } -export class State extends Observable{ +export class State { public agileInstance: () => Agile; public _key?: StateKey; // should be a unique key/name which identifies the state public valueType?: string; // primitive types for js users - public dep: Dep; // Includes the subscriptions and dependencies of the state public watchers: { [key: string]: (value: any) => void } = {}; public sideEffects?: Function; // SideEffects can be set by extended classes, such as Groups to build their output. public isSet: boolean = false; // Has been changed from initial value public persistSettings: PersistSettingsInterface; // Includes persist 'settings' (have to rename if I got an better name) public output?: any; // This contains the public value.. if _value doesn't contain the public value (Used for example by collections) public isPlaceholder: boolean = false; // Defines if the state is a placeholder or not + public observer: StateObserver; public initialState: ValueType; public _value: ValueType; // The current value of the state @@ -39,7 +39,6 @@ export class State extends Observable{ constructor(agileInstance: Agile, initialState: ValueType, key?: StateKey, deps: Array = []) { this.agileInstance = () => agileInstance; this.initialState = initialState; - this.dep = new Dep(deps); this._key = key; this._value = initialState; this.previousState = initialState; @@ -47,6 +46,7 @@ export class State extends Observable{ this.persistSettings = { isPersisted: false } + this.observer = new StateObserver(agileInstance, this); } public set value(value: ValueType) { @@ -94,7 +94,7 @@ export class State extends Observable{ return this; // Ingest updated value - this.agileInstance().runtime.ingest(this, value, { + this.observer.ingest(value, { background: options.background, sideEffects: options.sideEffects }); @@ -118,7 +118,7 @@ export class State extends Observable{ forceRerender: false }); - this.agileInstance().runtime.ingest(this, this.agileInstance().runtime.internalIngestKey, { + this.observer.ingest(this.observer.internalIngestKey, { background: options.background, sideEffects: options.sideEffects, forceRerender: options.forceRerender diff --git a/packages/core/src/state/state.worker.ts b/packages/core/src/state/state.observer.ts similarity index 80% rename from packages/core/src/state/state.worker.ts rename to packages/core/src/state/state.observer.ts index 3de0280c..150c9f2c 100644 --- a/packages/core/src/state/state.worker.ts +++ b/packages/core/src/state/state.observer.ts @@ -1,22 +1,22 @@ import {Agile} from "../agile"; -import {Worker} from "../runtime/worker"; +import {Observer} from "../runtime/observer"; import {State} from "./index"; import {copy, defineConfig} from "../utils"; import {Computed} from "../computed"; import {JobConfigInterface} from "../runtime"; import {Job} from "../runtime/job"; - -export class StateWorker extends Worker { +export class StateObserver extends Observer { public nextStateValue: ValueType; public state: State; - public internalIngestKey = "This is an Internal Key for ingesting internal stuff!"; + public internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; constructor(agileInstance: Agile, state: State) { super(agileInstance); this.state = state; this.nextStateValue = state.value; + this.value = state.value; this.key = state.key; } @@ -29,7 +29,7 @@ export class StateWorker extends Worker { * Creates a Job out of State and new Value and than add it to a job queue * Note: its not possible to set a state to undefined because undefined is used for internal activities! */ - public ingest(state: State, newStateValue?: any, options: JobConfigInterface = {}): void { + public ingest(newStateValue?: any, options: JobConfigInterface = {}): void { // Merge default values into options options = defineConfig(options, { perform: true, @@ -40,15 +40,15 @@ export class StateWorker extends Worker { // Grab nextState if newState not passed or compute if needed if (newStateValue === this.internalIngestKey) { - if (state instanceof Computed) - this.nextStateValue = state.computeValue(); + if (this.state instanceof Computed) + this.nextStateValue = this.state.computeValue(); else - this.nextStateValue = state.nextState + this.nextStateValue = this.state.nextState } this.nextStateValue = newStateValue; // Check if state value und newStateValue are the same.. if so return except force Rerender (stringifying because of possible object or array) - if (JSON.stringify(state.value) === JSON.stringify(this.nextStateValue) && !options.forceRerender) { + if (JSON.stringify(this.state.value) === JSON.stringify(this.nextStateValue) && !options.forceRerender) { if (this.agileInstance().config.logJobs) console.warn("Agile: Doesn't created job because state values are the same! "); return; @@ -67,7 +67,7 @@ export class StateWorker extends Worker { * TOD_O */ public perform(job: Job) { - const state = job.worker.state; + const state = job.observable.state; // Set Previous State state.previousState = copy(state.value); @@ -95,7 +95,7 @@ export class StateWorker extends Worker { * SideEffects are sideEffects of the perform function.. for instance the watchers */ private sideEffects(job: Job) { - const state = job.worker.state; + const state = job.observable.state; // Call Watchers for (let watcher in state.watchers) @@ -107,6 +107,6 @@ export class StateWorker extends Worker { state.sideEffects(); // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) - state.dep.deps.forEach((state) => this.ingest(state, this.internalIngestKey, {perform: false})); + job.observable.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(this.internalIngestKey, {perform: false})); } } \ No newline at end of file From ea9476ed395f5a00f6a451559555b6e7a9cebba9 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Sun, 4 Oct 2020 18:46:41 +0200 Subject: [PATCH 3/9] x --- packages/core/src/agile.ts | 3 ++- packages/core/src/computed/index.ts | 23 ++++++++++--------- packages/core/src/internal.ts | 4 +++- packages/core/src/runtime/dep.ts | 2 +- packages/core/src/runtime/index.ts | 19 ++++----------- packages/core/src/runtime/job.ts | 4 ++-- packages/core/src/runtime/subscription/sub.ts | 4 ++-- packages/core/src/state/index.ts | 2 +- packages/core/src/state/state.observer.ts | 8 +------ .../test_integration/hooks/useAgile_Test.ts | 2 +- 10 files changed, 30 insertions(+), 41 deletions(-) diff --git a/packages/core/src/agile.ts b/packages/core/src/agile.ts index 596b576d..16926307 100644 --- a/packages/core/src/agile.ts +++ b/packages/core/src/agile.ts @@ -13,6 +13,7 @@ import { EventConfig, DefaultEventPayload, Integrations } from './internal' +import {Observer} from "./runtime/observer"; export interface AgileConfigInterface { logJobs?: boolean // If Agile should log some stuff in the console @@ -110,7 +111,7 @@ export class Agile { * @param deps Array - An array of state items to depend on * @param computeFunction Function - A function where the return value is the state, ran every time a dep changes */ - public Computed = (computeFunction: () => ComputedValueType, deps?: Array) => new Computed(this, computeFunction, deps); + public Computed = (computeFunction: () => ComputedValueType, deps?: Array) => new Computed(this, computeFunction, deps?.map(state => state.observer)); //========================================================================================================= diff --git a/packages/core/src/computed/index.ts b/packages/core/src/computed/index.ts index 603223e7..0543b609 100644 --- a/packages/core/src/computed/index.ts +++ b/packages/core/src/computed/index.ts @@ -1,17 +1,18 @@ import { State, Agile, - defineConfig + defineConfig, + Observer } from '../internal'; export class Computed extends State { public agileInstance: () => Agile; public computeFunction: () => ComputedValueType; - public deps: Array = []; - public hardCodedDeps: Array = []; + public deps: Array = []; + public hardCodedDeps: Array = []; - constructor(agileInstance: Agile, computeFunction: () => ComputedValueType, deps: Array = []) { + constructor(agileInstance: Agile, computeFunction: () => ComputedValueType, deps: Array = []) { super(agileInstance, computeFunction()); this.agileInstance = () => agileInstance; this.computeFunction = computeFunction; @@ -30,7 +31,7 @@ export class Computed extends State // Add state to foundState (for auto tracking used states in computed functions) if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + this.agileInstance().runtime.foundStates.add(this.observer); return this._value; } @@ -60,7 +61,7 @@ export class Computed extends State /** * Updates the Compute Function */ - public updateComputeFunction(computeFunction: () => ComputedValueType, deps: Array = [], options?: { background?: boolean, sideEffects?: boolean }) { + public updateComputeFunction(computeFunction: () => ComputedValueType, deps: Array = [], options?: { background?: boolean, sideEffects?: boolean }) { this.computeFunction = computeFunction; this.hardCodedDeps = deps; @@ -84,22 +85,22 @@ export class Computed extends State const computedValue = this.computeFunction(); // Get tracked states and set trackSate to false - let foundStates = this.agileInstance().runtime.getTrackedStates(); + let foundStates = this.agileInstance().runtime.getTrackedObserver(); // Handle foundStates dependencies - const newDeps: Array = []; + const newDeps: Array = []; foundStates.forEach(state => { // Add the state to newDeps newDeps.push(state); // Add this as dependency of the state - state.dep.depend(this); + state.dep.depend(this.observer); }); // Handle hardCoded dependencies - this.hardCodedDeps.forEach(state => { + this.hardCodedDeps.forEach(observer => { // Add this as dependency of the state - state.dep.depend(this); + observer.dep.depend(this.observer); }); // Set deps diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index 2695b49a..8df262a2 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -25,9 +25,11 @@ export * from './collection/selector'; export * from './event'; // Internal Classes -export * from './runtime'; export * from './storage'; export * from './integrations'; +export * from './runtime'; +export * from './runtime/observer'; +export * from './runtime/job'; export * from './runtime/subscription/sub'; export * from './runtime/subscription/CallbackSubscriptionContainer'; export * from './runtime/subscription/ComponentSubscriptionContainer'; diff --git a/packages/core/src/runtime/dep.ts b/packages/core/src/runtime/dep.ts index 526ce39b..9e5f6726 100644 --- a/packages/core/src/runtime/dep.ts +++ b/packages/core/src/runtime/dep.ts @@ -1,7 +1,7 @@ import { SubscriptionContainer } from '../internal'; -import {Observer} from "./observer"; +import {Observer} from "../internal"; export class Dep { public deps: Set = new Set(); // Dependencies from the State diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 9ecc6a71..3f315e78 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -1,21 +1,12 @@ import { Agile, - Computed, SubscriptionContainer, - copy, - defineConfig + defineConfig, + Observer, + Job, + JobConfigInterface } from '../internal'; import {CallbackSubscriptionContainer} from "./subscription/CallbackSubscriptionContainer"; -import {Observer} from "./observer"; -import {Job} from "./job"; - - -export interface JobConfigInterface { - perform?: boolean // Should preform the job instantly - background?: boolean // Shouldn't cause an rerender during the perform process - sideEffects?: boolean // Should perform sideEffects like rebuilding groups - forceRerender?: boolean // Force rerender although for instance the values are the same -} export class Runtime { public agileInstance: () => Agile; @@ -196,7 +187,7 @@ export class Runtime { * @internal * Will return all tracked States */ - public getTrackedStates() { + public getTrackedObserver() { const finalFoundStates = this.foundStates; // Reset tracking diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts index 5c9ccfbf..769e432f 100644 --- a/packages/core/src/runtime/job.ts +++ b/packages/core/src/runtime/job.ts @@ -1,10 +1,10 @@ -import {Observer} from "./observer"; -import {defineConfig} from "../utils"; +import {Observer, defineConfig} from "../internal"; export interface JobConfigInterface { background?: boolean sideEffects?: boolean forceRerender?: boolean + perform?: boolean } export class Job { diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.ts index b1a0b827..ec803ffd 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.ts @@ -1,7 +1,7 @@ -import {Agile} from '../../internal'; +import {Agile, Observer} from '../../internal'; import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; import {CallbackSubscriptionContainer} from "./CallbackSubscriptionContainer"; -import {Observer} from "../observer"; + //========================================================================================================= diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index e9f163ea..38c95c48 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -56,7 +56,7 @@ export class State { public get value(): ValueType { // Add state to foundState (for auto tracking used states in computed functions) if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + this.agileInstance().runtime.foundStates.add(this.observer); return this._value; } diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 150c9f2c..fee6f7aa 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -1,10 +1,4 @@ -import {Agile} from "../agile"; -import {Observer} from "../runtime/observer"; -import {State} from "./index"; -import {copy, defineConfig} from "../utils"; -import {Computed} from "../computed"; -import {JobConfigInterface} from "../runtime"; -import {Job} from "../runtime/job"; +import {Agile, Observer, State, Computed, Job, copy, defineConfig, JobConfigInterface} from '../internal'; export class StateObserver extends Observer { diff --git a/packages/core/tests/test_integration/hooks/useAgile_Test.ts b/packages/core/tests/test_integration/hooks/useAgile_Test.ts index 458716e5..0e1a321f 100644 --- a/packages/core/tests/test_integration/hooks/useAgile_Test.ts +++ b/packages/core/tests/test_integration/hooks/useAgile_Test.ts @@ -58,7 +58,7 @@ export function useAgile_Test, Y } // Create a callback base subscription, Callback invokes re-render Trigger - agileInstance?.subController.subscribeWithSubsArray(callbackFunction, depsArray); + agileInstance?.subController.subscribeWithSubsArray(callbackFunction, depsArray.map(state => state.observer)); return getReturnValue(depsArray); } From 39d271a6535b794a52ddb5f44169c552afb7b60a Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Sun, 4 Oct 2020 19:29:52 +0200 Subject: [PATCH 4/9] x --- packages/core/src/agile.ts | 1 - packages/core/src/collection/group.ts | 4 ++-- packages/core/src/collection/index.ts | 2 +- packages/core/src/computed/index.ts | 8 ++++---- packages/core/src/internal.ts | 15 +++++++++------ packages/core/src/runtime/dep.ts | 4 ++-- packages/core/src/runtime/index.ts | 10 +++++----- packages/core/src/runtime/job.ts | 6 +++--- packages/core/src/runtime/observer.ts | 3 +-- .../CallbackSubscriptionContainer.ts | 2 +- .../ComponentSubscriptionContainer.ts | 3 +-- packages/core/src/runtime/subscription/sub.ts | 6 ------ packages/core/src/state/index.ts | 5 ++--- packages/core/src/state/state.observer.ts | 17 +++++++++++++---- 14 files changed, 44 insertions(+), 42 deletions(-) diff --git a/packages/core/src/agile.ts b/packages/core/src/agile.ts index 16926307..b1035971 100644 --- a/packages/core/src/agile.ts +++ b/packages/core/src/agile.ts @@ -13,7 +13,6 @@ import { EventConfig, DefaultEventPayload, Integrations } from './internal' -import {Observer} from "./runtime/observer"; export interface AgileConfigInterface { logJobs?: boolean // If Agile should log some stuff in the console diff --git a/packages/core/src/collection/group.ts b/packages/core/src/collection/group.ts index b95f2c25..1cc072ed 100644 --- a/packages/core/src/collection/group.ts +++ b/packages/core/src/collection/group.ts @@ -45,7 +45,7 @@ export class Group extends State> { public get output(): Array { // Add state(group) to foundState (for auto tracking used states in computed functions) if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + this.agileInstance().runtime.foundStates.add(this.observer); return this._output; } @@ -53,7 +53,7 @@ export class Group extends State> { public get states(): Array> { // Add state(group) to foundState (for auto tracking used states in computed functions) if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + this.agileInstance().runtime.foundStates.add(this.observer); return this._states.map(state => state()); } diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index f53b6472..4817340d 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -374,7 +374,7 @@ export class Collection { // Add state to foundState (for auto tracking used states in computed functions) if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.data[id]); + this.agileInstance().runtime.foundStates.add(this.data[id].observer); // Return data by id return this.data[id]; diff --git a/packages/core/src/computed/index.ts b/packages/core/src/computed/index.ts index 0543b609..10279421 100644 --- a/packages/core/src/computed/index.ts +++ b/packages/core/src/computed/index.ts @@ -85,16 +85,16 @@ export class Computed extends State const computedValue = this.computeFunction(); // Get tracked states and set trackSate to false - let foundStates = this.agileInstance().runtime.getTrackedObserver(); + let foundObservers = this.agileInstance().runtime.getTrackedObserver(); // Handle foundStates dependencies const newDeps: Array = []; - foundStates.forEach(state => { + foundObservers.forEach(observer => { // Add the state to newDeps - newDeps.push(state); + newDeps.push(observer); // Add this as dependency of the state - state.dep.depend(this.observer); + observer.dep.depend(this.observer); }); // Handle hardCoded dependencies diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index 8df262a2..7add1d11 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -8,9 +8,17 @@ // Agile export * from './agile'; +// Runtime +export * from './runtime/observer'; +export * from './runtime/dep'; +export * from './runtime/job'; +export * from './runtime/subscription/sub'; +export * from './runtime/subscription/CallbackSubscriptionContainer'; +export * from './runtime/subscription/ComponentSubscriptionContainer'; + // State export * from './state'; -export * from './runtime/dep'; +export * from './state/state.observer'; // Computed export {Computed} from './computed'; @@ -28,11 +36,6 @@ export * from './event'; export * from './storage'; export * from './integrations'; export * from './runtime'; -export * from './runtime/observer'; -export * from './runtime/job'; -export * from './runtime/subscription/sub'; -export * from './runtime/subscription/CallbackSubscriptionContainer'; -export * from './runtime/subscription/ComponentSubscriptionContainer'; // Utils export * from './utils'; \ No newline at end of file diff --git a/packages/core/src/runtime/dep.ts b/packages/core/src/runtime/dep.ts index 9e5f6726..c71c7bc3 100644 --- a/packages/core/src/runtime/dep.ts +++ b/packages/core/src/runtime/dep.ts @@ -1,7 +1,7 @@ import { - SubscriptionContainer + SubscriptionContainer, + Observer } from '../internal'; -import {Observer} from "../internal"; export class Dep { public deps: Set = new Set(); // Dependencies from the State diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 3f315e78..78fb97c1 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -42,7 +42,7 @@ export class Runtime { // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Created Job(${job.observable.key})`, job); + console.log(`Agile: Created Job(${job.observer.key})`, job); // Push the Job to the Queue (safety.. that no Job get forgotten) this.jobQueue.push(job); @@ -70,7 +70,7 @@ export class Runtime { this.currentJob = job; // Perform Job - job.observable.perform(job); + job.observer.perform(job); job.performed = true; // Add to rerender @@ -82,7 +82,7 @@ export class Runtime { // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Completed Job(${job.observable.key})`, job); + console.log(`Agile: Completed Job(${job.observer.key})`, job); // Continue the Loop and perform the next job.. if no job is left update the Subscribers for each completed job if (this.jobQueue.length > 0) { @@ -118,7 +118,7 @@ export class Runtime { // Map through Jobs to Rerender this.jobsToRerender.forEach(job => // Map through subs of the current Job State - job.observable.dep.subs.forEach(subscriptionContainer => { + job.observer.dep.subs.forEach(subscriptionContainer => { // Check if subscriptionContainer is ready if (!subscriptionContainer.ready) console.warn("Agile: SubscriptionContainer isn't ready yet ", subscriptionContainer); @@ -129,7 +129,7 @@ export class Runtime { // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object for (let key in subscriptionContainer.propObservable) - if (subscriptionContainer.propObservable[key] === job.observable) + if (subscriptionContainer.propObservable[key] === job.observer) localKey = key; // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts index 769e432f..b2992919 100644 --- a/packages/core/src/runtime/job.ts +++ b/packages/core/src/runtime/job.ts @@ -9,12 +9,12 @@ export interface JobConfigInterface { export class Job { - public observable: o; + public observer: o; public config: JobConfigInterface; public rerender: boolean; public performed: boolean = false; - constructor(observable: o, config: JobConfigInterface) { + constructor(observer: o, config: JobConfigInterface) { // Merge default values into options this.config = defineConfig(config, { background: false, @@ -22,7 +22,7 @@ export class Job { forceRerender: false }); - this.observable = observable; + this.observer = observer; this.config = config; this.rerender = !config?.background || config?.forceRerender || true; } diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts index f14302c2..b255fe0d 100644 --- a/packages/core/src/runtime/observer.ts +++ b/packages/core/src/runtime/observer.ts @@ -1,5 +1,4 @@ -import {Agile, Dep, StateKey} from "../internal"; -import {Job} from "./job"; +import {Agile, Dep, StateKey, Job} from "../internal"; export type ObservableKey = string | number; diff --git a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts index 55f57d7d..c8698d52 100644 --- a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts @@ -1,5 +1,5 @@ +import {Observer} from "../../internal"; import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; -import {Observer} from "../observer"; export class CallbackSubscriptionContainer extends ComponentSubscriptionContainer { public callback: Function; diff --git a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts index da627b38..c1089698 100644 --- a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts @@ -1,5 +1,4 @@ -import {State} from "../../internal"; -import {Observer} from "../observer"; +import {Observer} from "../../internal"; export class ComponentSubscriptionContainer { public component: any; diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.ts index ec803ffd..19b1e870 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.ts @@ -2,12 +2,6 @@ import {Agile, Observer} from '../../internal'; import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; import {CallbackSubscriptionContainer} from "./CallbackSubscriptionContainer"; - - -//========================================================================================================= -// Subscription Container -//========================================================================================================= - export type SubscriptionContainer = ComponentSubscriptionContainer | CallbackSubscriptionContainer; diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 38c95c48..18ad47ce 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -5,11 +5,10 @@ import { copy, defineConfig, flatMerge, - isValidObject + isValidObject, + StateObserver } from '../internal'; import {persistValue, updateValue} from './persist'; -import {StateObserver} from "./state.observer"; - export type StateKey = string | number; diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index fee6f7aa..9594622f 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -1,4 +1,13 @@ -import {Agile, Observer, State, Computed, Job, copy, defineConfig, JobConfigInterface} from '../internal'; +import { + Agile, + Observer, + State, + Computed, + Job, + JobConfigInterface, + copy, + defineConfig + } from '../internal'; export class StateObserver extends Observer { @@ -61,7 +70,7 @@ export class StateObserver extends Observer { * TOD_O */ public perform(job: Job) { - const state = job.observable.state; + const state = job.observer.state; // Set Previous State state.previousState = copy(state.value); @@ -89,7 +98,7 @@ export class StateObserver extends Observer { * SideEffects are sideEffects of the perform function.. for instance the watchers */ private sideEffects(job: Job) { - const state = job.observable.state; + const state = job.observer.state; // Call Watchers for (let watcher in state.watchers) @@ -101,6 +110,6 @@ export class StateObserver extends Observer { state.sideEffects(); // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) - job.observable.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(this.internalIngestKey, {perform: false})); + job.observer.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(this.internalIngestKey, {perform: false})); } } \ No newline at end of file From 5477b4f062ba00e4d08e2bb58c8ccea6aeb5bac4 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Tue, 6 Oct 2020 20:57:43 +0200 Subject: [PATCH 5/9] x --- packages/core/src/collection/group.ts | 9 ++- packages/core/src/collection/index.ts | 4 +- packages/core/src/computed/index.ts | 12 +-- packages/core/src/runtime/index.ts | 32 ++++---- packages/core/src/runtime/observer.ts | 10 ++- .../ComponentSubscriptionContainer.ts | 2 +- packages/core/src/runtime/subscription/sub.ts | 2 +- packages/core/src/state/index.ts | 8 +- packages/core/src/state/state.observer.ts | 13 ++-- .../functions/collect.function.spec.ts | 4 +- .../functions/update.function.spec.ts | 2 +- .../tests/collection/group/default.spec.ts | 8 +- .../tests/collection/selector/default.spec.ts | 2 +- .../state/functions/patch.function.spec.ts | 2 +- .../state/functions/reset.function.spec.ts | 2 +- .../state/functions/set.function.spec.ts | 2 +- .../state/functions/undo.function.spec.ts | 2 +- packages/react/package-lock.json | 73 ++++++++++++++++++- packages/react/package.json | 4 +- packages/react/src/hooks/AgileHOC.ts | 12 +-- packages/react/src/hooks/useAgile.ts | 2 +- 21 files changed, 143 insertions(+), 64 deletions(-) diff --git a/packages/core/src/collection/group.ts b/packages/core/src/collection/group.ts index 1cc072ed..a4e2829c 100644 --- a/packages/core/src/collection/group.ts +++ b/packages/core/src/collection/group.ts @@ -44,16 +44,16 @@ export class Group extends State> { public get output(): Array { // Add state(group) to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.observer); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._output; } public get states(): Array> { // Add state(group) to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.observer); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._states.map(state => state()); } @@ -65,6 +65,7 @@ export class Group extends State> { * Checks if the group contains the primaryKey */ public has(primaryKey: ItemKey) { + console.log(this.value); return this.value.findIndex(key => key === primaryKey) !== -1; } diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index 4817340d..76628c6f 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -373,8 +373,8 @@ export class Collection { return undefined; // Add state to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.data[id].observer); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.data[id].observer); // Return data by id return this.data[id]; diff --git a/packages/core/src/computed/index.ts b/packages/core/src/computed/index.ts index 10279421..871a9d06 100644 --- a/packages/core/src/computed/index.ts +++ b/packages/core/src/computed/index.ts @@ -30,8 +30,8 @@ export class Computed extends State // Note can't use 'super.value' because of 'https://github.com/Microsoft/TypeScript/issues/338' // Add state to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.observer); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._value; } @@ -61,9 +61,9 @@ export class Computed extends State /** * Updates the Compute Function */ - public updateComputeFunction(computeFunction: () => ComputedValueType, deps: Array = [], options?: { background?: boolean, sideEffects?: boolean }) { + public updateComputeFunction(computeFunction: () => ComputedValueType, deps: Array = [], options?: { background?: boolean, sideEffects?: boolean }) { this.computeFunction = computeFunction; - this.hardCodedDeps = deps; + this.hardCodedDeps = deps.map(state => state.observer); // Recompute for setting initial state value and adding missing dependencies this.recompute(options); @@ -79,7 +79,7 @@ export class Computed extends State */ public computeValue(): ComputedValueType { // Set tracking state to true which will than track all states which for instance call state.value - this.agileInstance().runtime.trackState = true; + this.agileInstance().runtime.trackObserver = true; // Call computeFunction const computedValue = this.computeFunction(); @@ -90,6 +90,8 @@ export class Computed extends State // Handle foundStates dependencies const newDeps: Array = []; foundObservers.forEach(observer => { + if(!observer) return; + // Add the state to newDeps newDeps.push(observer); diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 78fb97c1..49bedef2 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -17,14 +17,14 @@ export class Runtime { private jobsToRerender: Array = []; // Used for tracking computed dependencies - public trackState: boolean = false; // Check if agile should track states - public foundStates: Set = new Set(); // States which were tracked during the track time + public trackObserver: boolean = false; // Check if agile should track states + public foundObservers: Set = new Set(); // States which were tracked during the track time constructor(agileInstance: Agile) { this.agileInstance = () => agileInstance; } - public ingest(worker: Observer, options: JobConfigInterface): void { + public ingest(observer: Observer, options: JobConfigInterface): void { // Merge default values into options options = defineConfig(options, { perform: true, @@ -34,12 +34,15 @@ export class Runtime { }); // Create Job - const job = new Job(worker, { + const job = new Job(observer, { background: options.background, sideEffects: options.sideEffects, forceRerender: options.forceRerender }); + // Set rerender depending on if it has an integration + job.rerender = this.agileInstance().integrations.hasIntegration(); + // Logging if (this.agileInstance().config.logJobs) console.log(`Agile: Created Job(${job.observer.key})`, job); @@ -107,12 +110,7 @@ export class Runtime { * This will be update all Subscribers of complete jobs */ private updateSubscribers(): void { - // Check if Agile has an integration because its useless to go trough this process without framework - // It won't happen anything because the state has no subs.. but this check here will maybe improve the performance - if (!this.agileInstance().integrations.hasIntegration()) - return; - - // Subscriptions that has to be updated (Set = For preventing double subscriptions without further checks) + // Subscriptions that has to be updated const subscriptionsToUpdate: Set = new Set(); // Map through Jobs to Rerender @@ -128,8 +126,8 @@ export class Runtime { let localKey: string | null = null; // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object - for (let key in subscriptionContainer.propObservable) - if (subscriptionContainer.propObservable[key] === job.observer) + for (let key in subscriptionContainer.propObservers) + if (subscriptionContainer.propObservers[key] === job.observer) localKey = key; // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object @@ -172,8 +170,8 @@ export class Runtime { // Build Object subscriptionContainer.propKeysChanged.forEach(changedKey => { - if (subscriptionContainer.propObservable) - finalObject[changedKey] = subscriptionContainer.propObservable[changedKey].value; + if (subscriptionContainer.propObservers) + finalObject[changedKey] = subscriptionContainer.propObservers[changedKey].value; }); return finalObject; @@ -188,11 +186,11 @@ export class Runtime { * Will return all tracked States */ public getTrackedObserver() { - const finalFoundStates = this.foundStates; + const finalFoundStates = this.foundObservers; // Reset tracking - this.trackState = false; - this.foundStates = new Set(); + this.trackObserver = false; + this.foundObservers = new Set(); return finalFoundStates; } diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts index b255fe0d..20e0e52f 100644 --- a/packages/core/src/runtime/observer.ts +++ b/packages/core/src/runtime/observer.ts @@ -7,12 +7,14 @@ export class Observer { public agileInstance: () => Agile; public _key?: ObservableKey; - public dep: Dep; - public value: any; // The current value which will be returned for instance if its a prop based subscription + public dep: Dep; // Dependencies and Subscriptions of the Observer - constructor(agileInstance: Agile) { + public hasValue: boolean = false; // Weather the Observer has an value or not + public value: any; // Value of the Observer if it has one + + constructor(agileInstance: Agile, deps?: Array) { this.agileInstance = () => agileInstance; - this.dep = new Dep(); + this.dep = new Dep(deps); } public set key(value: StateKey | undefined) { diff --git a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts index c1089698..5895b3e9 100644 --- a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts @@ -5,7 +5,7 @@ export class ComponentSubscriptionContainer { // Only needed by object orientated subscriptions public passProps: boolean = false; - public propObservable?: { [key: string]: Observer }; // states which will than be returned as prop object by the integration + public propObservers?: { [key: string]: Observer }; // states which will than be returned as prop object by the integration public propKeysChanged: Array = []; // Used to preserve local keys to update before update is performed, cleared every update public ready: boolean = false; diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.ts index 19b1e870..176fc973 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.ts @@ -34,7 +34,7 @@ export class SubController { const props: { [key: string]: Observer } = {}; subscriptionContainer.passProps = true; - subscriptionContainer.propObservable = {...subs}; + subscriptionContainer.propObservers = {...subs}; // Go through subs let localKeys = Object.keys(subs); diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 18ad47ce..1b989322 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -6,7 +6,7 @@ import { defineConfig, flatMerge, isValidObject, - StateObserver + StateObserver, internalIngestKey } from '../internal'; import {persistValue, updateValue} from './persist'; @@ -54,8 +54,8 @@ export class State { public get value(): ValueType { // Add state to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.observer); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._value; } @@ -117,7 +117,7 @@ export class State { forceRerender: false }); - this.observer.ingest(this.observer.internalIngestKey, { + this.observer.ingest(internalIngestKey, { background: options.background, sideEffects: options.sideEffects, forceRerender: options.forceRerender diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 9594622f..5a32f931 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -9,18 +9,19 @@ import { defineConfig } from '../internal'; +export const internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; + export class StateObserver extends Observer { - public nextStateValue: ValueType; - public state: State; - public internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; + public state: State; // State on which the Observer is watching + public nextStateValue: ValueType; // The next State value constructor(agileInstance: Agile, state: State) { super(agileInstance); this.state = state; this.nextStateValue = state.value; this.value = state.value; - this.key = state.key; + this.key = `o_${state.key}`; } @@ -42,7 +43,7 @@ export class StateObserver extends Observer { }); // Grab nextState if newState not passed or compute if needed - if (newStateValue === this.internalIngestKey) { + if (newStateValue === internalIngestKey) { if (this.state instanceof Computed) this.nextStateValue = this.state.computeValue(); else @@ -110,6 +111,6 @@ export class StateObserver extends Observer { state.sideEffects(); // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) - job.observer.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(this.internalIngestKey, {perform: false})); + job.observer.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(internalIngestKey, {perform: false})); } } \ No newline at end of file diff --git a/packages/core/tests/collection/functions/collect.function.spec.ts b/packages/core/tests/collection/functions/collect.function.spec.ts index deea14f7..e66c88aa 100644 --- a/packages/core/tests/collection/functions/collect.function.spec.ts +++ b/packages/core/tests/collection/functions/collect.function.spec.ts @@ -28,7 +28,7 @@ describe('Collect Function Tests', () => { it('Has correct initial values', () => { expect(JSON.stringify(MY_COLLECTION.data)).to.eq(JSON.stringify({}), 'MY_COLLECTION has correct data'); expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created') - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(myHookCollection)).to.eq(JSON.stringify([]), 'myHookState has correct MY_COLLECTION value'); expect(rerenderCount).to.eq(0, 'rerenderCount is 0'); @@ -311,7 +311,7 @@ describe('Collect Function Tests', () => { it('Has correct initial values', () => { expect(JSON.stringify(MY_COLLECTION.data)).to.eq(JSON.stringify({}), 'MY_COLLECTION has correct data'); expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created') - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(myHookCollection)).to.eq(JSON.stringify([]), 'myHookState has correct MY_COLLECTION value'); }); diff --git a/packages/core/tests/collection/functions/update.function.spec.ts b/packages/core/tests/collection/functions/update.function.spec.ts index 6798d92b..94096012 100644 --- a/packages/core/tests/collection/functions/update.function.spec.ts +++ b/packages/core/tests/collection/functions/update.function.spec.ts @@ -29,7 +29,7 @@ describe('Update Function Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created') - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(MY_COLLECTION.size).to.eq(3, 'MY_COLLECTION has correct size'); expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq(JSON.stringify({ id: 1, diff --git a/packages/core/tests/collection/group/default.spec.ts b/packages/core/tests/collection/group/default.spec.ts index afc85def..2e1f87db 100644 --- a/packages/core/tests/collection/group/default.spec.ts +++ b/packages/core/tests/collection/group/default.spec.ts @@ -31,14 +31,14 @@ describe('Default Group Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created'); - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['default'].value)).to.eq(JSON.stringify([]), 'default Group has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['default'].output)).to.eq(JSON.stringify([]), 'default Group has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['default'].states)).to.eq(JSON.stringify([]), 'default Group has correct initial states'); expect(MY_COLLECTION.groups['default'].key).to.eq('default', 'group1 Group has correct initial key'); expect(MY_COLLECTION.groups['group1'] instanceof Group).to.eq(true, 'MY_COLLECTION group1 Group has been created'); - expect(MY_COLLECTION.groups['group1']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); + expect(MY_COLLECTION.groups['group1']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].value)).to.eq(JSON.stringify([]), 'group1 Group has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].output)).to.eq(JSON.stringify([]), 'group1 Group has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].states)).to.eq(JSON.stringify([]), 'group1 Group has correct initial states'); @@ -99,14 +99,14 @@ describe('Default Group Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created'); - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['default'].value)).to.eq(JSON.stringify([]), 'default has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['default'].output)).to.eq(JSON.stringify([]), 'default has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['default'].states)).to.eq(JSON.stringify([]), 'default has correct initial states'); expect(MY_COLLECTION.groups['default'].key).to.eq('default', 'default Group has correct initial key'); expect(MY_COLLECTION.groups['group1'] instanceof Group).to.eq(true, 'MY_COLLECTION group1 Group has been created'); - expect(MY_COLLECTION.groups['group1']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); + expect(MY_COLLECTION.groups['group1']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].value)).to.eq(JSON.stringify([1, 2, 3]), 'group1 Group has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].output)).to.eq(JSON.stringify([]), 'group1 Group has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].states)).to.eq(JSON.stringify([]), 'group1 Group has correct initial states'); diff --git a/packages/core/tests/collection/selector/default.spec.ts b/packages/core/tests/collection/selector/default.spec.ts index 61683127..500d9df4 100644 --- a/packages/core/tests/collection/selector/default.spec.ts +++ b/packages/core/tests/collection/selector/default.spec.ts @@ -31,7 +31,7 @@ describe('Default Selector Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.selectors['selector1'] instanceof Selector).to.eq(true, 'MY_COLLECTION selector1 Selector has been created'); - expect(MY_COLLECTION.selectors['selector1']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION selector1 Selector has correct subs size'); + expect(MY_COLLECTION.selectors['selector1']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION selector1 Selector has correct subs size'); expect(MY_COLLECTION.selectors['selector1'].key).to.eq('selector1', 'selector1 Selector has correct initial key'); expect(MY_COLLECTION.selectors['selector1'].id).to.eq(1, 'selector1 Selector has correct initial id'); expect(MY_COLLECTION.selectors['selector1'].exists).to.eq(false, 'selector1 Selector doesn\'t exist'); diff --git a/packages/core/tests/state/functions/patch.function.spec.ts b/packages/core/tests/state/functions/patch.function.spec.ts index feaeb06b..6787520f 100644 --- a/packages/core/tests/state/functions/patch.function.spec.ts +++ b/packages/core/tests/state/functions/patch.function.spec.ts @@ -34,7 +34,7 @@ describe('Patch Function Tests', () => { id: 1, name: 'jeff' }), 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(JSON.stringify(myHookState)).to.eq(JSON.stringify({ diff --git a/packages/core/tests/state/functions/reset.function.spec.ts b/packages/core/tests/state/functions/reset.function.spec.ts index d12edd91..34b3956d 100644 --- a/packages/core/tests/state/functions/reset.function.spec.ts +++ b/packages/core/tests/state/functions/reset.function.spec.ts @@ -25,7 +25,7 @@ describe('Reset Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(myHookState).to.eq(1, 'myHookState has correct MY_STATE value'); diff --git a/packages/core/tests/state/functions/set.function.spec.ts b/packages/core/tests/state/functions/set.function.spec.ts index 1c3e4b93..10df7d45 100644 --- a/packages/core/tests/state/functions/set.function.spec.ts +++ b/packages/core/tests/state/functions/set.function.spec.ts @@ -25,7 +25,7 @@ describe('Set Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(myHookState).to.eq(1, 'myHookState has correct MY_STATE value'); diff --git a/packages/core/tests/state/functions/undo.function.spec.ts b/packages/core/tests/state/functions/undo.function.spec.ts index ecd73978..2627e887 100644 --- a/packages/core/tests/state/functions/undo.function.spec.ts +++ b/packages/core/tests/state/functions/undo.function.spec.ts @@ -25,7 +25,7 @@ describe('Undo Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(myHookState).to.eq(1, 'myHookState has correct MY_STATE value'); diff --git a/packages/react/package-lock.json b/packages/react/package-lock.json index 0d03e49d..61cce528 100644 --- a/packages/react/package-lock.json +++ b/packages/react/package-lock.json @@ -1,6 +1,6 @@ { "name": "@agile-ts/react", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,6 +13,22 @@ "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.9.51", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.51.tgz", + "integrity": "sha512-lQa12IyO+DMlnSZ3+AGHRUiUcpK47aakMMoBG8f7HGxJT8Yfe+WE128HIXaHOHVPReAW0oDS3KAI0JI2DDe1PQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -36,6 +52,12 @@ "which": "^2.0.1" } }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==", + "dev": true + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -90,6 +112,21 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -108,6 +145,12 @@ "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -129,6 +172,17 @@ "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "dev": true }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "ps-tree": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", @@ -138,6 +192,23 @@ "event-stream": "=3.3.4" } }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/packages/react/package.json b/packages/react/package.json index 71342e94..1b643a84 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,7 +23,9 @@ "ts-node": "^8.10.2", "tsc-watch": "^4.1.0", "tslib": "^2.0.0", - "typescript": "^3.9.7" + "typescript": "^3.9.7", + "react": "^16.13.1", + "@types/react": "^16.9.51" }, "publishConfig": { "access": "public" diff --git a/packages/react/src/hooks/AgileHOC.ts b/packages/react/src/hooks/AgileHOC.ts index 5bfbded6..c86452f7 100644 --- a/packages/react/src/hooks/AgileHOC.ts +++ b/packages/react/src/hooks/AgileHOC.ts @@ -1,13 +1,13 @@ import React from "react"; -import {State, Agile, ComponentSubscriptionContainer, getAgileInstance, normalizeArray} from "@agile-ts/core"; +import {State, Agile, ComponentSubscriptionContainer, getAgileInstance, normalizeArray, Observer} from "@agile-ts/core"; export function AgileHOC(ReactComponent: any, deps?: Array | { [key: string]: State } | State, agileInstance?: Agile) { - let depsArray: Array; - let depsObject: { [key: string]: State }; + let depsArray: Array; + let depsObject: { [key: string]: Observer } = {}; if (deps instanceof State || Array.isArray(deps)) { // Normalize Dependencies - depsArray = normalizeArray(deps || []); + depsArray = normalizeArray(deps || []).map(dep => dep.observer); // Get Agile Instance if (!agileInstance) { @@ -19,7 +19,9 @@ export function AgileHOC(ReactComponent: any, deps?: Array | { [key: stri } } } else if (typeof deps === "object") { - depsObject = deps; + for (let dep in deps) { + depsObject[dep] = deps[dep].observer; + } // Get Agile Instance if (!agileInstance) { diff --git a/packages/react/src/hooks/useAgile.ts b/packages/react/src/hooks/useAgile.ts index 8cc80a26..1a820b56 100644 --- a/packages/react/src/hooks/useAgile.ts +++ b/packages/react/src/hooks/useAgile.ts @@ -61,7 +61,7 @@ export function useAgile, Y exte () => { set_({}); }, - depsArray + depsArray.map(dep => dep.observer) ); // Unsubscribe on Unmount From ea648f3aff84aae5479eed0edf000720ae0b4bd2 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Thu, 8 Oct 2020 20:53:06 +0200 Subject: [PATCH 6/9] Some fixes --- packages/core/src/runtime/index.ts | 3 -- packages/core/src/runtime/job.ts | 12 +++--- packages/core/src/runtime/observer.ts | 9 ++-- packages/core/src/state/index.ts | 5 ++- packages/core/src/state/state.observer.ts | 50 +++++++++++++---------- 5 files changed, 43 insertions(+), 36 deletions(-) diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 49bedef2..311d654e 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -40,9 +40,6 @@ export class Runtime { forceRerender: options.forceRerender }); - // Set rerender depending on if it has an integration - job.rerender = this.agileInstance().integrations.hasIntegration(); - // Logging if (this.agileInstance().config.logJobs) console.log(`Agile: Created Job(${job.observer.key})`, job); diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts index b2992919..f48d9c46 100644 --- a/packages/core/src/runtime/job.ts +++ b/packages/core/src/runtime/job.ts @@ -7,14 +7,14 @@ export interface JobConfigInterface { perform?: boolean } -export class Job { +export class Job { - public observer: o; + public observer: ObserverType; public config: JobConfigInterface; public rerender: boolean; public performed: boolean = false; - constructor(observer: o, config: JobConfigInterface) { + constructor(observer: ObserverType, config: JobConfigInterface) { // Merge default values into options this.config = defineConfig(config, { background: false, @@ -22,9 +22,11 @@ export class Job { forceRerender: false }); + this.observer = observer; this.config = config; - this.rerender = !config?.background || config?.forceRerender || true; - } + // @ts-ignore + this.rerender = (!config.background || config.forceRerender) && this.observer.agileInstance().integrations.hasIntegration(); + } } \ No newline at end of file diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts index 20e0e52f..8d21a5c8 100644 --- a/packages/core/src/runtime/observer.ts +++ b/packages/core/src/runtime/observer.ts @@ -1,20 +1,21 @@ import {Agile, Dep, StateKey, Job} from "../internal"; -export type ObservableKey = string | number; +export type ObserverKey = string | number; -export class Observer { +export class Observer { public agileInstance: () => Agile; - public _key?: ObservableKey; + public _key?: ObserverKey; public dep: Dep; // Dependencies and Subscriptions of the Observer public hasValue: boolean = false; // Weather the Observer has an value or not public value: any; // Value of the Observer if it has one - constructor(agileInstance: Agile, deps?: Array) { + constructor(agileInstance: Agile, deps?: Array, key?: ObserverKey) { this.agileInstance = () => agileInstance; this.dep = new Dep(deps); + this._key = key; } public set key(value: StateKey | undefined) { diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 1b989322..894b1caf 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -35,7 +35,7 @@ export class State { public previousState: ValueType; // Will be set in runtime public nextState: ValueType; // The next state is used internal and represents the nextState which can be edited as wished (cleaner than always setting the state) - constructor(agileInstance: Agile, initialState: ValueType, key?: StateKey, deps: Array = []) { + constructor(agileInstance: Agile, initialState: ValueType, key?: StateKey, deps: Array = []) { this.agileInstance = () => agileInstance; this.initialState = initialState; this._key = key; @@ -45,7 +45,7 @@ export class State { this.persistSettings = { isPersisted: false } - this.observer = new StateObserver(agileInstance, this); + this.observer = new StateObserver(agileInstance, this, deps.map(state => state.observer), key); } public set value(value: ValueType) { @@ -62,6 +62,7 @@ export class State { public set key(value: StateKey | undefined) { this._key = value; + this.observer.key = `o_${value}`; } public get key(): StateKey | undefined { diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 5a32f931..0530c4b3 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -6,22 +6,23 @@ import { Job, JobConfigInterface, copy, - defineConfig - } from '../internal'; + defineConfig, + ObserverKey +} from '../internal'; export const internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; export class StateObserver extends Observer { - public state: State; // State on which the Observer is watching + public state: () => State; // State on which the Observer is watching public nextStateValue: ValueType; // The next State value - constructor(agileInstance: Agile, state: State) { - super(agileInstance); - this.state = state; + constructor(agileInstance: Agile, state: State, deps?: Array, key?: ObserverKey) { + super(agileInstance, deps, key); + this.state = () => state; this.nextStateValue = state.value; this.value = state.value; - this.key = `o_${state.key}`; + this.hasValue = true; // States always have an value } @@ -47,19 +48,19 @@ export class StateObserver extends Observer { if (this.state instanceof Computed) this.nextStateValue = this.state.computeValue(); else - this.nextStateValue = this.state.nextState - } - this.nextStateValue = newStateValue; + this.nextStateValue = this.state().nextState + } else + this.nextStateValue = newStateValue; // Check if state value und newStateValue are the same.. if so return except force Rerender (stringifying because of possible object or array) - if (JSON.stringify(this.state.value) === JSON.stringify(this.nextStateValue) && !options.forceRerender) { + if (JSON.stringify(this.state().value) === JSON.stringify(this.nextStateValue) && !options.forceRerender) { if (this.agileInstance().config.logJobs) console.warn("Agile: Doesn't created job because state values are the same! "); return; } // Ingest into runtime - this.agileInstance().runtime.ingest(this, {}); + this.agileInstance().runtime.ingest(this, options); } @@ -74,17 +75,20 @@ export class StateObserver extends Observer { const state = job.observer.state; // Set Previous State - state.previousState = copy(state.value); + state().previousState = copy(state().value); // Write new value into the State - state.privateWrite(this.nextStateValue); + state().privateWrite(this.nextStateValue); // Set isSet - state.isSet = this.nextStateValue !== state.initialState; + state().isSet = this.nextStateValue !== state().initialState; // Set is placeholder to false, because it has got a value - if (state.isPlaceholder) - state.isPlaceholder = false; + if (state().isPlaceholder) + state().isPlaceholder = false; + + // Set Observer value + this.value = this.nextStateValue; // Perform SideEffects like watcher functions or state.sideEffects this.sideEffects(job); @@ -102,13 +106,15 @@ export class StateObserver extends Observer { const state = job.observer.state; // Call Watchers - for (let watcher in state.watchers) - if (typeof state.watchers[watcher] === 'function') - state.watchers[watcher](state.getPublicValue()); + for (let watcher in state().watchers) + if (typeof state().watchers[watcher] === 'function') + state().watchers[watcher](state().getPublicValue()); // Call State SideEffects - if (typeof state.sideEffects === 'function' && job.config?.sideEffects) - state.sideEffects(); + if (typeof state().sideEffects === 'function' && job.config?.sideEffects) + // @ts-ignore + state().sideEffects(); + // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) job.observer.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(internalIngestKey, {perform: false})); From b722194b98abb4b172a5e884a1c5ff142a570416 Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Fri, 9 Oct 2020 20:37:18 +0200 Subject: [PATCH 7/9] x --- packages/core/src/collection/group.ts | 13 +++ packages/core/src/computed/index.ts | 2 +- packages/core/src/internal.ts | 6 +- packages/core/src/runtime/dep.ts | 8 +- packages/core/src/runtime/index.ts | 83 +++++++++++-------- packages/core/src/runtime/job.ts | 10 +-- packages/core/src/runtime/observer.ts | 5 +- .../CallbackSubscriptionContainer.ts | 7 +- .../ComponentSubscriptionContainer.ts | 15 +--- .../subscription/SubscriptionContainer.ts | 15 ++++ packages/core/src/runtime/subscription/sub.ts | 50 +++++------ packages/core/src/state/index.ts | 5 +- packages/core/src/state/state.observer.ts | 2 +- 13 files changed, 120 insertions(+), 101 deletions(-) create mode 100644 packages/core/src/runtime/subscription/SubscriptionContainer.ts diff --git a/packages/core/src/collection/group.ts b/packages/core/src/collection/group.ts index a4e2829c..c883280f 100644 --- a/packages/core/src/collection/group.ts +++ b/packages/core/src/collection/group.ts @@ -226,4 +226,17 @@ export class Group extends State> { this._states = finalStates.map(state => (() => state)); this._output = finalOutput; } + + + //========================================================================================================= + // Get Public Value + //========================================================================================================= + /** + * @internal + * Returns the public value + */ + // @ts-ignore + public getPublicValue(): Array { + return this.output; + } } diff --git a/packages/core/src/computed/index.ts b/packages/core/src/computed/index.ts index 871a9d06..2fd99a77 100644 --- a/packages/core/src/computed/index.ts +++ b/packages/core/src/computed/index.ts @@ -85,7 +85,7 @@ export class Computed extends State const computedValue = this.computeFunction(); // Get tracked states and set trackSate to false - let foundObservers = this.agileInstance().runtime.getTrackedObserver(); + let foundObservers = this.agileInstance().runtime.getTrackedObservers(); // Handle foundStates dependencies const newDeps: Array = []; diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index 7add1d11..68eb523a 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -9,12 +9,15 @@ export * from './agile'; // Runtime +export * from './runtime'; export * from './runtime/observer'; export * from './runtime/dep'; export * from './runtime/job'; -export * from './runtime/subscription/sub'; +export * from './runtime/subscription/SubscriptionContainer'; export * from './runtime/subscription/CallbackSubscriptionContainer'; export * from './runtime/subscription/ComponentSubscriptionContainer'; +export * from './runtime/subscription/sub'; + // State export * from './state'; @@ -35,7 +38,6 @@ export * from './event'; // Internal Classes export * from './storage'; export * from './integrations'; -export * from './runtime'; // Utils export * from './utils'; \ No newline at end of file diff --git a/packages/core/src/runtime/dep.ts b/packages/core/src/runtime/dep.ts index c71c7bc3..dc119572 100644 --- a/packages/core/src/runtime/dep.ts +++ b/packages/core/src/runtime/dep.ts @@ -19,7 +19,7 @@ export class Dep { // Depend //========================================================================================================= /** - * Add new Dependency to the State + * Add new Dependency to the Observer */ public depend(observable: Observer) { if (observable.dep !== this && !this.deps.has(observable)) @@ -31,7 +31,7 @@ export class Dep { // Subscribe //========================================================================================================= /** - * Add new Subscription to the State + * Add new Subscription to the Observer */ public subscribe(subscriptionContainer: SubscriptionContainer){ if(!this.subs.has(subscriptionContainer)) @@ -43,10 +43,10 @@ export class Dep { // Unsubscribe //========================================================================================================= /** - * Delete Subscription from the State + * Delete Subscription from the Observer */ public unsubscribe(subscriptionContainer: SubscriptionContainer){ - if(!this.subs.has(subscriptionContainer)) + if(this.subs.has(subscriptionContainer)) this.subs.delete(subscriptionContainer); } } diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 311d654e..f36a74d9 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -4,16 +4,18 @@ import { defineConfig, Observer, Job, - JobConfigInterface + JobConfigInterface, + CallbackSubscriptionContainer, ComponentSubscriptionContainer, StateObserver } from '../internal'; -import {CallbackSubscriptionContainer} from "./subscription/CallbackSubscriptionContainer"; export class Runtime { + public agileInstance: () => Agile; // Queue system private currentJob: Job | null = null; private jobQueue: Array = []; + private notReadyJobsToRerender: Array = []; private jobsToRerender: Array = []; // Used for tracking computed dependencies @@ -25,7 +27,6 @@ export class Runtime { } public ingest(observer: Observer, options: JobConfigInterface): void { - // Merge default values into options options = defineConfig(options, { perform: true, background: false, @@ -44,10 +45,10 @@ export class Runtime { if (this.agileInstance().config.logJobs) console.log(`Agile: Created Job(${job.observer.key})`, job); - // Push the Job to the Queue (safety.. that no Job get forgotten) + // Push the Job to the Queue this.jobQueue.push(job); - // Perform the Job + // Perform the Job and remove it from queue if (options.perform) { const performJob = this.jobQueue.shift(); if (performJob) @@ -66,7 +67,6 @@ export class Runtime { * Perform a State Update */ private perform(job: Job): void { - // Set Job to currentJob this.currentJob = job; // Perform Job @@ -92,7 +92,6 @@ export class Runtime { } else { // https://stackoverflow.com/questions/9083594/call-settimeout-without-delay setTimeout(() => { - // Cause rerender on Subscribers this.updateSubscribers(); }); } @@ -104,46 +103,38 @@ export class Runtime { //========================================================================================================= /** * @internal - * This will be update all Subscribers of complete jobs + * Updates all Subscribers */ private updateSubscribers(): void { // Subscriptions that has to be updated const subscriptionsToUpdate: Set = new Set(); // Map through Jobs to Rerender - this.jobsToRerender.forEach(job => - // Map through subs of the current Job State + this.jobsToRerender.concat(this.notReadyJobsToRerender).forEach(job => job.observer.dep.subs.forEach(subscriptionContainer => { // Check if subscriptionContainer is ready - if (!subscriptionContainer.ready) - console.warn("Agile: SubscriptionContainer isn't ready yet ", subscriptionContainer); + if (!subscriptionContainer.ready) { + this.notReadyJobsToRerender.push(job); + if (this.agileInstance().config.logJobs) + console.warn("Agile: SubscriptionContainer isn't ready yet ", subscriptionContainer); + return; + } // For a Container that require props to be passed - if (subscriptionContainer.passProps) { - let localKey: string | null = null; + if (subscriptionContainer.passProps) + this.handlePassProps(subscriptionContainer, job); - // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object - for (let key in subscriptionContainer.propObservers) - if (subscriptionContainer.propObservers[key] === job.observer) - localKey = key; - - // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object - if (localKey) - subscriptionContainer.propKeysChanged.push(localKey); - } subscriptionsToUpdate.add(subscriptionContainer); - })); + }) + ); - // Perform Component or Callback updates + // Rerender the Components via CallbackSubscriptions or ComponentSubscription subscriptionsToUpdate.forEach(subscriptionContainer => { - // If Callback based subscription call the Callback Function - if (subscriptionContainer instanceof CallbackSubscriptionContainer) { + if (subscriptionContainer instanceof CallbackSubscriptionContainer) subscriptionContainer.callback(); - return; - } - // If Component based subscription call the updateMethod - this.agileInstance().integrations.update(subscriptionContainer.component, this.formatChangedPropKeys(subscriptionContainer)); + if (subscriptionContainer instanceof ComponentSubscriptionContainer) + this.agileInstance().integrations.update(subscriptionContainer.component, this.formatChangedPropKeys(subscriptionContainer)); }); // Log Job @@ -155,6 +146,26 @@ export class Runtime { } + //========================================================================================================= + // Handle Pass Props + //========================================================================================================= + /** + * @internal + * Handle prop passing subscription + */ + public handlePassProps(subscriptionContainer: SubscriptionContainer, job: Job) { + let localKey: string | null = null; + + // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object + for (let key in subscriptionContainer.subs) + if (subscriptionContainer.subs[key] === job.observer) + localKey = key; + + // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object + if (localKey) + subscriptionContainer.propKeysChanged.push(localKey); + } + //========================================================================================================= // Format Changed Prop Keys //========================================================================================================= @@ -167,8 +178,8 @@ export class Runtime { // Build Object subscriptionContainer.propKeysChanged.forEach(changedKey => { - if (subscriptionContainer.propObservers) - finalObject[changedKey] = subscriptionContainer.propObservers[changedKey].value; + if (subscriptionContainer.subs[changedKey] instanceof StateObserver) + finalObject[changedKey] = subscriptionContainer.subs[changedKey].value; }); return finalObject; @@ -182,13 +193,13 @@ export class Runtime { * @internal * Will return all tracked States */ - public getTrackedObserver() { - const finalFoundStates = this.foundObservers; + public getTrackedObservers(): Set { + const finalFoundObservers = this.foundObservers; // Reset tracking this.trackObserver = false; this.foundObservers = new Set(); - return finalFoundStates; + return finalFoundObservers; } } diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts index f48d9c46..557388d8 100644 --- a/packages/core/src/runtime/job.ts +++ b/packages/core/src/runtime/job.ts @@ -1,10 +1,10 @@ import {Observer, defineConfig} from "../internal"; export interface JobConfigInterface { - background?: boolean - sideEffects?: boolean - forceRerender?: boolean - perform?: boolean + background?: boolean // If it should cause an rerender + sideEffects?: boolean // If it should call sideEffects + forceRerender?: boolean // Force a rerender + perform?: boolean // If the Job should be performed } export class Job { @@ -15,14 +15,12 @@ export class Job { public performed: boolean = false; constructor(observer: ObserverType, config: JobConfigInterface) { - // Merge default values into options this.config = defineConfig(config, { background: false, sideEffects: true, forceRerender: false }); - this.observer = observer; this.config = config; diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts index 8d21a5c8..0a2f3ad8 100644 --- a/packages/core/src/runtime/observer.ts +++ b/packages/core/src/runtime/observer.ts @@ -9,10 +9,7 @@ export class Observer { public _key?: ObserverKey; public dep: Dep; // Dependencies and Subscriptions of the Observer - public hasValue: boolean = false; // Weather the Observer has an value or not - public value: any; // Value of the Observer if it has one - - constructor(agileInstance: Agile, deps?: Array, key?: ObserverKey) { + constructor(agileInstance: Agile, deps?: Array, key?: ObserverKey, value?: ValueType) { this.agileInstance = () => agileInstance; this.dep = new Dep(deps); this._key = key; diff --git a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts index c8698d52..ae1852b3 100644 --- a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts @@ -1,11 +1,10 @@ -import {Observer} from "../../internal"; -import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; +import {Observer, SubscriptionContainer} from "../../internal"; -export class CallbackSubscriptionContainer extends ComponentSubscriptionContainer { +export class CallbackSubscriptionContainer extends SubscriptionContainer{ public callback: Function; constructor(callback: Function, subs?: Set) { - super(null, subs); + super(subs); this.callback = callback; } diff --git a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts index 5895b3e9..0af4daff 100644 --- a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts @@ -1,19 +1,10 @@ -import {Observer} from "../../internal"; +import {Observer, SubscriptionContainer} from "../../internal"; -export class ComponentSubscriptionContainer { +export class ComponentSubscriptionContainer extends SubscriptionContainer{ public component: any; - // Only needed by object orientated subscriptions - public passProps: boolean = false; - public propObservers?: { [key: string]: Observer }; // states which will than be returned as prop object by the integration - public propKeysChanged: Array = []; // Used to preserve local keys to update before update is performed, cleared every update - - public ready: boolean = false; - public subs: Set = new Set([]); // States that are subscribed by this component - constructor(component: any, subs?: Set) { + super(subs); this.component = component - if (subs) - this.subs = subs; } } \ No newline at end of file diff --git a/packages/core/src/runtime/subscription/SubscriptionContainer.ts b/packages/core/src/runtime/subscription/SubscriptionContainer.ts new file mode 100644 index 00000000..53b9ce25 --- /dev/null +++ b/packages/core/src/runtime/subscription/SubscriptionContainer.ts @@ -0,0 +1,15 @@ +import {Observer} from "../../internal"; + +export class SubscriptionContainer { + + // For Object orientated subscriptions + public passProps: boolean = false; + public propKeysChanged: Array = []; // Used to preserve local keys to update before update is performed, cleared every update + + public ready: boolean = false; + public subs: Set = new Set([]); // Observers that are Subscribed to this Subscription Container + + constructor(subs?: Set) { + if (subs) this.subs = subs; + } +} \ No newline at end of file diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.ts index 176fc973..1c8cd2bf 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.ts @@ -1,9 +1,4 @@ -import {Agile, Observer} from '../../internal'; -import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; -import {CallbackSubscriptionContainer} from "./CallbackSubscriptionContainer"; - -export type SubscriptionContainer = ComponentSubscriptionContainer | CallbackSubscriptionContainer; - +import {Agile, Observer, StateObserver, SubscriptionContainer, ComponentSubscriptionContainer, CallbackSubscriptionContainer} from '../../internal'; //========================================================================================================= // Controller @@ -13,10 +8,10 @@ export class SubController { public agileInstance: () => Agile; // Component based Subscription - public components: Set = new Set(); + public componentSubs: Set = new Set(); // Callback based Subscription - public callbacks: Set = new Set(); + public callbackSubs: Set = new Set(); public constructor(agileInstance: Agile) { this.agileInstance = () => agileInstance; @@ -29,12 +24,11 @@ export class SubController { /** * Subscribe to Agile State with a returned object of props this props can than be returned by the component (See react-integration) */ - public subscribeWithSubsObject(subscriptionInstance: any, subs: { [key: string]: Observer } = {}): { subscriptionContainer: SubscriptionContainer, props: { [key: string]: Observer['value'] } } { + public subscribeWithSubsObject(subscriptionInstance: any, subs: { [key: string]: Observer } = {}): { subscriptionContainer: SubscriptionContainer, props: { [key: string]: StateObserver['value'] } } { const subscriptionContainer = this.registerSubscription(subscriptionInstance); const props: { [key: string]: Observer } = {}; subscriptionContainer.passProps = true; - subscriptionContainer.propObservers = {...subs}; // Go through subs let localKeys = Object.keys(subs); @@ -47,6 +41,9 @@ export class SubController { // Add SubscriptionContainer to State Subs observable.dep.subscribe(subscriptionContainer); + // If no state instance return, because only states have a 'value' + if(!(observable instanceof StateObserver)) return; + // Add state to props props[key] = observable.value; }); @@ -71,7 +68,7 @@ export class SubController { // Add State to SubscriptionContainer Subs subscriptionContainer.subs.add(observable); - // Add SubscriptionContainer to State Dependencies Subs + // Add SubscriptionContainer to State Subs observable.dep.subscribe(subscriptionContainer); }); @@ -119,20 +116,6 @@ export class SubController { } - //========================================================================================================= - // Mount - //========================================================================================================= - /** - * This will mount the component (Mounts currently only useful in Component based Subscription) - */ - public mount(integrationInstance: any) { - if (!integrationInstance.componentContainer) return; - - // Set Ready to true - integrationInstance.componentContainer.ready = true; - } - - //========================================================================================================= // Register Component Subscription //========================================================================================================= @@ -149,7 +132,7 @@ export class SubController { componentInstance.componentSubscriptionContainer = componentContainer; // Add to components - this.components.add(componentContainer); + this.componentSubs.add(componentContainer); // Set Ready if (!this.agileInstance().config.waitForMount) @@ -174,7 +157,7 @@ export class SubController { const callbackContainer = new CallbackSubscriptionContainer(callbackFunction as Function, new Set(subs)); // Add to callbacks - this.callbacks.add(callbackContainer); + this.callbackSubs.add(callbackContainer); // Set Ready callbackContainer.ready = true; @@ -184,4 +167,17 @@ export class SubController { return callbackContainer; } + + + + //========================================================================================================= + // Mount + //========================================================================================================= + /** + * This will mount the component (Mounts currently only useful in Component based Subscription) + */ + public mount(integrationInstance: any) { + if (!integrationInstance.componentContainer) return; + integrationInstance.componentContainer.ready = true; + } } diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 894b1caf..2ff80026 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -26,7 +26,6 @@ export class State { public sideEffects?: Function; // SideEffects can be set by extended classes, such as Groups to build their output. public isSet: boolean = false; // Has been changed from initial value public persistSettings: PersistSettingsInterface; // Includes persist 'settings' (have to rename if I got an better name) - public output?: any; // This contains the public value.. if _value doesn't contain the public value (Used for example by collections) public isPlaceholder: boolean = false; // Defines if the state is a placeholder or not public observer: StateObserver; @@ -292,11 +291,9 @@ export class State { //========================================================================================================= /** * @internal - * Returns 100% the public value of a state because at some points (group) the _value contains only keys + * Returns the public value (will be overwritten for instance in group) */ public getPublicValue(): ValueType { - if (this.output !== undefined) - return this.output; return this._value; } diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 0530c4b3..13ca69f8 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -16,13 +16,13 @@ export class StateObserver extends Observer { public state: () => State; // State on which the Observer is watching public nextStateValue: ValueType; // The next State value + public value: ValueType; constructor(agileInstance: Agile, state: State, deps?: Array, key?: ObserverKey) { super(agileInstance, deps, key); this.state = () => state; this.nextStateValue = state.value; this.value = state.value; - this.hasValue = true; // States always have an value } From d1a8654964fdc213585f6472b2c59baa4afd3a4a Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Fri, 9 Oct 2020 21:17:24 +0200 Subject: [PATCH 8/9] Fixed computed --- packages/core/src/state/index.ts | 17 +++++-------- packages/core/src/state/persist.ts | 10 ++++---- packages/core/src/state/state.observer.ts | 30 +++++++++++------------ 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 2ff80026..91038070 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -1,6 +1,5 @@ import { Agile, - Dep, StorageKey, copy, defineConfig, @@ -12,7 +11,7 @@ import {persistValue, updateValue} from './persist'; export type StateKey = string | number; -export interface PersistSettingsInterface { +export interface PersistConfigInterface { isPersisted: boolean // Is State persisted persistKey?: string | number // Current Persist Key.. for handling twice persisted states } @@ -25,7 +24,7 @@ export class State { public watchers: { [key: string]: (value: any) => void } = {}; public sideEffects?: Function; // SideEffects can be set by extended classes, such as Groups to build their output. public isSet: boolean = false; // Has been changed from initial value - public persistSettings: PersistSettingsInterface; // Includes persist 'settings' (have to rename if I got an better name) + public persistConfig: PersistConfigInterface; // Includes persist 'settings' (have to rename if I got an better name) public isPlaceholder: boolean = false; // Defines if the state is a placeholder or not public observer: StateObserver; @@ -41,7 +40,7 @@ export class State { this._value = initialState; this.previousState = initialState; this.nextState = initialState; - this.persistSettings = { + this.persistConfig = { isPersisted: false } this.observer = new StateObserver(agileInstance, this, deps.map(state => state.observer), key); @@ -117,11 +116,7 @@ export class State { forceRerender: false }); - this.observer.ingest(internalIngestKey, { - background: options.background, - sideEffects: options.sideEffects, - forceRerender: options.forceRerender - }); + this.observer.ingest(internalIngestKey, options); } @@ -167,8 +162,8 @@ export class State { */ public reset(): this { // Remove State from Storage (because it is than the initial State again and there is no need to save it anymore) - if (this.persistSettings.isPersisted && this.persistSettings.persistKey) - this.agileInstance().storage.remove(this.persistSettings.persistKey); + if (this.persistConfig.isPersisted && this.persistConfig.persistKey) + this.agileInstance().storage.remove(this.persistConfig.persistKey); // Set State to initial State this.set(this.initialState); diff --git a/packages/core/src/state/persist.ts b/packages/core/src/state/persist.ts index bbd23ba2..acf7fe89 100644 --- a/packages/core/src/state/persist.ts +++ b/packages/core/src/state/persist.ts @@ -24,8 +24,8 @@ export async function persistValue(state: State, key?: StorageKey) { const storage = state.agileInstance().storage; // Check if persist State is already a isPersistState if so remove the old one - if (state.persistSettings.isPersisted && state.persistSettings.persistKey) - storage.remove(state.persistSettings.persistKey); + if (state.persistConfig.isPersisted && state.persistConfig.persistKey) + storage.remove(state.persistConfig.persistKey); // Add State to persistedStates in Storage storage.persistedStates.add(state); @@ -34,7 +34,7 @@ export async function persistValue(state: State, key?: StorageKey) { await handleStorageValue(key, storage, state); // Set persistSettings - state.persistSettings = { + state.persistConfig = { isPersisted: true, persistKey: key } @@ -48,8 +48,8 @@ export async function persistValue(state: State, key?: StorageKey) { * Save current _value into storage if isPersistState */ export function updateValue(state: State) { - if (state.persistSettings.isPersisted && state.persistSettings.persistKey) - state.agileInstance().storage.set(state.persistSettings.persistKey, state._value); + if (state.persistConfig.isPersisted && state.persistConfig.persistKey) + state.agileInstance().storage.set(state.persistConfig.persistKey, state._value); } diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index 13ca69f8..87fdef58 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -45,8 +45,9 @@ export class StateObserver extends Observer { // Grab nextState if newState not passed or compute if needed if (newStateValue === internalIngestKey) { - if (this.state instanceof Computed) - this.nextStateValue = this.state.computeValue(); + if (this.state() instanceof Computed) + // @ts-ignore + this.nextStateValue = this.state().computeValue(); else this.nextStateValue = this.state().nextState } else @@ -72,20 +73,20 @@ export class StateObserver extends Observer { * TOD_O */ public perform(job: Job) { - const state = job.observer.state; + const state = job.observer.state(); // Set Previous State - state().previousState = copy(state().value); + state.previousState = copy(state.value); // Write new value into the State - state().privateWrite(this.nextStateValue); + state.privateWrite(this.nextStateValue); // Set isSet - state().isSet = this.nextStateValue !== state().initialState; + state.isSet = this.nextStateValue !== state.initialState; // Set is placeholder to false, because it has got a value - if (state().isPlaceholder) - state().isPlaceholder = false; + if (state.isPlaceholder) + state.isPlaceholder = false; // Set Observer value this.value = this.nextStateValue; @@ -103,17 +104,16 @@ export class StateObserver extends Observer { * SideEffects are sideEffects of the perform function.. for instance the watchers */ private sideEffects(job: Job) { - const state = job.observer.state; + const state = job.observer.state(); // Call Watchers - for (let watcher in state().watchers) - if (typeof state().watchers[watcher] === 'function') - state().watchers[watcher](state().getPublicValue()); + for (let watcher in state.watchers) + if (typeof state.watchers[watcher] === 'function') + state.watchers[watcher](state.getPublicValue()); // Call State SideEffects - if (typeof state().sideEffects === 'function' && job.config?.sideEffects) - // @ts-ignore - state().sideEffects(); + if (typeof state.sideEffects === 'function' && job.config?.sideEffects) + state.sideEffects(); // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) From a174cbca9409cb9807fec8bb1c1a31dee6aa809e Mon Sep 17 00:00:00 2001 From: Benno Kohrs Date: Fri, 9 Oct 2020 21:26:37 +0200 Subject: [PATCH 9/9] Fixed some tests --- packages/core/src/collection/group.ts | 1 - packages/core/tests/computed/default.spec.ts | 8 ++++-- .../updateComputeFunction.function.spec.ts | 14 +++++----- packages/core/tests/state/default.spec.ts | 2 +- .../state/functions/persist.function.spec.ts | 28 +++++++++---------- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/core/src/collection/group.ts b/packages/core/src/collection/group.ts index c883280f..7fe1dff4 100644 --- a/packages/core/src/collection/group.ts +++ b/packages/core/src/collection/group.ts @@ -65,7 +65,6 @@ export class Group extends State> { * Checks if the group contains the primaryKey */ public has(primaryKey: ItemKey) { - console.log(this.value); return this.value.findIndex(key => key === primaryKey) !== -1; } diff --git a/packages/core/tests/computed/default.spec.ts b/packages/core/tests/computed/default.spec.ts index b6ad74a5..84370142 100644 --- a/packages/core/tests/computed/default.spec.ts +++ b/packages/core/tests/computed/default.spec.ts @@ -47,7 +47,8 @@ describe('Default Computed Tests', () => { expect(MY_COMPUTED.key).to.eq(undefined, 'MY_COMPUTED has correct initial key'); expect(MY_COMPUTED.value).to.eq('hello_bye_jeff_hans', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE, MY_STATE_2, MY_SELECTOR, MY_COLLECTION.findById(2)]), 'MY_COMPUTED has correct deps'); + // @ts-ignore + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE.observer, MY_STATE_2.observer, MY_SELECTOR.observer, MY_COLLECTION.findById(2).observer]), 'MY_COMPUTED has correct deps'); expect(computedCallCount).to.eq(2, 'computedCallCount has correct initial value'); expect(rerenderCount).to.eq(0, 'rerenderCount has correct initial value'); }); @@ -120,8 +121,9 @@ describe('Default Computed Tests', () => { expect(MY_COMPUTED instanceof Computed).to.eq(true, 'MY_COMPUTED is computed'); expect(MY_COMPUTED.key).to.eq(undefined, 'MY_COMPUTED has correct initial key'); expect(MY_COMPUTED.value).to.eq('hello_bye_jeff_hans', 'MY_COMPUTED has correct value'); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_3]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3, MY_STATE, MY_STATE_2, MY_SELECTOR, MY_COLLECTION.findById(2)]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_3.observer]), 'MY_COMPUTED has correct hardCodedDeps'); + // @ts-ignore + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3.observer, MY_STATE.observer, MY_STATE_2.observer, MY_SELECTOR.observer, MY_COLLECTION.findById(2).observer]), 'MY_COMPUTED has correct deps'); expect(computedCallCount).to.eq(2, 'computedCallCount has correct initial value'); expect(rerenderCount).to.eq(0, 'rerenderCount has correct initial value'); }); diff --git a/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts b/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts index e047bcb2..92d58812 100644 --- a/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts +++ b/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts @@ -30,7 +30,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.key).to.eq(undefined, 'MY_COMPUTED has correct initial key'); expect(MY_COMPUTED.value).to.eq('hello_bye', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE, MY_STATE_2]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE.observer, MY_STATE_2.observer]), 'MY_COMPUTED has correct deps'); expect(myComputed).to.eq('hello_bye', 'myComputed has correct initial value'); expect(rerenderCount).to.eq(0, 'rerenderCount has correct initial value'); @@ -47,7 +47,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('jeff_hans', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3, MY_STATE_4]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3.observer, MY_STATE_4.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(1, 'rerenderCount has been increased by 1'); }); @@ -62,8 +62,8 @@ describe('updateComputeFunction Function test_integration', () => { await new Promise(resolve => setTimeout(resolve, 100)); expect(MY_COMPUTED.value).to.eq('bye_jeff', 'MY_COMPUTED has correct value'); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_4]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4, MY_STATE_2, MY_STATE_3]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_4.observer]), 'MY_COMPUTED has correct hardCodedDeps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4.observer, MY_STATE_2.observer, MY_STATE_3.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(2, 'rerenderCount has been increased by 1'); }); @@ -79,7 +79,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('bye_jeff', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_2, MY_STATE_3]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_2.observer, MY_STATE_3.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(2, 'rerenderCount stayed the same'); }); @@ -96,7 +96,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('hello_jeff', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE, MY_STATE_3]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE.observer, MY_STATE_3.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(3, 'rerenderCount has been increased by 1'); }); @@ -112,7 +112,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('hans_bye', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4, MY_STATE_2]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4.observer, MY_STATE_2.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(3, 'rerenderCount stayed the same'); }); diff --git a/packages/core/tests/state/default.spec.ts b/packages/core/tests/state/default.spec.ts index eac9f38e..51596d39 100644 --- a/packages/core/tests/state/default.spec.ts +++ b/packages/core/tests/state/default.spec.ts @@ -22,7 +22,7 @@ describe('Default State Tests', () => { expect(MY_STATE.initialState).to.eq('hello', 'MY_STATE has correct initialState'); expect(MY_STATE.exists).to.eq(true, 'MY_STATE has correct exists'); expect(MY_STATE.isSet).to.eq(false, 'MY_STATE has correct isSet'); - expect(MY_STATE.persistSettings.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); expect(MY_STATE.isPlaceholder).to.eq(false, 'MY_STATE has correct isPlaceholder'); expect(MY_STATE.valueType).to.eq(undefined, 'MY_STATE has correct valueType'); expect(MY_STATE.exists).to.eq(true, 'MY_STATE exists'); diff --git a/packages/core/tests/state/functions/persist.function.spec.ts b/packages/core/tests/state/functions/persist.function.spec.ts index a0c892be..288c4054 100644 --- a/packages/core/tests/state/functions/persist.function.spec.ts +++ b/packages/core/tests/state/functions/persist.function.spec.ts @@ -30,7 +30,7 @@ describe('Persist Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.persistSettings.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(false, 'MY_STATE isn\'t in persistedStates'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(false, 'MY_STATE isn\'t in persistedStates'); }); @@ -39,8 +39,8 @@ describe('Persist Function Tests', () => { // Persist State MY_STATE.persist(); - expect(MY_STATE.persistSettings.isPersisted).to.eq(false, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq(undefined, 'MY_STATE has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(false, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq(undefined, 'MY_STATE has correct persistKey'); expect(MY_STATE.key).to.eq(undefined, 'MY_STATE has correct key'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(false, 'MY_STATE isn\'t in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(undefined, 'MY_STATE isn\'t in storage'); @@ -53,8 +53,8 @@ describe('Persist Function Tests', () => { // Needs some time to persist value await new Promise(resolve => setTimeout(resolve, 100)); - expect(MY_STATE.persistSettings.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); expect(MY_STATE.key).to.eq('mySecondKey', 'MY_STATE key has been set to persistKey if no key is provided'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(true, 'MY_STATE isn\'t in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(1, 'MY_STATE is in storage'); @@ -65,8 +65,8 @@ describe('Persist Function Tests', () => { // Reset State MY_STATE.reset(); - expect(MY_STATE.persistSettings.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); expect(MY_STATE.key).to.eq('mySecondKey', 'MY_STATE has correct key'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(true, 'MY_STATE is in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(undefined, 'MY_STATE isn\'t in storage'); @@ -78,8 +78,8 @@ describe('Persist Function Tests', () => { // Reset State MY_STATE.set(5); - expect(MY_STATE.persistSettings.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq('mySecondKey', 'MY_STATE_WITH_KEY has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq('mySecondKey', 'MY_STATE_WITH_KEY has correct persistKey'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(true, 'MY_STATE_WITH_KEY is in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(5, 'MY_STATE_WITH_KEY is in storage and has been updated'); }); @@ -93,7 +93,7 @@ describe('Persist Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE_WITH_KEY.value).to.eq('hello', 'MY_STATE_WITH_KEY has correct value'); expect(MY_STATE_WITH_KEY.key).to.eq('myKey', 'MY_STATE_WITH_KEY has correct key'); - expect(MY_STATE_WITH_KEY.persistSettings.isPersisted).to.eq(false, 'MY_STATE_WITH_KEY has correct isPersistState'); + expect(MY_STATE_WITH_KEY.persistConfig.isPersisted).to.eq(false, 'MY_STATE_WITH_KEY has correct isPersistState'); expect(App.storage.persistedStates.has(MY_STATE_WITH_KEY)).to.eq(false, 'MY_STATE_WITH_KEY isn\'t in persistedStates'); expect(App.storage.get('myKey')).to.eq(undefined, 'MY_STATE_WITH_KEY isn\'t in storage'); }); @@ -105,8 +105,8 @@ describe('Persist Function Tests', () => { // Needs some time to persist value await new Promise(resolve => setTimeout(resolve, 100)); - expect(MY_STATE_WITH_KEY.persistSettings.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); - expect(MY_STATE_WITH_KEY.persistSettings.persistKey).to.eq('myKey', 'MY_STATE_WITH_KEY has correct persistKey'); + expect(MY_STATE_WITH_KEY.persistConfig.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); + expect(MY_STATE_WITH_KEY.persistConfig.persistKey).to.eq('myKey', 'MY_STATE_WITH_KEY has correct persistKey'); expect(App.storage.persistedStates.has(MY_STATE_WITH_KEY)).to.eq(true, 'MY_STATE_WITH_KEY is in persistedStates'); expect(App.storage.get('myKey')).to.eq('hello', 'MY_STATE_WITH_KEY is in storage'); }); @@ -118,8 +118,8 @@ describe('Persist Function Tests', () => { // Needs some time to persist value await new Promise(resolve => setTimeout(resolve, 100)); - expect(MY_STATE_WITH_KEY.persistSettings.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); - expect(MY_STATE_WITH_KEY.persistSettings.persistKey).to.eq('myThirdKey', 'MY_STATE_WITH_KEY has correct persistKey'); + expect(MY_STATE_WITH_KEY.persistConfig.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); + expect(MY_STATE_WITH_KEY.persistConfig.persistKey).to.eq('myThirdKey', 'MY_STATE_WITH_KEY has correct persistKey'); expect(MY_STATE_WITH_KEY.key).to.eq('myKey', 'MY_STATE_WITH_KEY has correct key'); expect(App.storage.persistedStates.has(MY_STATE_WITH_KEY)).to.eq(true, 'MY_STATE_WITH_KEY is in persistedStates'); expect(App.storage.get('myThirdKey')).to.eq('hello', 'MY_STATE_WITH_KEY with new key is in storage');