diff --git a/examples/README.md b/examples/README.md index 28ef0ff..e28f00d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,4 +9,3 @@ application main files. Shows how to use event system to loosely couple application modules and achieve encapsulation. Each module knows only it's own domain plus *eventist* API. - diff --git a/examples/node/console.js b/examples/node/console.js index 2913327..a141c90 100644 --- a/examples/node/console.js +++ b/examples/node/console.js @@ -1,29 +1,40 @@ /** - * Console app does nothing clever. Most of code below is actually suicidal. + * console.js + * + * This app does nothing clever, but it illustrates some basic patterns + * of event-driven design. * * @author Villem Alango */ 'use strict'; + // Event bus is the only connection between functional modules. var bus = require('eventist')(); var emit = bus.emit; var logging = true; +// Override the original method for logging purposes. bus.emit = function () { logging && emit.apply(bus, ['ui', 'log', Array.prototype.join.call(arguments, '::')]); return emit.apply(bus, arguments); }; +/* + Application logic: + - maintaining dynamic configuration and connections between modules; + - implementing some functions of existential significance. + */ bus .on('module.connected', function () { return true; }) .once('module.quit', function (name) { + // Make a dramatic farewell before finishing the seppuku. bus.send('ui', 'say', ['Ewwww... you have killed my', name, '!!! :,('].join(' ')); var left = 9 - , die = function () { + , die = function () { // ... doing it loud and slowly. if (left) { bus.send('ui', 'say', ['I\'ll die in', left, 'seconds...'].join(' ')); @@ -31,31 +42,38 @@ bus setTimeout(die, 3000); } else { bus.send('ui', 'say', 'Good-bye, Cruel World!'); - bus.send('app', 'close', 'world!'); + bus.send('app', 'close'); } }; die(); }) + // Some application-level events are handled here. .on('app', function (cmd, a) { - if (cmd === 'close') { - bus.send('ui', 'say', 'Have a great day!'); - process.exit(0); - } else if (cmd === 'load.filter') { - bus.send('ui', 'say', 'loading sfaety filter...'); - require('./filter')(bus); - bus.send('ui', 'prompt'); - }else if(cmd === 'log'){ - logging = !!a; - } else { - return void 0; + switch (cmd) { + case 'close': + bus.send('ui', 'say', 'Have a great day!'); + process.exit(0); + break; // Just to keep jshint happy. + + case 'load.filter': + bus.send('ui', 'say', 'loading safety filter...'); + require('./filter')(bus); + bus.send('ui', 'prompt'); + break; + + case 'log': + logging = !!a; + break; + + default: + return void 0; } return true; - }) -; + }); // Here we load functional modules require('./ui-simple')(bus); require('./decoder')(bus); -// Now we are good to go... +// And now we are good to go... bus.send('ui', 'prompt'); diff --git a/examples/node/decoder/index.js b/examples/node/decoder/index.js index b955fc1..3b216ed 100644 --- a/examples/node/decoder/index.js +++ b/examples/node/decoder/index.js @@ -7,59 +7,83 @@ 'use strict'; var bus; -var tolerance = 1; +var tolerance = 1; // Crossing 0 threshold will start suicide sequence. +var retry = false; // Flag preventing potential message translation deadlock. +var decode; -var logics = function (command, a) { +/** + * Make a last attempt to handle unknown user input and succumb to depression + * if that fails. + * + * @param {string} command + */ +var lastTry = function(command){ + + var res = !retry && bus.emit('app', 'bad-user', command); + + if (res !== void 0) { // Take a chance with translated value. + res && (retry = true) && bus.send('user', res); + } else if (tolerance) { + bus.send('ui', 'say', + ['I do not understand! Type "help", if you mind...', + tolerance, 'try left.'].join(' ')); + --tolerance; + bus.send('ui', 'prompt'); + } else { // Module suicide - letting the whole world to know. + bus.off('user', decode); + bus.send('module.quit', 'decoder'); + bus = void 0; + } +}; + +/* jshint maxcomplexity:10 */ +decode = function (command) { switch (command) { case 'help': bus.send('ui', 'say', 'Available commands: clear, filter, help, log on, log off, hello, exit, quit'); break; + case 'clear': bus.send('ui', 'clear'); break; + case 'filter': bus.send('app', 'load.filter'); - return true; + return true; // Avoid an extra prompt. + case 'hello': bus.send('ui', 'say', 'world!'); break; + case 'log': case 'log on': bus.send('app', 'log', 1); break; + case 'log off': bus.send('app', 'log', 0); break; + case 'exit': case 'quit': bus.send('app', 'close'); break; + default: - if (tolerance) { - bus.send('ui', 'say', - ['I do not understand! Type "help" if you mind...', tolerance, 'try left.'].join(' ')); - --tolerance; - bus.send('ui', 'prompt'); - } else { - bus.off('user', logics); - bus.send('module.quit', 'decoder'); - bus = void 0; - } - return; + return lastTry(command); } - tolerance = 1; + retry = false; // Here after successful processing - + tolerance = 1; // reset the doom counter. bus.send('ui', 'prompt'); return true; }; var init = function (busInstance) { - bus = busInstance; - - bus - .on('user', logics) + (bus = busInstance) + .on('user', decode) .send('module.connected', 'decoder'); }; diff --git a/examples/node/filter/index.js b/examples/node/filter/index.js index 50dc91a..d53ff36 100644 --- a/examples/node/filter/index.js +++ b/examples/node/filter/index.js @@ -8,33 +8,47 @@ var bus, count = 0; -var logics = function (command) { - - switch (command) { - case 'help': - case 'clear': - case 'filter': - case 'hello': - case 'log': - case 'log on': - case 'log off': - case 'exit': - case 'quit': - return void 0; +/* + Event app/bad-user/command is emitted by decoder on unknown command. + Replacement to this command can be sent as a return value. + */ +var translate = function (command) { + + if (command === 'bad-user') { + if (--count <= 0) { // Provide help, but not too often. + count = 5; + return 'help'; + } + bus.send('ui', 'prompt'); + return null; // User input will be ignored. } - if (--count <= 0) { - bus.send('user', 'help'); - count = 5; +}; + +/* + Because being registered after original decoder, this handler receives + user input before the original one and can effectively filter/translate + everything. + */ +var decode = function (command) { + if (command === 'help') { // Eat 'help' command... + if (count !== 5) { // except when it was sent by ourselves. + bus.send('ui', 'prompt'); + return true; // Just disable it + } + } else if (command === 'filter off') { // Process special command here. + bus.send('ui', 'say', 'disbling safety filter...') + .off('app', translate).off('user', decode) + .send('ui', 'set.prompt', 'OHAY').send('ui', 'prompt'); + return true; } - return true; + // Returning undefined value here allows the original decoder to be invoked. }; var init = function (busInstance) { - bus = busInstance; - - bus - .on('user', logics) + (bus = busInstance) + .on('app', translate) + .on('user', decode) .send('module.connected', 'filter') .send('ui', 'set.prompt', 'BABY'); }; diff --git a/examples/node/ui-simple/index.js b/examples/node/ui-simple/index.js index 58bb7eb..4a618f0 100644 --- a/examples/node/ui-simple/index.js +++ b/examples/node/ui-simple/index.js @@ -1,6 +1,6 @@ /** - * Simple UI receives translates user input and performs rudimentary - * output functions. + * Simple UI receives translates user input into 'user' events + * and performs rudimentary output functions. * * @author Villem Alango */ @@ -58,18 +58,16 @@ var logics = function (cmd, a1) { var init = function (busInstance) { - bus = busInstance; - - bus + (bus = busInstance) .on('ui', logics) .send('module.connected', name); clear(); setPrompt('OHAI'); + // Actual input feed event listener. rl.on('line', function (line) { var input = line.trim(); - bus.send('user', input); }).on('close', function () { bus.off('ui', logics).emit('app', 'close'); diff --git a/package.json b/package.json index d8d43e8..3f9c336 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "postinstall": "examples/initialize" }, "keywords": [ + "bus", "event", "emitter", "extendable",