Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dispatchr

Package Overview
Dependencies
Maintainers
5
Versions
40
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dispatchr - npm Package Compare versions

Comparing version 0.2.14 to 0.3.0

addons/BaseStore.js

36

docs/dispatchr.md

@@ -1,17 +0,21 @@

# Dispatcher API
# Dispatchr API
## Constructor(context)
Dispatchr has one main function that is exported: `createDispatcher(options)`. This returns a new [`Dispatcher`](#dispatcher-api) instance. `createDispatcher` takes the following `options`:
Creates a new Dispatcher instance with the following parameters:
* `options.stores`: An array of stores to register automatically
* `context`: A context object that will be made available to all stores. Useful for request or session level settings.
## Dispatcher API
## Static Methods
### registerStore(storeClass)
A static method to register stores to the Dispatcher class making them available to handle actions and be accessible through `getStore` on Dispatchr instances.
A static method to register stores to the dispatcher making them available to handle actions and be accessible through `getStore` on the dispatcher context.
## Instance Methods
### createContext(contextOptions)
Creates a new dispatcher [context](#context-api) that isolates stores and dispatches with the following parameters:
* `contextOptions`: A context object that will be made available to all stores. Useful for request or session level settings.
## Context API
### dispatch(actionName, payload)

@@ -24,3 +28,3 @@

### getStore(storeClass)
#### getStore(storeClass)

@@ -30,7 +34,7 @@ Retrieve a store instance by class. Allows access to stores from components or stores from other stores.

```js
var store = require('./stores/MessageStore');
dispatcher.getStore(store);
var MessageStore = require('./stores/MessageStore');
dispatcher.getStore(MessageStore);
```
### waitFor(storeClasses, callback)
#### waitFor(storeClasses, callback)

@@ -42,9 +46,9 @@ Waits for another store's handler to finish before calling the callback. This is useful from within stores if they need to wait for other stores to finish first.

### dehydrate()
#### dehydrate()
Returns a serializable object containing the state of the Dispatchr instance as well as all stores that have been used since instantiation. This is useful for serializing the state of the application to send it to the client.
Returns a serializable object containing the state of the dispatcher context as well as all stores that have been used since instantiation. This is useful for serializing the state of the application to send it to the client.
### rehydrate(dispatcherState)
#### rehydrate(dispatcherState)
Takes an object representing the state of the Dispatchr instance (usually retrieved from dehydrate) to rehydrate the instance as well as the store instance state.
Takes an object representing the state of the dispatcher context (usually retrieved from dehydrate) to rehydrate the instance as well as the store instance state.

@@ -109,1 +109,90 @@ # Store API

```
## Helper Utilities
### BaseStore
A base class that you can extend to reduce boilerplate when creating stores.
```js
var BaseStore = require('fluxible/addons').BaseStore;
```
#### Built-In Methods
* `emitChange()` - emits a 'change' event
* `getContext()` - returns the [store context](FluxibleContext.md#store-context)
* `addChangeListener(callback)` - simple method to add a change listener
* `removeChangeListener(callback)` - removes a change listener
* `shouldDehydrate()` - default implementation that returns true if a `change` event has been emitted
```js
var BaseStore = require('fluxible/addons').BaseStore;
var util = require('util');
function ApplicationStore(dispatcher) {
BaseStore.apply(this, arguments);
this.currentPageName = null;
}
ApplicationStore.storeName = 'ApplicationStore';
ApplicationStore.handlers = {
'RECEIVE_PAGE': 'handleReceivePage'
};
util.inherits(ApplicationStore, BaseStore);
ApplicationStore.prototype.handleReceivePage = function (payload) {
this.currentPageName = payload.pageName;
this.emitChange();
};
ApplicationStore.prototype.getCurrentPageName = function () {
return this.currentPageName;
};
// For sending state to the client
ApplicationStore.prototype.dehydrate = function () {
return {
currentPageName: this.currentPageName
};
};
// For rehydrating server state
ApplicationStore.prototype.rehydrate = function (state) {
this.currentPageName = state.currentPageName;
};
module.exports = ApplicationStore;
```
### createStore
A helper method similar to `React.createClass` but for creating stores that extend `BaseStore`. Also supports mixins.
```js
var createStore = require('fluxible/addons').createStore;
module.exports = createStore({
storeName: 'ApplicationStore',
handlers: {
'RECEIVE_PAGE': 'handleReceivePage'
},
handleReceivePage: function (payload) {
this.currentPageName = payload.pageName;
this.emitChange();
},
getCurrentPage: function () {
return this.currentPageName;
},
dehydrate: function () {
return {
currentPageName: this.currentPageName
};
},
rehydrate: function (state) {
this.currentPageName = state.currentPageName;
}
});

@@ -7,246 +7,121 @@ /**

var Action = require('./Action'),
DEFAULT = 'default';
var Action = require('./Action');
var DEFAULT = 'default';
var DispatcherContext = require('./DispatcherContext');
module.exports = function () {
var debug = require('debug')('Dispatchr:dispatcher');
/**
* @class Dispatcher
* @param {Object} options Dispatcher options
* @param {Array} options.stores Array of stores to register
* @constructor
*/
function Dispatcher (options) {
options = options || {};
options.stores = options.stores || [];
this.stores = {};
this.handlers = {};
this.handlers[DEFAULT] = [];
options.stores.forEach(function (store) {
this.registerStore(store);
}, this);
}
/**
* @class Dispatcher
* @param {Object} context The context to be used for store instances
* @constructor
*/
function Dispatcher (context) {
this.storeInstances = {};
this.currentAction = null;
this.dispatcherInterface = {
getContext: function getContext() { return context; },
getStore: this.getStore.bind(this),
waitFor: this.waitFor.bind(this)
};
Dispatcher.prototype.createContext = function createContext(context) {
return new DispatcherContext(this, context);
};
/**
* Registers a store so that it can handle actions.
* @method registerStore
* @static
* @param {Object} store A store class to be registered. The store should have a static
* `name` property so that it can be loaded later.
* @throws {Error} if store is invalid
* @throws {Error} if store is already registered
*/
Dispatcher.prototype.registerStore = function registerStore(store) {
if ('function' !== typeof store) {
throw new Error('registerStore requires a constructor as first parameter');
}
Dispatcher.stores = {};
Dispatcher.handlers = {
'default': []
};
/**
* Registers a store so that it can handle actions.
* @method registerStore
* @static
* @param {Object} store A store class to be registered. The store should have a static
* `name` property so that it can be loaded later.
* @throws {Error} if store is invalid
* @throws {Error} if store is already registered
*/
Dispatcher.registerStore = function registerStore(store) {
if ('function' !== typeof store) {
throw new Error('registerStore requires a constructor as first parameter');
}
var storeName = Dispatcher.getStoreName(store);
if (!storeName) {
throw new Error('Store is required to have a `storeName` property.');
}
if (Dispatcher.stores[storeName]) {
if (Dispatcher.stores[storeName] === store) {
// Store is already registered, nothing to do
return;
}
throw new Error('Store with name `' + storeName + '` has already been registered.');
}
Dispatcher.stores[storeName] = store;
if (store.handlers) {
Object.keys(store.handlers).forEach(function storeHandlersEach(action) {
var handler = store.handlers[action];
Dispatcher._registerHandler(action, storeName, handler);
});
}
};
/**
* Method to discover if a storeName has been registered
* @method isRegistered
* @static
* @param {Object|String} store The store to check
* @returns {boolean}
*/
Dispatcher.isRegistered = function isRegistered(store) {
var storeName = Dispatcher.getStoreName(store),
storeInstance = Dispatcher.stores[storeName];
if (!storeInstance) {
return false;
}
if ('function' === typeof store) {
if (store !== storeInstance) {
return false;
}
}
return true;
};
/**
* Gets a name from a store
* @method getStoreName
* @static
* @param {String|Object} store The store name or class from which to extract
* the name
* @returns {String}
*/
Dispatcher.getStoreName = function getStoreName(store) {
if ('string' === typeof store) {
return store;
}
return store.storeName;
};
/**
* Adds a handler function to be called for the given action
* @method registerHandler
* @private
* @static
* @param {String} action Name of the action
* @param {String} name Name of the store that handles the action
* @param {String|Function} handler The function or name of the method that handles the action
* @returns {number}
*/
Dispatcher._registerHandler = function registerHandler(action, name, handler) {
Dispatcher.handlers[action] = Dispatcher.handlers[action] || [];
Dispatcher.handlers[action].push({
name: Dispatcher.getStoreName(name),
handler: handler
});
return Dispatcher.handlers.length - 1;
};
/**
* Returns a single store instance and creates one if it doesn't already exist
* @method getStore
* @param {String} name The name of the instance
* @returns {Object} The store instance
* @throws {Error} if store is not registered
*/
Dispatcher.prototype.getStore = function getStore(name) {
var storeName = Dispatcher.getStoreName(name);
if (!this.storeInstances[storeName]) {
var Store = Dispatcher.stores[storeName];
if (!Store) {
throw new Error('Store ' + storeName + ' was not registered.');
}
this.storeInstances[storeName] = new (Dispatcher.stores[storeName])(this.dispatcherInterface);
}
return this.storeInstances[storeName];
};
/**
* Dispatches a new action or queues it up if one is already in progress
* @method dispatch
* @param {String} actionName Name of the action to be dispatched
* @param {Object} payload Parameters to describe the action
* @throws {Error} if store has handler registered that does not exist
*/
Dispatcher.prototype.dispatch = function dispatch(actionName, payload) {
if (!actionName) {
throw new Error('actionName parameter `' + actionName + '` is invalid.');
}
if (this.currentAction) {
throw new Error('Cannot call dispatch while another dispatch is executing. Attempted to execute \'' + actionName + '\' but \'' + this.currentAction.name + '\' is already executing.');
}
var actionHandlers = Dispatcher.handlers[actionName] || [],
defaultHandlers = Dispatcher.handlers[DEFAULT] || [];
if (!actionHandlers.length && !defaultHandlers.length) {
debug(actionName + ' does not have any registered handlers');
var storeName = this.getStoreName(store);
if (!storeName) {
throw new Error('Store is required to have a `storeName` property.');
}
if (this.stores[storeName]) {
if (this.stores[storeName] === store) {
// Store is already registered, nothing to do
return;
}
debug('dispatching ' + actionName, payload);
this.currentAction = new Action(actionName, payload);
var self = this,
allHandlers = actionHandlers.concat(defaultHandlers),
handlerFns = {};
throw new Error('Store with name `' + storeName + '` has already been registered.');
}
this.stores[storeName] = store;
if (store.handlers) {
Object.keys(store.handlers).forEach(function storeHandlersEach(action) {
var handler = store.handlers[action];
this._registerHandler(action, storeName, handler);
}, this);
}
};
try {
allHandlers.forEach(function actionHandlersEach(store) {
if (handlerFns[store.name]) {
// Don't call the default if the store has an explicit action handler
return;
}
var storeInstance = self.getStore(store.name);
if ('function' === typeof store.handler) {
handlerFns[store.name] = store.handler.bind(storeInstance);
} else {
if (!storeInstance[store.handler]) {
throw new Error(store.name + ' does not have a method called ' + store.handler);
}
handlerFns[store.name] = storeInstance[store.handler].bind(storeInstance);
}
});
this.currentAction.execute(handlerFns);
} catch (e) {
throw e;
} finally {
debug('finished ' + actionName);
this.currentAction = null;
}
};
/**
* Method to discover if a storeName has been registered
* @method isRegistered
* @static
* @param {Object|String} store The store to check
* @returns {boolean}
*/
Dispatcher.prototype.isRegistered = function isRegistered(store) {
var storeName = this.getStoreName(store),
storeInstance = this.stores[storeName];
/**
* Returns a raw data object representation of the current state of the
* dispatcher and all store instances. If the store implements a shouldDehdyrate
* function, then it will be called and only dehydrate if the method returns `true`
* @method dehydrate
* @returns {Object} dehydrated dispatcher data
*/
Dispatcher.prototype.dehydrate = function dehydrate() {
var self = this,
stores = {};
Object.keys(self.storeInstances).forEach(function storeInstancesEach(storeName) {
var store = self.storeInstances[storeName];
if (!store.dehydrate || (store.shouldDehydrate && !store.shouldDehydrate())) {
return;
}
stores[storeName] = store.dehydrate();
});
return {
stores: stores
};
};
if (!storeInstance) {
return false;
}
/**
* Takes a raw data object and rehydrates the dispatcher and store instances
* @method rehydrate
* @param {Object} dispatcherState raw state typically retrieved from `dehydrate`
* method
*/
Dispatcher.prototype.rehydrate = function rehydrate(dispatcherState) {
var self = this;
if (dispatcherState.stores) {
Object.keys(dispatcherState.stores).forEach(function storeStateEach(storeName) {
var state = dispatcherState.stores[storeName],
store = self.getStore(storeName);
if (store.rehydrate) {
store.rehydrate(state);
}
});
if ('function' === typeof store) {
if (store !== storeInstance) {
return false;
}
};
}
return true;
};
/**
* Waits until all stores have finished handling an action and then calls
* the callback
* @method waitFor
* @param {String|String[]} stores An array of stores as strings to wait for
* @param {Function} callback Called after all stores have completed handling their actions
* @throws {Error} if there is no action dispatching
*/
Dispatcher.prototype.waitFor = function waitFor(stores, callback) {
if (!this.currentAction) {
throw new Error('waitFor called even though there is no action dispatching');
}
this.currentAction.waitFor(stores, callback);
};
/**
* Gets a name from a store
* @method getStoreName
* @static
* @param {String|Object} store The store name or class from which to extract
* the name
* @returns {String}
*/
Dispatcher.prototype.getStoreName = function getStoreName(store) {
if ('string' === typeof store) {
return store;
}
return store.storeName || store.name;
};
return Dispatcher;
/**
* Adds a handler function to be called for the given action
* @method registerHandler
* @private
* @static
* @param {String} action Name of the action
* @param {String} name Name of the store that handles the action
* @param {String|Function} handler The function or name of the method that handles the action
* @returns {number}
*/
Dispatcher.prototype._registerHandler = function registerHandler(action, name, handler) {
this.handlers[action] = this.handlers[action] || [];
this.handlers[action].push({
name: this.getStoreName(name),
handler: handler
});
return this.handlers.length - 1;
};
module.exports = {
createDispatcher: function (options) {
return new Dispatcher(options);
}
};
{
"name": "dispatchr",
"version": "0.2.14",
"version": "0.3.0",
"description": "A Flux dispatcher for applications that run on the server and the client.",

@@ -23,3 +23,5 @@ "main": "index.js",

"dependencies": {
"debug": "^2.0.0"
"debug": "^2.0.0",
"eventemitter3": "^0.1.6",
"inherits": "^2.0.1"
},

@@ -26,0 +28,0 @@ "devDependencies": {

@@ -9,3 +9,3 @@ # Dispatchr

A [Flux](http://facebook.github.io/react/docs/flux-overview.html) dispatcher for applications that run on the server and the client.
A [Flux](http://facebook.github.io/flux/docs/overview.html) dispatcher for applications that run on the server and the client.

@@ -17,10 +17,10 @@ ## Usage

```js
var Dispatchr = require('dispatchr')(),
ExampleStore = require('./example-store.js'),
context = {};
var ExampleStore = require('./stores/ExampleStore.js');
var dispatcher = require('dispatchr').createDispatcher({
stores: [ExampleStore]
});
Dispatchr.registerStore(ExampleStore);
var contextOptions = {};
var dispatcherContext = dispatcher.createContext(contextOptions);
var dispatcher = new Dispatchr(context);
dispatcher.dispatch('NAVIGATE', {});

@@ -46,11 +46,11 @@ // Action has been handled fully

`require('dispatchr/utils/BaseStore')` provides a base store class for extending. Provides `getContext`, `emitChange`, `addChangeListener`, and `removeChangeListener` functions. Example:
`require('dispatchr/addons/BaseStore')` provides a base store class for extending. Provides `getContext`, `emitChange`, `addChangeListener`, and `removeChangeListener` functions. Example:
```js
var util = require('util');
var BaseStore = require('dispatchr/utils/BaseStore');
var inherits = require('inherits');
var BaseStore = require('dispatchr/addons/BaseStore');
var MyStore = function (dispatcherInterface) {
BaseStore.apply(this, arguments);
};
util.inherits(MyStore, BaseStore);
inherits(MyStore, BaseStore);
MyStore.storeName = 'MyStore';

@@ -67,6 +67,6 @@ MyStore.handlers = {

`require('dispatchr/utils/createStore')` provides a helper function for creating stores similar to React's `createClass` function. The created store class will extend BaseStore and have the same built-in functions. Example:
`require('dispatchr/addons/createStore')` provides a helper function for creating stores similar to React's `createClass` function. The created store class will extend BaseStore and have the same built-in functions. Example:
```js
var createStore = require('dispatchr/utils/createStore');
var createStore = require('dispatchr/addons/createStore');
var MyStore = createStore({

@@ -73,0 +73,0 @@ initialize: function () {}, // Called immediately after instantiation

@@ -1,75 +0,1 @@

/**
* Copyright 2014, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var CHANGE_EVENT = 'change';
/**
* @class BaseStore
* @extends EventEmitter
* @param dispatcher The dispatcher interface
* @constructor
*/
function BaseStore(dispatcher) {
this.dispatcher = dispatcher;
this._hasChanged = false;
if (this.initialize) {
this.initialize();
}
}
util.inherits(BaseStore, EventEmitter);
/**
* Convenience method for getting the store context object.
* @method getContext
* @return {Object} Returns the store context object.
*/
BaseStore.prototype.getContext = function getContext() {
return this.dispatcher.getContext();
};
/**
* Add a listener for the change event
* @method addChangeListener
* @param {Function} callback
*/
BaseStore.prototype.addChangeListener = function addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
};
/**
* Remove a listener for the change event
* @method removeChangeListener
* @param {Function} callback
*/
BaseStore.prototype.removeChangeListener = function removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
};
/**
* Determines whether the store should dehydrate or not. By default, only dehydrates
* if the store has emitted an update event. If no update has been emitted, it is assumed
* that the store is in its default state and therefore does not need to dehydrate.
* @method shouldDehydrate
* @returns {boolean}
*/
BaseStore.prototype.shouldDehydrate = function shouldDehydrate() {
return this._hasChanged;
};
/**
* Emit a change event
* @method emitChange
* @param {*} param=this
*/
BaseStore.prototype.emitChange = function emitChange(param) {
this._hasChanged = true;
this.emit(CHANGE_EVENT, param || this);
};
module.exports = BaseStore;
throw new Error("require('fluxible/utils/BaseStore') has moved to require('fluxible/addons/BaseStore')");

@@ -1,75 +0,1 @@

/**
* Copyright 2014, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
'use strict';
var util = require('util'),
BaseStore = require('./BaseStore'),
IGNORE_ON_PROTOTYPE = ['statics', 'storeName', 'handlers', 'mixins'];
function createChainedFunction(one, two) {
return function chainedFunction() {
one.apply(this, arguments);
two.apply(this, arguments);
};
}
function mixInto(dest, src) {
Object.keys(src).forEach(function (prop) {
if (-1 !== IGNORE_ON_PROTOTYPE.indexOf(prop)) {
return;
}
if ('initialize' === prop) {
if (!dest[prop]) {
dest[prop] = src[prop];
} else {
dest[prop] = createChainedFunction(dest[prop], src[prop]);
}
} else {
if (dest.hasOwnProperty(prop)) {
throw new Error('Mixin property collision for property "' + prop + '"');
}
dest[prop] = src[prop];
}
});
}
/**
* Helper for creating a store class
* @method createStore
* @param {Object} spec
* @param {String} spec.storeName The name of the store
* @param {Object} spec.handlers Hash of action name to method name of action handlers
* @param {Function} spec.initialize Function called during construction for setting the default state
* @param {Function} spec.dehydrate Function that returns serializable data to send to the client
* @param {Function} spec.rehydrate Function that takes in serializable data to rehydrate the store
*/
module.exports = function createStore(spec) {
spec.statics = spec.statics || {};
if (!spec.storeName && !spec.statics.storeName) {
throw new Error('createStore called without a storeName');
}
var Store = function (dispatcher) {
BaseStore.call(this, dispatcher);
};
util.inherits(Store, BaseStore);
Object.keys(spec.statics).forEach(function (prop) {
Store[prop] = spec.statics[prop];
});
Store.storeName = spec.storeName || Store.storeName;
Store.handlers = spec.handlers || Store.handlers;
Store.mixins = spec.mixins || Store.mixins;
if (Store.mixins) {
Store.mixins.forEach(function(mixin) {
mixInto(Store.prototype, mixin);
});
}
mixInto(Store.prototype, spec);
return Store;
};
throw new Error("require('fluxible/utils/createStore') has moved to require('fluxible/addons/createStore')");

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc