Last active
August 29, 2015 14:16
-
-
Save nackjicholson/aaf48a61d42a62c581b6 to your computer and use it in GitHub Desktop.
The Widget King A tale written in javascript monad streams
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
// What do you guys think about this as a way to possibly pass streams | |
// around from one module to another? I don't think it comes up in simple | |
// examples but I can see in complex cases where if you wanted a separation of | |
// concerns for a stream, that you might want them to be passed around. It | |
// kind of opens up a protocol for communication between modules. | |
// | |
// Essentially this is a widgetMaker module which is responsible for making | |
// widgets. And the widgetKing module wants to be notified every time a widget | |
// comes off the factory floor. | |
// Lol IIFE's and Module Pattern to get this all in one file. | |
// think of the returns from each module as "module.exports" if they | |
// were in separate files. | |
var most = require('most'); | |
var Q = require('q'); | |
var colors = require('colors/safe'); | |
// Character voices / wrapper for console.log. | |
var say = function() { | |
console.log.apply(undefined, arguments); | |
}; | |
// Alias some colors. | |
var g = colors.green; | |
var b = colors.blue; | |
/** | |
* This is the MAGIC ingredient which allows the modules to communicate. | |
* Creates a new most stream from a promise which is waiting on a stream. | |
* | |
* @param {Promise} streamPromise | |
* @returns {Stream} | |
*/ | |
var mostFromStreamPromise = function(streamPromise) { | |
return most.create(function(add, end, error) { | |
streamPromise.then(function(stream) { | |
stream.observe(add).then(end, error); | |
}); | |
}); | |
}; | |
/** | |
* Module1 The WidgetMaker | |
* | |
* He utilizes the "mostFromStreamPromise" to clone two streams to | |
* perform tasks when he receives a "widgetRequest" from the king. One stream | |
* allows the widget maker to make widgets, and the other collects the money | |
* in the incoming requests and calculates his tax payments to the | |
* honorable king. | |
* | |
* @exports taxes | |
* @exports widgets | |
*/ | |
var widgetMaker = (function() { | |
var requestsDeferred = Q.defer(); | |
var moneyInTheBank = 0; | |
/** | |
* A copy of the widget request stream, which will be created | |
* when the request stream is registered. The resultant "widgets" | |
* stream is responsible for making widgets. | |
* | |
* @type {Stream|*} | |
*/ | |
var widgets = mostFromStreamPromise(requestsDeferred.promise) | |
.scan(makeWidget, {id: 0}) | |
.skip(1) | |
.tap(notifyWidgetComplete); | |
/** | |
* Also a copy of the widget request stream. This one collects | |
* money from the requests, and calculates taxes, which it then | |
* exports. | |
* | |
* @type {Stream|*} | |
*/ | |
var taxes = mostFromStreamPromise(requestsDeferred.promise) | |
.tap(putMoneyInTheBank) | |
.map(giveTheKingHisCut); | |
/** | |
* Registers the input streams the WidgetMaker susbscribes to. | |
* Just the never satisfying periodic dribble of widget requests. | |
*/ | |
function register(widgetKing) { | |
requestsDeferred.resolve(widgetKing.widgetRequests); | |
} | |
/** | |
* scan - Creates a widget with an incremented widgetId, and owner. | |
*/ | |
function makeWidget(acc, request) { | |
say(b('Making a widget.')); | |
acc.id++; | |
acc.widgetId = request.purchaserName + '-' + acc.id; | |
acc.owner = request.purchaserName; | |
return acc; | |
} | |
/** | |
* tap - logging. | |
*/ | |
function notifyWidgetComplete(widget) { | |
say(b('Widget off the line.', JSON.stringify(widget), '\n')); | |
} | |
/** | |
* tap - collect money from requests. | |
*/ | |
function putMoneyInTheBank(request) { | |
say(b('Widget request received, collecting cash')); | |
moneyInTheBank += request.payment; | |
say(b('Wowzers I have: $' + moneyInTheBank + '\n')); | |
} | |
/** | |
* map - calculates taxes, complains about it, and reluctantly returns cut. | |
*/ | |
function giveTheKingHisCut(request) { | |
var cut = request.payment * 0.5; | |
moneyInTheBank -= cut; | |
say(b('I hate taxes, boo hoo '), '-$' + moneyInTheBank); | |
return cut; | |
} | |
// Think of this like module.exports | |
return { | |
register: register, | |
taxes: taxes, | |
widgets: widgets | |
}; | |
})(); | |
/** | |
* Module2 The WidgetKing | |
* | |
* The widget king generates his widget requests - five requests over five | |
* seconds. He has a "mostFromStreamPromise" copy of widgets so he knows when | |
* he receives one of his widgets. He then adds it to his collection. Another | |
* task the king does is sit back and add to his fat stack of cash through | |
* tax collection. | |
* | |
* @exports widgetRequests | |
*/ | |
var widgetKing = (function(kingName) { | |
var moneyInTheBank = 1000000; | |
var widgetsDeferred = Q.defer(); | |
var taxesDeferred = Q.defer(); | |
/** | |
* Copy of incoming widgetMaker.widgets | |
* Let's the king stay up on the widgets he receives. | |
*/ | |
var widgets = mostFromStreamPromise(widgetsDeferred.promise); | |
/** | |
* Copy of the incoming widgetMaker.taxes stream | |
* Gives the king money, which he stores in his bank. | |
*/ | |
var taxes = mostFromStreamPromise(taxesDeferred.promise); | |
/** | |
* Periodic generation of 5 widget requests. Exported | |
*/ | |
var widgetRequests = most | |
.periodic(1000, {purchaserName: kingName, payment: 1}) | |
.take(5); | |
widgets | |
.reduce(collectWidgets, {collection: []}) | |
.then(proclaimGreatnessThroughMaterialWealth, console.error); | |
taxes | |
.observe(sitBackAndTakeMoney) | |
.then(null, console.error); | |
/** | |
* This function registers the input streams the king subscribes to. | |
* Namely -- his mother fucking widgets AND his coin! | |
*/ | |
function register(widgetMaker) { | |
widgetsDeferred.resolve(widgetMaker.widgets); | |
taxesDeferred.resolve(widgetMaker.taxes); | |
} | |
/** | |
* reduce - puts widgets in collection, returns accumulated final result. | |
*/ | |
function collectWidgets(acc, widget) { | |
acc.collection.push(widget); | |
return acc; | |
} | |
/** | |
* observe - Collects tax money in the bank. | |
*/ | |
function sitBackAndTakeMoney(taxMoney) { | |
moneyInTheBank += taxMoney; | |
say(g('Gettin\' that paper'), moneyInTheBank, '\n'); | |
} | |
/** | |
* Reduce Promise succes handler, does what it says it does. | |
*/ | |
function proclaimGreatnessThroughMaterialWealth(result) { | |
say(g('I am KING ' + kingName.toUpperCase() + ' behold my Widgets!')); | |
// I'm either doing this wrong or this doesn't work right. Opening a github question. | |
// result ends up with five of the last widget........wat? | |
//say(g(JSON.stringify(result))); | |
} | |
return { | |
register: register, | |
widgetRequests: widgetRequests | |
}; | |
})('nackjicholson'); | |
/** | |
* Module Binding...no | |
* Module Coupling...maybe | |
* Module Attach...definitely not | |
* Module Adhesion...also no. | |
*/ | |
widgetMaker.register(widgetKing); | |
widgetKing.register(widgetMaker); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Terminal output:
https://wesleybauman.files.wordpress.com/2015/03/screen-shot-2015-03-02-at-11-28-47-pm.png?w=1000