diff --git a/ActionStrategy.md b/ActionStrategy.md index 7b5577e..e7cf086 100644 --- a/ActionStrategy.md +++ b/ActionStrategy.md @@ -120,7 +120,7 @@ SomethingFactory> This was a purposeful design choice, if you find yourself doing such. Known this system is already complicated enough. ## Helper Functions for Standard Method Creators -*Note you still need to create a function of MethodCreator to use these Helpers* +*You still need to create a function of type MethodCreator to use these Helpers. :MethodCreator = () => methodCreator* ```typescript export const createMethod = (method: (action: Action) => Action): [Method, Subject] => {} @@ -146,7 +146,8 @@ export const createAsyncMethodDebounceWithConcepts = * createMethodWithConcepts - This will allow your method to have the most recent concepts to be accessed via the asyncMethod function. * createAsyncMethod - Handled differently than the rest, you will have to use the passed controller to fire your actions back into the action stream. * createAsyncMethodWithConcepts - Will also have access to the most recent concepts. +*Note if you are implementing your own debounceAction how these methods work. They are handling a passed conclude from debounceAction within their map/switchMap* * createMethodDebounce - After the first action, this will filter actions within the duration to be set to the conclude action. -* createMethodDebounceWithConcepts - Will filter actions within the duration while providing access to the most recent concepts. +* createMethodDebounceWithConcepts - Will filter actions within the duration while providing access to the most recent concepts. * createAsyncMethodDebounce - Will not disengage the initial ActionController, but will allow debounced actions to pass through when filtered as conclude actions. And will fire the first action upon its own conditions are met asynchronously. * createAsyncMethodDebounceWithConcepts - Filters and then first the first action once conditions are met, and provides access to the most recent concepts. \ No newline at end of file diff --git a/Axium.md b/Axium.md index ecb660b..503312c 100644 --- a/Axium.md +++ b/Axium.md @@ -94,6 +94,7 @@ Please avoid using these qualities, but are providing explanations to understand * clearBadActionTypeFromBadActionList - This is to allow for plans to take into account for expired actions and clear such. * clearBadStrategyTopicFromBadActionList - Allows plans to accounts for specific ActionStrategy topics that might find themselves in badActions and clear such. * clearBadPlanFromBadPlanList - This additionally allows for concepts to take into account potentially failed plans that are set by axium.stage(). Via their topic as payload and clears such. +* kick - This is a pure action that will just trigger the next function via the UnifiedSubject to prime subscribers or stages. Noting that the downside of STRX's halting quality, is you have to kick it into gear if it hasn't received an action recently for your staged Plans to operate as intended. ## Axium Strategies Concept Set Transformation ```typescript diff --git a/src/concepts/axium/axium.concept.ts b/src/concepts/axium/axium.concept.ts index e60379a..2eb8cf2 100644 --- a/src/concepts/axium/axium.concept.ts +++ b/src/concepts/axium/axium.concept.ts @@ -26,6 +26,7 @@ import { clearBadActionTypeFromBadActionListQuality } from './qualities/clearBad import { clearBadStrategyTopicFromBadActionListQuality } from './qualities/clearBadStrategyTopicFromBadActionList.quality'; import { clearBadPlanFromBadPlanListQuality } from './qualities/clearBadPlanFromBadPlanList.quality'; import { registerStagePlannerQuality } from './qualities/registerStagePlanner.quality'; +import { kickQuality } from './qualities/kick.quality'; export type NamedSubscription = { name: string; @@ -92,6 +93,7 @@ export const createAxiumConcept = (name: string, storeDialog?: boolean, logging? axiumName, createAxiumState(name, storeDialog, logging), [ + kickQuality, openQuality, badActionQuality, closeQuality, diff --git a/src/concepts/axium/qualities/kick.quality.ts b/src/concepts/axium/qualities/kick.quality.ts new file mode 100644 index 0000000..7096fe7 --- /dev/null +++ b/src/concepts/axium/qualities/kick.quality.ts @@ -0,0 +1,12 @@ +import { defaultMethodCreator, defaultReducer } from '../../../model/concept'; +import { ActionType, prepareActionCreator } from '../../../model/action'; +import { createQuality } from '../../../model/concept'; + +export const axiumKickType: ActionType = 'Kick Axium'; +export const axiumKick = prepareActionCreator(axiumKickType); + +export const kickQuality = createQuality( + axiumKickType, + defaultReducer, + defaultMethodCreator +); diff --git a/src/concepts/counter/qualities/add.quality.ts b/src/concepts/counter/qualities/add.quality.ts index 5eeb3ae..7690d68 100644 --- a/src/concepts/counter/qualities/add.quality.ts +++ b/src/concepts/counter/qualities/add.quality.ts @@ -3,11 +3,18 @@ import { defaultMethodCreator, Method, MethodCreator } from '../../../model/conc import { Counter } from '../counter.concept'; import { createQuality } from '../../../model/concept'; import { counterSelectCount } from '../counter.selector'; +// import { createMethod } from '../../../model/method'; +// import { strategySuccess } from '../../../model/actionStrategy'; export const counterAddType: ActionType = 'Counter Add'; export const counterAdd = prepareActionCreator(counterAddType); - +// const createAddMethodCreator: MethodCreator = () => createMethod((action) => { +// if (action.strategy) { +// return strategySuccess(action.strategy); +// } +// return action; +// }); export function addReducer(state: Counter, _: Action) { return { ...state, diff --git a/src/index.ts b/src/index.ts index 25ab32c..fd4da28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -62,6 +62,7 @@ export { AxiumState, axiumName, createAxiumConcept } from './concepts/axium/axiu export { blockingMode, permissiveMode } from './concepts/axium/axium.mode'; export { axiumSelectOpen, axiumSelectLastStrategy, axiumSelectBadActions, axiumSelectBadPlans } from './concepts/axium/axium.selector'; // Qualities +export { axiumKick, axiumKickType } from './concepts/axium/qualities/kick.quality'; export { axiumConclude, axiumConcludeType } from './concepts/axium/qualities/conclude.quality'; export { axiumOpen, axiumOpenType } from './concepts/axium/qualities/open.quality'; export { axiumLog, axiumLogType } from './concepts/axium/qualities/log.quality'; diff --git a/src/model/actionController.ts b/src/model/actionController.ts index f07b30f..abbc38a 100644 --- a/src/model/actionController.ts +++ b/src/model/actionController.ts @@ -1,4 +1,4 @@ -import { Action, ActionStrategy, axiumBadAction, axiumConclude, axiumConcludeType, strategyFailed } from '../index'; +import { Action, axiumBadAction, strategyFailed } from '../index'; import { Subject } from 'rxjs'; import { failureConditions, strategyData_appendFailure } from './actionStrategyData'; @@ -20,12 +20,6 @@ export class ActionController extends Subject { strategyData_appendFailure(this.action.strategy, failureConditions.controllerExpired) )); } else { - const badAction = axiumBadAction([this.action]); - if (this.action.strategy) { - badAction.strategy = this.action.strategy; - (badAction.strategy as ActionStrategy).currentNode.action = badAction; - (badAction.strategy as ActionStrategy).currentNode.actionType = badAction.type; - } this.next(axiumBadAction([this.action])); } }, this.expiration - Date.now()); @@ -46,35 +40,12 @@ export class ActionController extends Subject { clearTimeout(this.timer); this.timer.unref(); } - let nextAction; - let end = true; - // Logically Determined axiumConclude - if (action.semaphore[3] === 3) { - end = false; - } - if (action.strategy) { - nextAction = action; - // Logically Determined axiumConclude - } else if (action.semaphore[3] === 3) { - nextAction = action; - // Logically Determined axiumBadAction - } else if (!action.strategy && action.semaphore[3] !== 1) { - const conclude = axiumConclude(); - nextAction = { - ...action, - ...conclude - }; - } else { - nextAction = action; - } const { observers } = this; const len = observers.length; for (let i = 0; i < len; i++) { - observers[i].next(nextAction); - } - if (end) { - this.complete(); + observers[i].next(action); } + this.complete(); } } } diff --git a/src/model/debounceAction.ts b/src/model/debounceAction.ts index 7739208..5ff146c 100644 --- a/src/model/debounceAction.ts +++ b/src/model/debounceAction.ts @@ -142,12 +142,12 @@ export function debounceAction(dueTime: number, scheduler: SchedulerLike = async subscriber.add(activeTask); } else { // All this code just to place this code block. - const conclude = axiumConclude(); + const conclude = { + ...value, + ...axiumConclude(), + }; subscriber.next( - { - ...value, - ...conclude, - } + conclude ); } }, diff --git a/src/model/method.ts b/src/model/method.ts index 65f1161..e732745 100644 --- a/src/model/method.ts +++ b/src/model/method.ts @@ -20,9 +20,6 @@ type Action = { }; type Method = Observable; - - - export const createMethod = (method: (action: Action) => Action): [Method, Subject] => { const defaultSubject = new Subject(); @@ -134,9 +131,17 @@ export const createAsyncMethodDebounce = const defaultSubject = new Subject(); const defaultMethod: Method = defaultSubject.pipe( debounceAction(duration), - switchMap(act => createActionController$(act, (controller: ActionController, action: Action) => { - asyncMethod(controller, action); - })), + switchMap((act) => { + if (act.semaphore[3] !== 3) { + return createActionController$(act, (controller: ActionController, action: Action) => { + asyncMethod(controller, action); + }); + } else { + return createActionController$(act, (controller: ActionController, _) => { + controller.fire(act); + }); + } + }), ); return [defaultMethod, defaultSubject]; }; @@ -147,10 +152,17 @@ export const createAsyncMethodDebounceWithConcepts = const defaultMethod: Method = defaultSubject.pipe( debounceAction(duration), withLatestFrom(concepts$ as UnifiedSubject), - switchMap(([act, concepts] : [Action, Concept[]]) => - createActionController$(act, (controller: ActionController, action: Action) => { - asyncMethodWithConcepts(controller, action, concepts); - })), + switchMap(([act, concepts] : [Action, Concept[]]) => { + if (act.semaphore[3] !== 3) { + return createActionController$(act, (controller: ActionController, action: Action) => { + asyncMethodWithConcepts(controller, action, concepts); + }); + } else { + return createActionController$(act, (controller: ActionController, _) => { + controller.fire(act); + }); + } + }) ); return [defaultMethod, defaultSubject]; }; \ No newline at end of file diff --git a/src/model/stagePlanner.ts b/src/model/stagePlanner.ts index d1690b5..8590cf8 100644 --- a/src/model/stagePlanner.ts +++ b/src/model/stagePlanner.ts @@ -196,9 +196,9 @@ export class UnifiedSubject extends Subject { super(); } stage(title: string, stages: Staging[]): StagePlanner { - this.currentStages.set(this.planId, {title, stages, stage: 0, stageFailed: -1}); const planId = this.planId; this.planId++; + this.currentStages.set(planId, {title, stages, stage: 0, stageFailed: -1}); const conclude = () => { this.currentStages.delete(planId); }; diff --git a/src/test/actionController.test.ts b/src/test/actionController.test.ts index 39967c3..80bd184 100644 --- a/src/test/actionController.test.ts +++ b/src/test/actionController.test.ts @@ -29,7 +29,7 @@ test('ActionController Next Test', (done) => { const act = axiumLog(undefined, 200); const cont = new ActionController(act); cont.subscribe(action => { - expect(action.type).toBe(axiumConcludeType); + expect(action.type).toBe(axiumLogType); done(); }); cont.fire(act); @@ -41,7 +41,7 @@ test('ActionController createActionController$ Test', (done) => { controller.fire(action); }); cont.subscribe(action => { - expect(action.type).toBe(axiumConcludeType); + expect(action.type).toBe(axiumLogType); done(); }); }); diff --git a/src/test/debounceMethods.test.ts b/src/test/debounceMethods.test.ts index 46e7ae0..25b62c4 100644 --- a/src/test/debounceMethods.test.ts +++ b/src/test/debounceMethods.test.ts @@ -1,3 +1,4 @@ +import { axiumKick } from '../concepts/axium/qualities/kick.quality'; import { Counter, counterName, createCounterConcept } from '../concepts/counter/counter.concept'; import { createExperimentConcept, createExperimentState } from '../concepts/experiment/experiment.concept'; import { asyncDebounceNextActionNodeQuality } from '../concepts/experiment/qualities/debounceAsyncNextActionNode.quality'; @@ -8,7 +9,6 @@ import { strategyBegin } from '../model/actionStrategy'; import { createAxium } from '../model/axium'; import { selectState } from '../model/selector'; -jest.setTimeout(10000); test('Debounce method prevent excess count', (done) => { const experiment = createExperimentConcept(createExperimentState(), [debounceNextActionNodeQuality]); const axium = createAxium('Experiment async method creator with Concepts', [createCounterConcept(), experiment]); @@ -64,14 +64,14 @@ test('Async debounce method prevent excess count', (done) => { const counterState = selectState(concepts, counterName); console.log('Async Debounce HIT 4', counterState); if (counterState.count === 1) { - console.log('Async Debounce HIT 4', counterState); + console.log('FINAL Async Debounce HIT 4', counterState); expect(counterState.count).toBe(1); plan.conclude(); } } ]); setTimeout(() => { - const secondPlan = axium.stage('timed mock to true', [ + const secondPlan = axium.stage('second timed mock', [ (_, dispatch) => { dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { iterateStage: true @@ -89,9 +89,9 @@ test('Async debounce method prevent excess count', (done) => { }, (concepts, _) => { const counterState = selectState(concepts, counterName); - console.log('Async Plan 2 Debounce HIT 4', counterState); + console.log('Async 2 Debounce HIT 4', counterState); if (counterState.count === 2) { - console.log('Async Plan 2 Debounce HIT 4', counterState); + console.log('FINAL Async 2 Debounce HIT 4', counterState); expect(counterState.count).toBe(2); secondPlan.conclude(); setTimeout(() => { @@ -100,5 +100,8 @@ test('Async debounce method prevent excess count', (done) => { } } ]); - }, 600); + // Axium must be primed, therefore we kick it back into gear. + // Downside of halting quality. + axium.dispatch(axiumKick()); + }, 1000); }); \ No newline at end of file