Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit b3d8c93a3be1f78982045150d2ffb40f4496967e 0 parents
@olado authored
Showing with 301 additions and 0 deletions.
  1. +72 −0 README
  2. +182 −0 causeeffect.js
  3. +47 −0 examples/sample.js
72 README
@@ -0,0 +1,72 @@
+Created to simplify flow management when programming in asynchronous
+environments like nodejs.
+
+ CauseEffect extends EventEmitter and allows to set cause-effect scenarios.
+
+ It is simple yet effective.
+
+ CauseEffect allows you to:
+ * have an event fired when multiple parallel flows complete.
+ * easily unite multiple parallel flows initiated from the for loop.
+ * set up chains: completion of one rule may trigger another.
+ * set the rules dynamically or statically.
+ * have an event fired when any of the flows completes.
+
+ To run example:
+ node examples/sample.js
+
+Usage:
+ To setup a rule where an event will fire if all of the causes happen:
+ ce.setEvents("myevent", ["cause1", "cause2", "cause3"]);
+
+ To setup a rule where an event will fire if some of the causes happen:
+ ce.setEvents("myevent", ["cause1", "cause2", "cause3"], true);
+
+ To setup a rule where an event will fire if it happens X times:
+ ce.setEvents("myevent", 10);
+
+ Listen to effect event as usual with EventEmitter API:
+ ce.on("myevent", callback);
+
+ To let CauseEffect know that the cause has happened call:
+ ce.setState("cause1", 1)
+
+ To run example:
+ node examples/sample.js
+
+Sample:
+
+1. Uniting multiple parallel flows initiated from the for loop:
+ var ce = require('causeeffect');
+ ce = new ce.CauseEffect();
+
+ // Set up ticker cause. Event "myticker" will fire if ce.setState("myticker") was invoked 4 times.
+ // Handy for figuring out when all callbacks have been called initiated from the for loop.
+ ce.setEvents("myticker", 4);
+
+ function callback() {
+ ce.setState("myticker");
+ }
+
+ for(var i = 0; i< 4; i++) {
+ setTimeout(callback, 100);
+ }
+
+ ce.on("myticker", function() {
+ });
+
+
+2. Uniting specific parallel flows
+ // Set up AND cause effect. Event "alldone" will be fired if all of causes happen
+ ce.setEvents("alldone", ["cause1", "cause2", "myticker"]);
+
+ // Listen to event as usual with EventEmitter API
+ ce.on("alldone", function() {
+ });
+
+
+ // To let CauseEffect know that the cause has happened
+ ce.setState("cause1", 1);
+ ce.setState("cause2", 1);
+
+
182 causeeffect.js
@@ -0,0 +1,182 @@
+/*
+* causeeffect - simple evented flow control for nodejs
+* Copyright 2011, Laura Doktorova
+* http://github.com/olado/causeeffect
+*
+* Dual licensed under the MIT or GPL Version 2 licenses.
+*
+* Version: 0.1.0
+*/
+
+(function() {
+ var sys = require("sys"),
+ events = require('events');
+
+ function CauseEffect() {
+ events.EventEmitter.call(this);
+ this.events = {};
+ }
+ sys.inherits(CauseEffect, events.EventEmitter);
+
+ exports.version = '0.1.0';
+ exports.CauseEffect = CauseEffect;
+
+ function setEffect(effect, state) {
+ if (state) {
+ this.events[effect].$state = state;
+ this.emit(effect);
+ if (this.events[effect] && this.events[effect].$state === state) {
+ this.setState(effect, state);
+ }
+ } else {
+ this.events[effect].$state = undefined;
+ }
+ }
+
+ function evalCauses(effect) {
+ var toeval = this.events[effect] && this.events[effect].$and, i;
+ if (toeval && toeval.length) {
+ for(i=toeval.length-1; i >=0 && this.events[toeval[i]] && this.events[toeval[i]].$state; i-=1 ) {}
+ setEffect.call(this, effect, (i < 0) ? 1 : undefined);
+ if (i < 0) {
+ return;
+ }
+ }
+ toeval = this.events[effect] && this.events[effect].$or;
+ if (toeval && toeval.length) {
+ for(i=toeval.length-1; i >=0 && !(this.events[toeval[i]] && this.events[toeval[i]].$state); i-=1 ) {}
+ setEffect.call(this, effect, (i >= 0) ? 1 : undefined);
+ }
+ }
+
+ function isArray (v) {
+ return Object.prototype.toString.call(v) === '[object Array]';
+ }
+
+ CauseEffect.prototype.setState = function(event, state) {
+ if (!this.events[event]) {
+ this.events[event] = { $state : state };
+ } else {
+ var e, t = this.events[event], eff = t.$effects;
+ if (t.$counter) {
+ t.$counter -= 1;
+ if (t.$counter === 0) {
+ setEffect.call(this, event, 1);
+ }
+ return;
+ } else {
+ t.$state = state;
+ }
+
+ if (eff) {
+ for(e in eff) {
+ if (eff.hasOwnProperty(e)) {
+ evalCauses.call(this, e);
+ }
+ }
+ }
+ }
+ };
+
+ CauseEffect.prototype.getState = function(event) {
+ var ev = this.events[event];
+ if (ev) {
+ if (ev.$counter !== undefined) {
+ return ev.$counter;
+ }
+ return ev.$state;
+ }
+ };
+
+ CauseEffect.prototype.setEvents = function(effect, causes, orop) {
+ this.events[effect] = this.events[effect] || {};
+ var index, self, i, cause;
+ if (!isArray(causes)) {
+ if (isNaN(causes)) {
+ index = arguments.length-1;
+ if (typeof arguments[index] === 'boolean') {
+ orop = arguments[index];
+ causes = Array.prototype.slice.call(arguments, 1, index);
+ } else {
+ orop = undefined;
+ causes = Array.prototype.slice.call(arguments, 1);
+ }
+ } else {
+ //ticker case
+ this.events[effect].$counter = causes;
+ return;
+ }
+ }
+
+ this.events[effect][(orop) ? '$or' : '$and'] = causes;
+ // go through causes and see which ones need to be reevaluated
+ index = causes.length;
+ for(i=0; i <index; i+=1) {
+ cause = causes[i];
+ if (!this.events[cause]) {
+ this.events[cause] = {};
+ }
+ if (!this.events[cause].$effects) {
+ this.events[cause].$effects = {};
+ }
+ this.events[cause].$effects[effect] = 1;
+ }
+ // evaluate current state
+ if (index) {
+ self = this;
+ process.nextTick(function() {
+ if (self.getState(effect) === undefined) {
+ evalCauses.call(self, effect);
+ }
+ });
+ }
+ };
+
+ function cleanupRelatedEvents(effect, causes, dontwipe) {
+ var count = causes.length, i, cause;
+ for(i=0; i <count; i+=1) {
+ cause = causes[i];
+ if (this.events[cause]) {
+ if (dontwipe) {
+ if (this.events[cause].$effects) {
+ delete this.events[cause].$effects[effect];
+ }
+ } else {
+ delete this.events[cause];
+ }
+ }
+ }
+ }
+
+ CauseEffect.prototype.removeEvents = function(events/*, event2,...*/, dontwipe) {
+ var args, count, i, event, item;
+ if (isArray(events)) {
+ args = events;
+ } else {
+ i = arguments.length-1;
+ if (typeof arguments[i] === 'boolean') {
+ dontwipe = arguments[i];
+ args = Array.prototype.slice.call(arguments, 0, i);
+ } else {
+ dontwipe = undefined;
+ args = arguments;
+ }
+ }
+
+ count = args.length;
+ for(i=0; i <count; i+=1) {
+ item = args[i];
+ event = this.events[item];
+ if (event) {
+ if (event.$or) {
+ cleanupRelatedEvents.call(this, item, event.$or, dontwipe);
+ }
+ if (event.$and) {
+ cleanupRelatedEvents.call(this, item, event.$and, dontwipe);
+ }
+ delete this.events[item];
+ }
+ }
+ };
+
+}());
47 examples/sample.js
@@ -0,0 +1,47 @@
+var ce = require('../causeeffect');
+ce = new ce.CauseEffect();
+
+// Set up AND cause effect. Event "alldone" will be fired if all of causes happen
+ce.setEvents("alldone", ["cause1", "cause2", "cause3", "myticker"]);
+// Set up OR cause effect. Event "somedone" will be fired if any of causes happen
+ce.setEvents("somedone", ["cause1", "cause2", "cause3"], true);
+// Set up ticker cause. Event "myticker" will fire if ce.setState("myticker") was invoked 4 times.
+// Handy for figuring out when all callbacks have been called initiated from the for loop.
+ce.setEvents("myticker", 4);
+
+ce.on("alldone", function() {
+ ce.removeEvents("alldone", true);
+ console.log("All done: cause1:" +
+ ce.getState("cause1") + " cause2: " +
+ ce.getState("cause2") + " cause3: " +
+ ce.getState("cause3"));
+});
+
+ce.on("somedone", function() {
+ console.log("Some done: cause1:" +
+ ce.getState("cause1") + " cause2: " +
+ ce.getState("cause2") + " cause3: " +
+ ce.getState("cause3"));
+});
+
+ce.on("myticker", function() {
+ console.log("Ticker ran out");
+});
+
+console.log("set state cause1");
+ce.setState("cause1", 11);
+
+console.log("set state cause3");
+ce.setState("cause3", 13);
+
+console.log("set state cause2");
+ce.setState("cause2", 12);
+
+function callback() {
+ ce.setState("myticker");
+}
+
+console.log("triggering myticker");
+for(var i = 0; i< 4; i++) {
+ setTimeout(callback, 100);
+}

0 comments on commit b3d8c93

Please sign in to comment.
Something went wrong with that request. Please try again.