nodeSpider updated: Now based on ActionHero

actionhero javascript node.js 
2012-02-13
↞ See all posts



A quick update about the nodeSpider project (my first public node.js project!): It is now based on ActionHero.

I announced the nodeSpider project here, and it was what first "sold" me on node.js as a framework. Without going into all of the details of the project, I can simply say that what used to be a very hard sync problem in C++ is now easy to do in node. Since I completed version 1 of the project, I have gone on to create actionHero which used many of the ideas from nodeSpider. Now the project has come full-circle and now implements actionHero directly.

This is a significant update, as the syntax of the outputs has been updated to match actionHero, and hopefully this makes the responses more intelligible :D this project will now benefit from the normal bug-fix pipeline of actionHero.

Here’s a new conversation between 2 peers (and the server log it generated) as an example of the updated syntax:

Client 1

1> telnet localhost 5555 2 3Trying 127.0.0.1... 4Connected to localhost. 5Escape character is '^\]'. 6 7{"welcome":"Welcome to the Node Spider communication server.","room":"defaultRoom","context":"api","messageCount":0} 8say hello from client 1 9{"context":"response","status":"OK","messageCount":1} 10{"message":"hi! from client 2","from":"ccc158b6b5ab19cff3eca71a876f83fc","context":"user","messageCount":2} 11roomView 12{"context":"response","status":"OK","room":"defaultRoom","roomStatus":{"members":\[{"id":"18c3ab44cb093ba3a400aab48fafcdbe"},{"id":"ccc158b6b5ab19cff3eca71a876f83fc"}\],"membersCount":2},"messageCount":3} 13roomView 14{"context":"response","status":"OK","room":"defaultRoom","roomStatus":{"members":\[{"id":"18c3ab44cb093ba3a400aab48fafcdbe"}\],"membersCount":1},"messageCount":4} 15roomChange secretRoom 16{"context":"response","status":"OK","room":"secretRoom","messageCount":5} 17{"message":"still talking in secret room","from":"ccc158b6b5ab19cff3eca71a876f83fc","context":"user","messageCount":6} 18{"context":"api","status":"keep-alive","serverTime":"2012-02-13T01:18:07.185Z","messageCount":7} 19quit 20{"status":"Bye!","messageCount":8} 21Connection closed by foreign host.

Client 2

1> telnet localhost 5555 2 3Trying 127.0.0.1... 4Connected to localhost. 5Escape character is '^\]'. 6 7{"welcome":"Welcome to the Node Spider communication server.","room":"defaultRoom","context":"api","messageCount":0} 8{"message":"hello from client 1","from":"18c3ab44cb093ba3a400aab48fafcdbe","context":"user","messageCount":1} 9hi! from client 2 10{"context":"response","messageCount":2} 11roomChange secretRoom 12{"context":"response","status":"OK","room":"secretRoom","messageCount":3} 13talking in secret room 14{"context":"response","messageCount":4} 15still talking in secret room 16{"context":"response","messageCount":5} 17{"context":"api","status":"keep-alive","serverTime":"2012-02-13T01:18:07.185Z","messageCount":6} 18quit 19{"status":"Bye!","messageCount":7} 20Connection closed by foreign host.

Server Log:

1$ npm start 2 3> spider@2.0.0 start /Users/evantahler/PROJECTS/nodeSpider 4> node ./spider.js 5 62012-02-12 17:17:07 | no ./tasks.js file in project, loading defaults tasks from /Users/evantahler/PROJECTS/nodeSpider/node_modules/actionHero/tasks.js 72012-02-12 17:17:07 | periodic (internal cron) interval set to process evey 60000ms 82012-02-12 17:17:07 | data cache from backup file. 92012-02-12 17:17:07 | \*\*\* Server Started @ 2012-02-12 17:17:07 @ web port 8080 & socket port 5555 \*\*\* 102012-02-12 17:17:07 | Boot Sucessful! 112012-02-12 17:17:08 | socket connection 127.0.0.1 | connected 122012-02-12 17:17:09 | socket connection 127.0.0.1 | connected 132012-02-12 17:17:13 | > socket request from 127.0.0.1 | say hello from client 1 142012-02-12 17:17:19 | > socket request from 127.0.0.1 | hi! from client 2 152012-02-12 17:17:19 | action @ 127.0.0.1 | params: {"action":"hi!"} 162012-02-12 17:17:24 | > socket request from 127.0.0.1 | roomView 172012-02-12 17:17:32 | > socket request from 127.0.0.1 | roomChange secretRoom 182012-02-12 17:17:35 | > socket request from 127.0.0.1 | roomView 192012-02-12 17:17:44 | > socket request from 127.0.0.1 | talking in secret room 202012-02-12 17:17:44 | action @ 127.0.0.1 | params: {"action":"talking"} 212012-02-12 17:17:50 | > socket request from 127.0.0.1 | roomChange secretRoom 222012-02-12 17:17:56 | > socket request from 127.0.0.1 | still talking in secret room 232012-02-12 17:17:56 | action @ 127.0.0.1 | params: {"action":"still"} 242012-02-12 17:18:07 | \* periodic cron tasks starting now \* 252012-02-12 17:18:07 | starging task: Clean cache object 262012-02-12 17:18:07 | starging task: Clean Log Files 272012-02-12 17:18:07 | starging task: caclculateStats 282012-02-12 17:18:07 | starging task: saveCacheToDisk 292012-02-12 17:18:07 | starging task: pingSocketClients 302012-02-12 17:18:07 | completed task: Clean cache object 312012-02-12 17:18:07 | completed task: Clean Log Files 322012-02-12 17:18:07 | completed task: saveCacheToDisk 332012-02-12 17:18:07 | >> pingSocketClients | sent keepAlive to 2 socket clients 342012-02-12 17:18:07 | completed task: pingSocketClients 352012-02-12 17:18:07 | completed task: caclculateStats 362012-02-12 17:18:07 | \* periodic cron tasks comple. see you again in 60000ms \* 372012-02-12 17:18:11 | > socket request from 127.0.0.1 | requesting disconnect 382012-02-12 17:18:11 | > socket connection 127.0.0.1 disconnected 392012-02-12 17:18:12 | > socket request from 127.0.0.1 | requesting disconnect 402012-02-12 17:18:12 | > socket connection 127.0.0.1 disconnected

To implement nodeSpider from actionHero, the only significant pice of code replacing the buil-in api.processAction() method with one that would assume an "unknown" action was a "say" command and should do that instead of throwing an error. Conveniently, there is already a method to update/add api methods as part of the actionHero initializer.

Here’s how I did it:

1var actionHero = require("actionHero").actionHero; 2 3var params = {}; 4params.initFunction = function (api, next) { 5 // update process action to make all sent strings a "say" by default 6 api.processAction = function (api, connection, next) { 7 if (api.configData.logRequests) { 8 api.log( 9 "action @ " + 10 connection.remoteIP + 11 " | params: " + 12 JSON.stringify(connection.params), 13 ); 14 } 15 16 if (connection.error === false) { 17 connection.action = connection.params["action"]; 18 if (api.actions[connection.action] != undefined) { 19 api.utils.requiredParamChecker( 20 api, 21 connection, 22 api.actions[connection.action].inputs.required, 23 ); 24 if (connection.error == false) { 25 process.nextTick(function () { 26 api.actions[connection.action].run(api, connection, next); 27 }); 28 } else { 29 process.nextTick(function () { 30 next(connection, true); 31 }); 32 } 33 } else { 34 if (connection.action == "" || connection.action == null) { 35 connection.action = "say"; 36 } 37 api.socketServer.socketRoomBroadcast( 38 api, 39 connection, 40 connection.lastLine, 41 ); 42 process.nextTick(function () { 43 next(connection, true); 44 }); 45 } 46 } else { 47 process.nextTick(function () { 48 next(connection, true); 49 }); 50 } 51 }; 52 53 next(); 54}; 55 56actionHero.start(params, function (api) { 57 api.webServer.webApp.close(); // turn off the webserver 58 api.log("Boot Sucessful!"); 59});

https://github.com/evantahler/nodeSpider

Hi, I'm Evan

I write about Technology, Software, and Startups. I use my Product Management, Software Engineering, and Leadership skills to build teams that create world-class digital products.

Get in touch