Last active
January 27, 2017 14:43
-
-
Save hejrobin/37c6290a1360bb3c83a0537b844eb61f to your computer and use it in GitHub Desktop.
Common ES7 modules I use for a simple Flux architecture.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @const int EVENT_MIN_LISTENERS | |
*/ | |
const EVENT_MIN_LISTENERS = 1; | |
/** | |
* @const int EVENT_MAX_LISTENERS | |
*/ | |
const EVENT_MAX_LISTENERS = 10; | |
/** | |
* @const Symbol EVENT_STORE | |
*/ | |
const EVENT_STORE = Symbol('EVENT_STORE'); | |
/** | |
* @private validateArguments | |
* | |
* Validates eventType and eventListener arguments. | |
* | |
* @param string eventType | |
* @param callable eventListener | |
* | |
* @throws TypeError | |
* | |
* @return void | |
*/ | |
const validateArguments = (eventType, eventListener) => { | |
if (typeof eventType !== 'string') { | |
throw new TypeError(); | |
} | |
if (typeof eventListener !== 'function') { | |
throw new TypeError(); | |
} | |
} | |
/** | |
* @class EventEmitter | |
* | |
* Event emitter class implementation in ES6. | |
*/ | |
export default class EventEmitter { | |
/** | |
* @var int maxListenerCount | |
*/ | |
maxListenerCount = EVENT_MAX_LISTENERS; | |
/** | |
* Constructor | |
* | |
* Creates a new instance of EventEmitter. | |
* | |
* @return void | |
*/ | |
constructor() { | |
this[EVENT_STORE] = {}; | |
} | |
/** | |
* @getter eventStore | |
* | |
* Returns current event store. | |
* | |
* @return object | |
*/ | |
get eventStore() { | |
return this[EVENT_STORE]; | |
} | |
/** | |
* setMaxListenerCount | |
* | |
* Sets new max listener count per event type. | |
* | |
* @param int newMaxListenerCount | |
* | |
* @return void | |
*/ | |
setMaxListenerCount(newMaxListenerCount) { | |
if (isNaN(newMaxListenerCount) === true) return; | |
let checkFloat = parseFloat(newMaxListenerCount); | |
if (((checkFloat | 0) === checkFloat) && newMaxListenerCount >= EVENT_MIN_LISTENERS) { | |
this.maxListenerCount = newMaxListenerCount; | |
} | |
} | |
/** | |
* getMaxListenerCount | |
* | |
* Returns current max listener count. | |
* | |
* @return int | |
*/ | |
getMaxListenerCount() { | |
return this.maxListenerCount; | |
} | |
/** | |
* @getter eventTypes | |
* | |
* Returns all registered event types to current instance. | |
* | |
* @return array | |
*/ | |
get eventTypes() { | |
return Object.keys(this.eventStore); | |
} | |
/** | |
* getListeners | |
* | |
* Returns all registered listeners to event type. | |
* | |
* @param string eventType | |
* | |
* @return array | |
*/ | |
getListeners(eventType) { | |
if (this.eventTypes.indexOf(eventType) >= 0) { | |
return Object.values(this.eventStore[eventType]); | |
} | |
return []; | |
} | |
/** | |
* getListenerPosition | |
* | |
* Returns index of event listener in event store. | |
* | |
* @param string eventType | |
* @param callabke eventListener | |
* | |
* @return int | |
*/ | |
getListenerPosition(eventType, eventListener) { | |
validateArguments(...arguments); | |
return this.getListeners(eventType).indexOf(eventListener); | |
} | |
/** | |
* hasListener | |
* | |
* Validates whether or not event type has a specific listener registered. | |
* | |
* @param string eventType | |
* @param callable eventListener | |
* | |
* @return bool | |
*/ | |
hasListener(eventType, eventListener) { | |
validateArguments(...arguments); | |
if (this.eventTypes.indexOf(eventType) >= 0) { | |
return (this.getListenerPosition(eventType, eventListener)>= 0); | |
} | |
return false; | |
} | |
/** | |
* addListener | |
* | |
* Attempts to add an event listener to event type. | |
* | |
* @param string eventType | |
* @param callabke eventListener | |
* | |
* @return self | |
*/ | |
addListener(eventType, eventListener) { | |
validateArguments(...arguments); | |
if (this.hasListener(eventType, eventListener) === true) { | |
return this; | |
} | |
if (this.getListeners(eventType).length >= this.maxListenerCount) { | |
throw new RangeError(`Possible memory leak, max listener count exceeded.`); | |
} | |
if (this.eventStore.hasOwnProperty(eventType) === false) { | |
this.eventStore[eventType] = []; | |
} | |
this.eventStore[eventType].push(eventListener); | |
return this; | |
} | |
// @alias this.on > this.addListener | |
on = ::this.addListener | |
/** | |
* addOnceListener | |
* | |
* Creates a self removing event listener and registerers it. | |
* | |
* @param string eventType | |
* @param callable eventListener | |
* | |
* @return this | |
*/ | |
addOnceListener(eventType, eventListener) { | |
const selfRemovingEventListener = () => { | |
this.removeListener(eventType, selfRemovingEventListener); | |
eventListener.apply(this, arguments); | |
} | |
return this.addListener(eventType, selfRemovingEventListener); | |
} | |
// @alias this.once > this.addOnceListener | |
once = ::this.addOnceListener | |
/** | |
* removeListener | |
* | |
* Attempts to remove event listener. | |
* | |
* @param string eventType | |
* @param callable eventListener | |
* | |
* @return this | |
*/ | |
removeListener(eventType, eventListener) { | |
validateArguments(...arguments); | |
if (this.hasListener(eventType, eventListener) === true) { | |
let listenerPosition = this.getListenerPosition(eventType, eventListener); | |
this.eventStore[eventType].splice(listenerPosition, 1); | |
} | |
return this; | |
} | |
// @alias this.off > this.removeListener | |
off = ::this.removeListener | |
/** | |
* removeListeners | |
* | |
* Removes all specified event listeners. | |
* | |
* @param string eventType | |
* @param callabke eventListeners, ... | |
* | |
* @return self | |
*/ | |
removeListeners(eventType, ...eventListeners) { | |
eventListeners.forEach((eventListener) => { | |
this.removeListener(eventType, eventListener); | |
}); | |
return this; | |
} | |
/** | |
* removeAllListeners | |
* | |
* Removes all listeners to registered event type. | |
* | |
* @param string eventType | |
* | |
* @return self | |
*/ | |
removeAllListeners(eventType) { | |
if (typeof eventType !== 'string') { | |
throw new TypeError(); | |
} | |
if (this.eventStore.hasOwnProperty(eventType) === true) { | |
this.eventStore[eventType] = []; | |
} | |
return this; | |
} | |
emit(eventType, ...listenerArguments) { | |
if (this.eventStore.hasOwnProperty(eventType) === true) { | |
this.eventStore[eventType].forEach(eventListener => { | |
eventListener.apply(null, listenerArguments) | |
}); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Required packages | |
import EventEmitter from './EventEmitter.es7'; | |
/** | |
* @const Symbol STORE_REGISTRY | |
*/ | |
const STORE_REGISTRY = Symbol('STORE_REGISTRY'); | |
/** | |
* @class Store | |
* | |
* Primitive key-value store class, extends EventEmitter. | |
* | |
* @emits change | |
*/ | |
export default class Store extends EventEmitter { | |
constructor() { | |
super(); | |
this[STORE_REGISTRY] = {}; | |
} | |
/** | |
* @getter storage | |
* | |
* Returns current store. | |
* | |
* @return object | |
*/ | |
get storage() { | |
return this[STORE_REGISTRY]; | |
} | |
/** | |
* @getter entries | |
* | |
* Returns entries of store. | |
* | |
* @return array | |
*/ | |
get entries() { | |
return Object.entries(this.store); | |
} | |
/** | |
* @getter keys | |
* | |
* Returns keys of store. | |
* | |
* @return array | |
*/ | |
get keys() { | |
return Object.keys(this.store); | |
} | |
/** | |
* @getter values | |
* | |
* Returns values of store. | |
* | |
* @return array | |
*/ | |
get values() { | |
return Object.values(this.store); | |
} | |
/** | |
* has | |
* | |
* Validates whether or not store key data exists. | |
* | |
* @param string key | |
* | |
* @return bool | |
*/ | |
has(key) { | |
return this.storage.hasOwnProperty(key) === true; | |
} | |
/** | |
* get | |
* | |
* Attempts to return storage value, returns undefined if not set. | |
* | |
* @param string key | |
* | |
* @return mixed | |
*/ | |
get(key) { | |
if (this.has(key) === true) { | |
return this.storage[key]; | |
} | |
return undefined; | |
} | |
/** | |
* set | |
* | |
* Sets new storage value to key, emits 'change' event if value changes. | |
* | |
* @param string key | |
* @param mixed data | |
* | |
* @return void | |
*/ | |
set(key, data) { | |
let previousData = this.get(key); | |
if (data !== previousData) { | |
this.storage[key] = data; | |
if (previousData !== undefined) { | |
this.emit('change', previousData, data); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment