Skip to content

Commit

Permalink
feat(Dispatcher): Introduce DispatcherPayloadMeta
Browse files Browse the repository at this point in the history
- Add DispatcherPayloadMeta.js
- Dispatcher User dispatch `payload` and `meta`.

refs #61
  • Loading branch information
azu committed Nov 20, 2016
1 parent 8655902 commit d123fbf
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 167 deletions.
35 changes: 17 additions & 18 deletions src/Context.js
Expand Up @@ -25,7 +25,7 @@ export default class Context {
* @param {QueuedStoreGroup|StoreGroup|Store} store store is either Store or StoreGroup
* @public
*/
constructor({dispatcher, store}) {
constructor({ dispatcher, store }) {
StoreGroupValidator.validateInstance(store);
// central dispatcher
this._dispatcher = dispatcher;
Expand Down Expand Up @@ -82,13 +82,13 @@ export default class Context {

/**
* called the {@link handler} with useCase when the useCase will do.
* @param {function(useCase: UseCase, args: *)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @public
*/
onWillExecuteEachUseCase(handler) {
const releaseHandler = this._dispatcher.onDispatch(payload => {
const releaseHandler = this._dispatcher.onDispatch((payload, meta) => {
if (payload.type === ActionTypes.ON_WILL_EXECUTE_EACH_USECASE) {
handler(payload.useCase, payload.args);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
Expand All @@ -100,16 +100,16 @@ export default class Context {
* This `onDispatch` is not called at built-in event. It is filtered by Context.
* If you want to *All* dispatched event and use listen directly your `dispatcher` object.
* In other word, listen the dispatcher of `new Context({dispatcher})`.
* @param {function(payload: DispatcherPayload)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @returns {Function}
* @public
*/
onDispatch(handler) {
const releaseHandler = this._dispatcher.onDispatch(payload => {
const releaseHandler = this._dispatcher.onDispatch((payload, meta) => {
// call handler, if payload's type is not built-in event.
// It means that `onDispatch` is called when dispatching user event.
if (ActionTypes[payload.type] === undefined) {
handler(payload);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
Expand All @@ -118,13 +118,13 @@ export default class Context {

/**
* called the `handler` with useCase when the useCase is executed..
* @param {function(useCase: UseCase)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @public
*/
onDidExecuteEachUseCase(handler) {
const releaseHandler = this._dispatcher.onDispatch(payload => {
const releaseHandler = this._dispatcher.onDispatch((payload, meta) => {
if (payload.type === ActionTypes.ON_DID_EXECUTE_EACH_USECASE) {
handler(payload.useCase);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
Expand All @@ -133,31 +133,30 @@ export default class Context {

/**
* called the `handler` with useCase when the useCase is completed.
* @param {function(useCase: UseCase)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @public
*/
onCompleteEachUseCase(handler) {
const releaseHandler = this._dispatcher.onDispatch(payload => {
const releaseHandler = this._dispatcher.onDispatch((payload, meta) => {
if (payload.type === ActionTypes.ON_COMPLETE_EACH_USECASE) {
handler(payload.useCase);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
return releaseHandler;
}



/**
* called the `errorHandler` with error when error is occurred.
* @param {function(payload: UseCaseErrorPayload)} errorHandler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @returns {function(this:Dispatcher)}
* @public
*/
onErrorDispatch(errorHandler) {
const releaseHandler = this._dispatcher.onDispatch(payload => {
onErrorDispatch(handler) {
const releaseHandler = this._dispatcher.onDispatch((payload, meta) => {
if (payload.type === ActionTypes.ON_ERROR) {
errorHandler(payload);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
Expand Down
28 changes: 18 additions & 10 deletions src/Dispatcher.js
Expand Up @@ -2,11 +2,13 @@
"use strict";
const assert = require("assert");
const EventEmitter = require("events");
import DispatcherPayloadMeta from "./DispatcherPayloadMeta";
export const ON_DISPATCH = "__ON_DISPATCH__";
/**
* payload The payload object that must have `type` property.
* DispatcherPayload is an object.
* The payload object that must have `type` property.
* `type` property is a good idea to use string constants or Symbol for dispatch types.
* @typedef {Object} DispatcherPayload
* @property {*} type The event type to dispatch.
* @public
*/
/**
Expand Down Expand Up @@ -52,27 +54,33 @@ export default class Dispatcher extends EventEmitter {

/**
* add onAction handler and return unbind function
* @param {function(payload: DispatcherPayload)} payloadHandler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @returns {Function} call the function and release handler
* @public
*/
onDispatch(payloadHandler) {
this.on(ON_DISPATCH, payloadHandler);
return this.removeListener.bind(this, ON_DISPATCH, payloadHandler);
onDispatch(handler) {
this.on(ON_DISPATCH, handler);
return this.removeListener.bind(this, ON_DISPATCH, handler);
}

/**
* dispatch action object.
* StoreGroups receive this action and reduce state.
* @param {DispatcherPayload} payload
* @param {DispatcherPayloadMeta} [meta]
* @public
*/
dispatch(payload) {
dispatch(payload, meta) {
if (process.env.NODE_ENV !== "production") {
assert(payload !== undefined && payload !== null, "payload should not null or undefined");
assert(typeof payload.type !== "undefined", "payload's `type` should be required");
}
this.emit(ON_DISPATCH, payload);
if (meta === undefined) {
const dispatchOnlyMeta = new DispatcherPayloadMeta();
this.emit(ON_DISPATCH, payload, dispatchOnlyMeta);
} else {
this.emit(ON_DISPATCH, payload, meta);
}
}

/**
Expand All @@ -85,9 +93,9 @@ export default class Dispatcher extends EventEmitter {
const fromName = this.constructor.name;
const toName = toDispatcher.constructor.name;
const displayName = `delegate-payload:${fromName}-to-${toName}`;
const delegatePayload = function delegatePayload(payload) {
const delegatePayload = function delegatePayload(payload, meta) {
delegatePayload.displayName = displayName;
toDispatcher.dispatch(payload);
toDispatcher.dispatch(payload, meta);
};
return this.onDispatch(delegatePayload);
}
Expand Down
37 changes: 37 additions & 0 deletions src/DispatcherPayloadMeta.js
@@ -0,0 +1,37 @@
// LICENSE : MIT
"use strict";
/**
* DispatcherPayloadMeta is a meta object for dispatcher.
* This object is always created by System(= Almin).
* The user can get this meta object together with dispatched `payload` object
* @example
* context.onDispatch((payload, meta) => {});
*/
export default class DispatcherPayloadMeta {
/**
* @param {UseCase} [useCase]
* @param {UseCase|Dispatcher} [parentDispatcher]
*/
constructor({
useCase,
parentDispatcher
} = {}) {
/**
* @param {UseCase|Dispatcher|null|undefined} useCase A reference to the useCase/dispatcher to which the payload was originally dispatched.
* @public
*/
this.useCase = useCase;
/**
* Parent dispatcher/useCase of the `this.useCase`,
* @param {UseCase|Dispatcher|null|undefined}
* @public
*/
this.parentDispatcher = parentDispatcher;
/**
* timeStamp is created time of the meta.
* @type {number}
* @public
*/
this.timeStamp = Date.now();
}
}
6 changes: 3 additions & 3 deletions src/Store.js
Expand Up @@ -68,7 +68,7 @@ export default class Store extends Dispatcher {

/**
* invoke `handler` when UseCase throw error events.
* @param {function(payload: UseCaseErrorPayload)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @returns {Function} call the function and release handler
* @public
* @example
Expand All @@ -80,9 +80,9 @@ export default class Store extends Dispatcher {
* }):
*/
onError(handler) {
return this.onDispatch(payload => {
return this.onDispatch((payload, meta) => {
if (payload.type === ActionTypes.ON_ERROR) {
handler(payload);
handler(payload, meta);
}
});
}
Expand Down
6 changes: 3 additions & 3 deletions src/UILayer/QueuedStoreGroup.js
Expand Up @@ -95,7 +95,7 @@ export default class QueuedStoreGroup extends Dispatcher {
this._stateCache = new LRU(100);
// `this` can catch the events of dispatchers
// Because context delegate dispatched events to **this**
const tryToEmitChange = (payload) => {
const tryToEmitChange = (payload, meta) => {
// check stores, if payload's type is not built-in event.
// It means that `onDispatch` is called when dispatching user event.
if (ActionTypes[payload.type] === undefined) {
Expand All @@ -107,7 +107,7 @@ export default class QueuedStoreGroup extends Dispatcher {
this.emitChange();
}
} else if (payload.type === ActionTypes.ON_DID_EXECUTE_EACH_USECASE) {
const parent = payload.parent;
const parent = meta.parentDispatcher;
// when {asap: false}, emitChange when root useCase is executed
if (!asap && parent) {
return;
Expand All @@ -116,7 +116,7 @@ export default class QueuedStoreGroup extends Dispatcher {
this.emitChange();
}
} else if (payload.type === ActionTypes.ON_COMPLETE_EACH_USECASE) {
const parent = payload.parent;
const parent = meta.parentDispatcher;
// when {asap: false}, emitChange when root useCase is executed
if (!asap && parent) {
return;
Expand Down
7 changes: 5 additions & 2 deletions src/UseCase.js
Expand Up @@ -3,6 +3,7 @@
import Dispatcher from "./Dispatcher";
import UseCaseContext from "./UseCaseContext";
import {ActionTypes} from "./Context";
import DispatcherPayloadMeta from "./DispatcherPayloadMeta";
/**
* @type {string}
* @private
Expand Down Expand Up @@ -97,14 +98,16 @@ export default class UseCase extends Dispatcher {
* @public
*/
throwError(error) {
const meta = new DispatcherPayloadMeta({
useCase: this
});
/**
* @type {UseCaseErrorPayload}
*/
const payload = {
type: ActionTypes.ON_ERROR,
useCase: this,
error: error
};
this.dispatch(payload);
this.dispatch(payload, meta);
}
}
38 changes: 23 additions & 15 deletions src/UseCaseExecutor.js
Expand Up @@ -4,6 +4,7 @@ const assert = require("assert");
import {ActionTypes} from "./Context";
import Dispatcher from "./Dispatcher";
import UseCase from "./UseCase";
import DispatcherPayloadMeta from "./DispatcherPayloadMeta";
/**
* UseCaseExecutor is a helper class for executing UseCase.
* @public
Expand Down Expand Up @@ -61,36 +62,43 @@ export default class UseCaseExecutor {
*/
willExecute(args) {
// emit event for System
this.disptcher.dispatch({
type: ActionTypes.ON_WILL_EXECUTE_EACH_USECASE,
const meta = new DispatcherPayloadMeta({
useCase: this.useCase,
parent: this.parentUseCase,
parentDispatcher: this.parentUseCase,
args
});
this.disptcher.dispatch({
type: ActionTypes.ON_WILL_EXECUTE_EACH_USECASE,
args
}, meta);
}

/**
* dispatch did execute each UseCase
* @private
*/
didExecute() {
this.disptcher.dispatch({
type: ActionTypes.ON_DID_EXECUTE_EACH_USECASE,
const meta = new DispatcherPayloadMeta({
useCase: this.useCase,
parent: this.parentUseCase
parentDispatcher: this.parentUseCase
});
this.disptcher.dispatch({
type: ActionTypes.ON_DID_EXECUTE_EACH_USECASE
}, meta);
}

/**
* dispatch complete each UseCase
* @private
*/
complete() {
this.disptcher.dispatch({
type: ActionTypes.ON_COMPLETE_EACH_USECASE,
const meta = new DispatcherPayloadMeta({
useCase: this.useCase,
parent: this.parentUseCase
parentDispatcher: this.parentUseCase
});
this.disptcher.dispatch({
type: ActionTypes.ON_COMPLETE_EACH_USECASE
}, meta);
}

/**
Expand All @@ -110,13 +118,13 @@ export default class UseCaseExecutor {

/**
* called the `handler` with useCase when the useCase is executed.
* @param {function(useCase: UseCase)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @public
*/
onDidExecuteEachUseCase(handler) {
const releaseHandler = this.disptcher.onDispatch(function onDidExecuted(payload) {
const releaseHandler = this.disptcher.onDispatch(function onDidExecuted(payload, meta) {
if (payload.type === ActionTypes.ON_DID_EXECUTE_EACH_USECASE) {
handler(payload.useCase);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
Expand All @@ -125,14 +133,14 @@ export default class UseCaseExecutor {

/**
* called the `handler` with useCase when the useCase is completed.
* @param {function(useCase: UseCase)} handler
* @param {function(payload: DispatcherPayload, meta: DispatcherPayloadMeta)} handler
* @returns {Function}
* @public
*/
onCompleteExecuteEachUseCase(handler) {
const releaseHandler = this.disptcher.onDispatch(function onCompleted(payload) {
const releaseHandler = this.disptcher.onDispatch(function onCompleted(payload, meta) {
if (payload.type === ActionTypes.ON_COMPLETE_EACH_USECASE) {
handler(payload.useCase);
handler(payload, meta);
}
});
this._releaseHandlers.push(releaseHandler);
Expand Down

0 comments on commit d123fbf

Please sign in to comment.