diff --git a/README.md b/README.md index 6938882..626be4c 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ const axium = createAxium('ownershipTest', [ createCounterConcept(), createExperimentConcept(createExperimentActionQueState(), [checkInQuality], [experimentActionQuePrinciple]) ], true, true); -const staged = axium.stage( +const plan = axium.stage( 'Testing Ownership Staging', [ (cpts, dispatch) => { const axiumState = cpts[0].state as AxiumState; diff --git a/Stage.md b/Stage.md index ba66c62..5aa9286 100644 --- a/Stage.md +++ b/Stage.md @@ -1,13 +1,13 @@ ## Stage ### Abstract -This is derived from the newly created UnifiedSubject to handle the main point of vulnerability that a recursive machine carries. As the main point of dispatching new actions in the system would traditionally be informed via the subscription to listen to state changes. This Design Pattern allows one to safely dispatch in a tightly patterned subscription. This design pattern watches each stage for the potential of a runaway configuration which would normally prevent this machine from halting. But since the Unified Turing Machine was created to be halting complete. The UnifiedSubject internally watches each stage of your application independently and the actions that it dispatches via the supplied dispatch function. If a similar action is dispatched in rapid Succession denoted by its type and no debounce option. That Stage will close and be added to the axium's badPlans property. +This is derived from the newly created UnifiedSubject to handle the main point of vulnerability that a recursive machine carries. As the main point of dispatching new actions in the system would traditionally be informed via the subscription to listen to state changes. This Design Pattern allows one to safely dispatch in a tightly patterned subscription. This design pattern watches each stage for the potential of a runaway configuration which would normally prevent this machine from halting. But since the Unified Turing Machine was created to be halting complete. The UnifiedSubject internally watches each stage of your application independently and the actions that it dispatches via the supplied dispatch function. If a similar action is dispatched in rapid Succession denoted by its type and no debounce option. That plan will conclude and be added to the axium's badPlans property. Once attached to the badPlan property, it would be possible to reinitialize said stage via your concept's principle utilizing the stage's title. But places that burden of responsibility on the developer. As the scope of a Unified Turing Machine is to be designed to specification and halt appropriately. We accept failure as likewise the ability to halt. -"You stage a plan and a plan has multiple stages." +*"You stage a plan and a plan has multiple stages."* ## Working with the Stage Paradigm -The added benefit of the creation of a plan to control the flow of actions. Allows the ability to create a series of stages to handle how the dispatch would be handled within a subscription, but with the added benefit of iterating through each stage your plan. A typical plan would typically be composed of an initialization, main run time, and likewise the ability to close. 3 acts if you will. +The added benefit of the creation of a plan to control the flow of actions. Allows the ability to create a series of stages to handle how the dispatch would be handled within a subscription, but with the added benefit of iterating through each stage your plan. A typical plan would typically be composed of an initialization, main run time, and likewise the ability to conclude. 3 acts if you will. ```typescript // Multiple Stages are a Plan export type Plan = { @@ -41,8 +41,7 @@ export type dispatchOptions = { * incrementStage - Will increment to the next stage index, this should be your default option for dispatching actions or strategies to prevent action overflow. * setStage - This will set the stage to a specific stage index, useful if some strategy failed and the staging needs to be reset to prepare for that strategy again. This will always override iterateStage. * on - Simple handler that will prevent dispatch until the selected value is set to what is expected. Keep in mind this should also be occupied by a debounce, as this dispatch will run on each successful state update. This should be utilized alongside iterateStage, setStage, or debounce to prevent action overflow. - -*Note* To prevent action overflow, each stage is paying attention to consecutive actions. + ### Internals ```typescript export type Dispatcher = (action: Action, options: dispatchOptions) => void; @@ -50,7 +49,6 @@ export type Staging = ( concepts: Concept[], dispatch: (action: Action, options: dispatchOptions) => void ) => void; - export class UnifiedSubject extends Subject { stage(title: string, stages: Staging[]) {} } @@ -69,14 +67,14 @@ const sub = axium.subscribe((concepts) => { const badPlan = axiumState.badPlans[0]; const counter = selectState(concepts, counterName); console.log('Stage Ran Away, badPlans.length: ', axiumState.badPlans.length, 'Count: ', counter.count); - staged.close(); + plan.conclude(); sub.unsubscribe(); expect(badPlan.stageFailed).toBe(2); expect(counter.count).toBe(2); setTimeout(() => {done();}, 500); } }); -const staged = axium.stage('Stage DispatchOptions Test', +const plan = axium.stage('Stage DispatchOptions Test', [ (concepts, dispatch) => { const counter = selectState(concepts, counterName); @@ -107,7 +105,7 @@ const staged = axium.stage('Stage DispatchOptions Test', runCount++; const counter = selectState(concepts, counterName); console.log('Should run twice, Stage 3 ', counter, runCount); - // Will cause an action overflow forcing the stage to close and add itself to badPlans + // Will cause an action overflow forcing the current stage to conclude and add the plan to badPlans dispatch(counterSubtract(), { // Enabling will cause this test to timeout via the subscription watching for badPlans to never be ran. // debounce: 500 @@ -115,11 +113,32 @@ const staged = axium.stage('Stage DispatchOptions Test', }); // This dispatch will be invalidated and never dispatched due to the effect of action overflow of the above. dispatch(counterAdd(), {}); - console.log('Should run twice. 1st will be before "Stage Ran Away," and 2nd will be final console log output.'); + console.log( + 'Will also run twice. 1st will be before "Stage Ran Away,"', + 'and after "Should run twice." The 2nd will be final console log output.' + ); } ]); ``` +To prevent action overflow, each stage is paying attention to consecutive actions of the same type. In an action overflow state, sequentially the overflow will call the same dispatch before called the next dispatch even if within the same stage. Keep in mind behind the scenes during a STRX runtime, there will be multiple strategies running concurrently. Observe the runCount specified in this example. Please look to the STRX's tests folder. -As well that STRX is designed to be run primarily through the loaded concepts and their associated principles. To prevent unexpected behaviors in your own principles. Please utilize the supplied KeyedSelector for axium's open property to begin the stage of your concepts. \ No newline at end of file +## Stage within your Principle +STRX is designed to be ran primarily through its loaded concepts and their associated principles. To prevent unexpected behaviors in your own principles. Please utilize the supplied KeyedSelector for axium's open property to begin the stage of your concepts. +```typescript +const plan = concept$.stage('Principle Stage Example', [ + (___, dispatch) => { + dispatch(someAction(), { + iterateStage: true, + on: { + selector: axiumSelectOpen, + expected: true + }, + }); + }, + (concepts, dispatch) => { + // Your principle's run time logic. + } +]); +``` diff --git a/src/model/unifiedSubject.ts b/src/model/unifiedSubject.ts index f7c934c..7855c94 100644 --- a/src/model/unifiedSubject.ts +++ b/src/model/unifiedSubject.ts @@ -143,11 +143,11 @@ export class UnifiedSubject extends Subject { this.currentStages.set(this.stageId, {title, stages, stage: 0, stageFailed: -1}); const stageId = this.stageId; this.stageId++; - const close = () => { + const conclude = () => { this.currentStages.delete(stageId); }; return { - close: close.bind(this) + conclude: conclude.bind(this) }; } diff --git a/src/test/addConcepts.test.ts b/src/test/addConcepts.test.ts index 15a2878..73ecea7 100644 --- a/src/test/addConcepts.test.ts +++ b/src/test/addConcepts.test.ts @@ -9,7 +9,7 @@ import { countingTopic } from '../concepts/counter/strategies/counting.strategy' test('Axium add Concepts Strategy Test', (done) => { const axium = createAxium('axiumAddConceptTest',[], true, true); - const staged = axium.stage('Add Concepts Stage',[ + const plan = axium.stage('Add Concepts Stage',[ (concepts, dispatch) => { dispatch( strategyBegin( @@ -36,7 +36,7 @@ test('Axium add Concepts Strategy Test', (done) => { const counter = selectState(concepts, counterName); expect(counter.count).toBe(1); setTimeout(() => {done();}, 500); - staged.close(); + plan.conclude(); } } ]); diff --git a/src/test/ownership.test.ts b/src/test/ownership.test.ts index 083a958..6e51867 100644 --- a/src/test/ownership.test.ts +++ b/src/test/ownership.test.ts @@ -26,7 +26,7 @@ test('Ownership Test', (done) => { createCounterConcept(), createExperimentConcept(createExperimentActionQueState(), [checkInQuality], [experimentActionQuePrinciple]) ], true, true); - const staged = axium.stage( + const plan = axium.stage( 'Testing Ownership Staging', [ (cpts, dispatch) => { const axiumState = cpts[0].state as AxiumState; @@ -70,7 +70,7 @@ test('Ownership Test', (done) => { expect(counter.count).toBe(3); // Comment in if testing the halting ability of log and setCount stage is commented out. // setTimeout(() => {done();}, 1000); - staged.close(); + plan.conclude(); } else if ( (axiumState.lastStrategy === experimentCountingTopic || axiumState.lastStrategy === experimentPrimedCountingTopic) && diff --git a/src/test/removeConcepts.test.ts b/src/test/removeConcepts.test.ts index e6f02c9..a0e5dcc 100644 --- a/src/test/removeConcepts.test.ts +++ b/src/test/removeConcepts.test.ts @@ -11,7 +11,7 @@ import { AxiumState } from '../concepts/axium/axium.concept'; test('Axium remove Concepts Strategy Test', (done) => { const axium = createAxium('axiumRemoveConceptsTest', [createCounterConcept()], true, true); - const staged = axium.stage('Remove Concepts Stage',[ + const plan = axium.stage('Remove Concepts Stage',[ (concepts, dispatch) => { dispatch( strategyBegin( @@ -33,7 +33,7 @@ test('Axium remove Concepts Strategy Test', (done) => { }); expect(exists).toBe(false); setTimeout(() => {done();}, 500); - staged.close(); + plan.conclude(); } } ]); diff --git a/src/test/stageDispatchOptions.test.ts b/src/test/stageDispatchOptions.test.ts index 4bd3e36..9e7e7d7 100644 --- a/src/test/stageDispatchOptions.test.ts +++ b/src/test/stageDispatchOptions.test.ts @@ -15,14 +15,14 @@ test('Axium Stage Dispatch Options Test', (done) => { const badPlan = axiumState.badPlans[0]; const counter = selectState(concepts, counterName); console.log('Stage Ran Away, badPlans.length: ', axiumState.badPlans.length, 'Count: ', counter.count); - staged.close(); + plan.conclude(); sub.unsubscribe(); expect(badPlan.stageFailed).toBe(2); expect(counter.count).toBe(2); setTimeout(() => {done();}, 500); } }); - const staged = axium.stage('Stage DispatchOptions Test', + const plan = axium.stage('Stage DispatchOptions Test', [ (concepts, dispatch) => { const counter = selectState(concepts, counterName); @@ -61,7 +61,10 @@ test('Axium Stage Dispatch Options Test', (done) => { }); // This dispatch will be invalidated and never dispatched due to the effect of action overflow of the above. dispatch(counterAdd(), {}); - console.log('Should run twice. 1st will be before "Stage Ran Away," and 2nd will be final console log output.'); + console.log( + 'Will also run twice. 1st will be before "Stage Ran Away,"', + 'and after "Should run twice." The 2nd will be final console log output.' + ); } ]); }); \ No newline at end of file diff --git a/src/test/stagedPrinciple.test.ts b/src/test/stagedPrinciple.test.ts index 53b85f5..6321c1f 100644 --- a/src/test/stagedPrinciple.test.ts +++ b/src/test/stagedPrinciple.test.ts @@ -26,7 +26,7 @@ const experimentMockToTrueQuality = createQuality(experimentMockToTrueType, expe test('Axium Principle Stage', (done) => { const experimentPrinciple: PrincipleFunction = (_: Subscriber, __: Concept[], concept$: UnifiedSubject) => { - const stage = concept$.stage('Experiment Principle', [ + const plan = concept$.stage('Experiment Principle', [ (___, dispatch) => { dispatch(experimentMockToTrue(), { iterateStage: true, @@ -41,7 +41,7 @@ test('Axium Principle Stage', (done) => { if (experimentState.mock) { expect(experimentState.mock).toBe(true); setTimeout(() => done(), 1000); - stage.close(); + plan.conclude(); } } ]); diff --git a/src/test/strategy.test.ts b/src/test/strategy.test.ts index a47b67a..dbab7a7 100644 --- a/src/test/strategy.test.ts +++ b/src/test/strategy.test.ts @@ -8,7 +8,7 @@ import { countingTopic } from '../concepts/counter/strategies/counting.strategy' test('Axium Counting Strategy Test', (done) => { const axium = createAxium('axiumStrategyTest', [createCounterConcept()], true, true); - const staged = axium.stage('Counting Strategy Stage', + const plan = axium.stage('Counting Strategy Stage', [ (_, dispatch) => { dispatch(strategyBegin(countingStrategy()), { @@ -20,7 +20,7 @@ test('Axium Counting Strategy Test', (done) => { const counter = selectState(concepts, counterName); expect(counter.count).toBe(1); setTimeout(() => {done();}, 500); - staged.close(); + plan.conclude(); } } ]);