daily pastebin goal
38%
SHARE
TWEET

testing/learning utility library for node.js

mvaganov Mar 10th, 2015 173 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * Custom functionality written by Michael Vaganov, while learning Node.js and JavaScript
  3.  * MIT License.
  4.  * @module mvaganov
  5.  */
  6.  
  7. var url = require("url");
  8.  
  9. /**
  10.  * add functionality to the String implementation.... pretty sweet that JavaScript can do this
  11.  */
  12. function addToStrings()
  13. {       "use strict";
  14.         if (typeof String.prototype.startsWith !== 'function')
  15.         {
  16.                 String.prototype.startsWith = function (str){
  17.                         return this.substring(0, str.length) === str; //this.slice(0, str.length) === str;
  18.                 }
  19.         }
  20.         if (typeof String.prototype.replaceAll !== 'function')
  21.         {
  22.                 /**
  23.                  * @param {string} str1
  24.                  * @param {string} str2
  25.                  * @param {(boolean|null)} ignore ignore case?
  26.                  * @return {string} a new string, a copy of this one, with all instances of str1 replaced with str2.
  27.                  */
  28.                 String.prototype.replaceAll = function(str1, str2, ignore)
  29.                 {
  30.                         return this.replace(new RegExp(
  31.                                 str1.replace(
  32.                                         /([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, "\\$&"
  33.                                 ),
  34.                                 (ignore?"gi":"g")),
  35.                                 (typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2
  36.                         );
  37.                 }
  38.         }
  39.         if (typeof String.prototype.indexOfOneOfThese !== 'function')
  40.         {
  41.                 /**
  42.                  * @param {Array<String>} listOfDelimeters possible string delimeters that are being sought after
  43.                  * @param {number=} start where to start looking in the string
  44.                  * @return {Array<number>} [index that has one of these first, which delimeter was actually found here].
  45.                  * if none of these exist, returns [this.length, -1]
  46.                  * return[0] is the index
  47.                  * return[1] is the index of the delimeter that was found
  48.                  */
  49.                 String.prototype.indexOfOneOfThese = function (listOfDelimeters, start)
  50.                 {
  51.                         var bestIndex = this.length;
  52.                         var foundDelimeter = -1;
  53.                         if(start == null) start = 0;
  54.                         for(var i = 0; i < listOfDelimeters.length; ++i)
  55.                         {
  56.                                 var index = this.indexOf(listOfDelimeters[i], start);
  57.                                 if(index >= 0 && index < bestIndex)
  58.                                 {
  59.                                         foundDelimeter  = i;
  60.                                         bestIndex = index;
  61.                                 }
  62.                         }
  63.                         return [bestIndex, foundDelimeter];
  64.                 }
  65.         }
  66.         if (typeof String.prototype.splitByOneOfThese !== 'function')
  67.         {
  68.                 /**
  69.                  * @param {Array<String>} listOfDelimeters possible string delimeters that are being sought after
  70.                  * @param {Number} maxSplits how many times to split. will split from left to right. -1 means no limit
  71.                  * @return {Array<String>} as split, except with multiple delimeter tokens
  72.                  */
  73.                 String.prototype.splitByOneOfThese = function (listOfDelimeters, maxSplits)
  74.                 {
  75.                         if(maxSplits == null) maxSplits = -1;
  76.                         var splitted = [], index = 0, whereToSplit, segment, splitCount = 0;
  77.                         for(var i = 0; index < this.length; ++i)
  78.                         {
  79.                                 if(maxSplits >= 0 && splitCount >= maxSplits)
  80.                                 {
  81.                                         whereToSplit = [this.length, -1];
  82.                                 }
  83.                                 else
  84.                                 {
  85.                                         whereToSplit = this.indexOfOneOfThese(listOfDelimeters, index);
  86.                                 }
  87.                                 segment = this.slice(index, whereToSplit[0]);
  88.                                 //console.log("("+index+", "+whereToSplit[0]+"... "+whereToSplit[1]+") ="+segment);
  89.                                 splitCount++;
  90.                                 if(segment.length > 0)
  91.                                 {
  92.                                         splitted.push(segment);
  93.                                 }
  94.                                 index = whereToSplit[0];
  95.                                 if(whereToSplit[1] != -1)
  96.                                 {
  97.                                         index += listOfDelimeters[whereToSplit[1]].length;
  98.                                 }
  99.                         }
  100.                         return splitted;
  101.                 }
  102.         }
  103.         if (typeof String.prototype.splitIntoTable !== 'function')
  104.         {
  105.                 /**
  106.                  * @param {Array<String>} listOfEntryDelimeters example: {@code ["{", "}", ","]}
  107.                  * @param {Array<String>} listOfAssignmentDelimeters example: {@code ["=",":"]}
  108.                  * @return {Object<String,String>} a key/value pair table. see {@link String#prototype#parseCookies} as an example
  109.                  */
  110.                 String.prototype.splitIntoTable = function (listOfEntryDelimeters, listOfAssignmentDelimeters)
  111.                 {
  112.                         // first, split by entry delimeters
  113.                         var entries = this.splitByOneOfThese(listOfEntryDelimeters);
  114.                         // then split in half by the assignment delimeter
  115.                         var table = {};
  116.                         for(var i = 0; i < entries.length; ++i)
  117.                         {
  118.                                 var pair = entries[i].splitByOneOfThese(listOfAssignmentDelimeters, 1);
  119.                                 if(pair.length > 1)
  120.                                 {
  121.                                         table[pair[0]] = pair[1];
  122.                                 }
  123.                                 else
  124.                                 {
  125.                                         table[pair[0]] = null;
  126.                                 }
  127.                         }
  128.                         return table;
  129.                 }
  130.         }
  131.         if (typeof String.prototype.parseCookies !== 'function')
  132.         {
  133.                 /**
  134.                  * @return {Map<String,String>} a table of cookies parameters, assuming this is formatted like an html cookie.
  135.                  */
  136.                 String.prototype.parseCookies = function ()
  137.                 {
  138.                         return this.splitIntoTable([";", " "], ["=", ":"]);
  139.                 }
  140.         }
  141.         if (typeof ObjectId.prototype.getTimestamp !== 'function')
  142.         {
  143.                 ObjectId.prototype.getTimestamp = function()
  144.                 {
  145.                         return new Date(parseInt(this.toString().slice(0,8), 16)*1000);
  146.                 }
  147.         }
  148. }
  149. addToStrings();
  150.  
  151. /** a list of all of the modules this machine can access, built by the command line "npm ls --json" */
  152. var npmListing = null;
  153. var processStarted = false;
  154. function gatherNpmListing() {
  155.         if(processStarted) return;
  156.         processStarted = true;
  157.         // try to get other installed modules from the npm command line tool
  158.         require("child_process").exec("npm ls --json",
  159.                 function(err, stdout, stderr) {
  160.                         if (err) return console.log(err)
  161.                         npmListing = JSON.parse(stdout);
  162.                         // add the libraries to the npmListing
  163.                         function addTheModuleToThisListing(npmListing, moduleName)
  164.                         {
  165.                                 console.log(moduleName);
  166.                                 if(moduleName != null)
  167.                                 {
  168.                                         try{
  169.                                                 npmListing["module"] = require(moduleName);
  170.                                         }catch(err){
  171.                                                 npmListing["module"] = err;
  172.                                                 //console.log("couldn't load "+moduleName);
  173.                                         }
  174.                                 }
  175.                                 if(npmListing["dependencies"])
  176.                                 {
  177.                                         for(var d in npmListing.dependencies)
  178.                                         {
  179.                                                 addTheModuleToThisListing(npmListing.dependencies[d], d);
  180.                                         }
  181.                                 }
  182.                                 // after the root is done, print.
  183.                                 if(moduleName == null)
  184.                                 {
  185.                                         printReflectionInConsole(npmListing, "npmListing", 3);
  186.                                 }
  187.                         }(npmListing); // call the function right after defining it
  188.                 }
  189.         );
  190. }
  191.  
  192. /** @param {Object} obj print an object as a JSON string, including function code */
  193. function toJSONWithFuncs(obj)
  194. {
  195.         Object.prototype.toJSON = function()
  196.         {
  197.                 var sobj = {}, i;
  198.                 for (i in this)
  199.                         if (this.hasOwnProperty(i))
  200.                                 sobj[i] = typeof this[i] == 'function' ? this[i].toString() : this[i];
  201.                 return sobj;
  202.         };
  203.         var str = JSON.stringify(obj);
  204.         delete Object.prototype.toJSON;
  205.         return str;
  206. }
  207.  
  208. /** how deep a tree to show when including the HTML traversal of JavaScriptObjects @type Number */
  209. var maxIndentLevel = 0;
  210. /** if true, will spam the server's console whenever {@link #createReflectedHtmlForJso} is called @type Boolean */
  211. var printInConsole = false;//true;
  212. var showPrivateVariables = true;
  213. var javaScriptObjectTraversalParameter = "jso";
  214.  
  215. /**
  216.  * creates a simple directory traversal interface using anchor tags
  217.  * @param {?} obj which object is being traversed (can be any type, including null)
  218.  * @param {?String=} pathStr the path (through parent objects) taken to get here (optional, can be null)
  219.  * @param {Number=} indentLevel used to properly indent, and prevent infinite recursion. limited by {@link #maxIndentLevel} (optional)
  220.  * @return {String} the HTML script that will allow traversal. the key parameter is {@link javaScriptObjectTraversalParameter}
  221.  */
  222. function createReflectedHtmlForJso(obj, pathStr, indentLevel)
  223. {       "use strict";
  224.         var indentation = "", i = 0, j = 0, props = [], functions = [], childText = [], nChildTexts = 0, key, strResult = "";
  225.         // default arguments
  226.         if ( typeof pathStr === 'undefined')            pathStr = "";
  227.         if ( typeof indentLevel === 'undefined')        indentLevel = 0;
  228.         // write the "path" at the top
  229.         if(indentLevel == 0)
  230.         {
  231.                 strResult = "\n<h1>@ ";
  232.                 var arr = pathStr.split(".");
  233.                 for(i = 0; i < arr.length; ++i)
  234.                 {
  235.                         if(i > 0) strResult += ".";
  236.                         strResult += "<a href=\"/?"+javaScriptObjectTraversalParameter+"=";
  237.                         for(j = 0; j < i; ++j)
  238.                         {
  239.                                 strResult += arr[j] + ".";
  240.                         }
  241.                         strResult += arr[i] + "\">"+arr[i]+"</a>";
  242.                 }
  243.                 strResult += "</h1>\n";
  244.         }
  245.         // for debug output in the console...
  246.         if(printInConsole)
  247.                 for(i=0; i < indentLevel; i++)
  248.                         indentation = indentation.concat("    ");
  249.         // reflect a JS functions
  250.         if(typeof obj === 'function')
  251.         {
  252.                 strResult += "<pre>"+obj.toString()+"</pre>";
  253.                 if(printInConsole) console.log(obj.toString());
  254.         }
  255.         // reflect simple types
  256.         else if(typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean')
  257.         {
  258.                 strResult += "<b>"+(typeof obj)+"</b> = "+obj+"\n";
  259.                 if(printInConsole) console.log((typeof obj)+" = "+obj);
  260.         }
  261.         // reflect objects
  262.         else if(typeof obj === 'object')
  263.         {
  264.                 // null objects don't need all of this processing
  265.                 if(obj != null)
  266.                 {
  267.                         // asks an object for all of it's members
  268.                         for(key in obj)
  269.                         {
  270.                                 if(obj.hasOwnProperty(key) && (showPrivateVariables || !key.startsWith('_')))
  271.                                 {
  272.                                         if(typeof obj[key] !== 'function')
  273.                                         {
  274.                                                 if(printInConsole) console.log(indentation+key+" : "+typeof obj[key]+" = "+obj[key]);
  275.                                                 props.push(key);
  276.                                                 if(typeof obj[key] === 'object'
  277.                                                 && indentLevel < maxIndentLevel)
  278.                                                 {
  279.                                                         var childStr;
  280.                                                         if(obj[key] == null)
  281.                                                                 childStr = "";
  282.                                                         else
  283.                                                                 childStr = createReflectedHtmlForJso(obj[key], pathStr+"."+key, indentLevel+1);
  284.                                                         if(indentLevel < maxIndentLevel)
  285.                                                         {
  286.                                                                 childText[nChildTexts] = childStr;
  287.                                                                 nChildTexts += 1;
  288.                                                         }
  289.                                                 }
  290.                                         }
  291.                                         else
  292.                                         {
  293.                                                 functions.push(key);
  294.                                         }
  295.                                 }
  296.                         }
  297.                         strResult += "<ul>"
  298.                         j = 0;
  299.                         // print child elements
  300.                         for(i = 0; i < props.length; ++i)
  301.                         {
  302.                                 key = props[i];
  303.                                 strResult += "<li><a href=\"/?"+javaScriptObjectTraversalParameter+"="+pathStr+"."+key+"\">"+key+"</a> : <b>"+(typeof obj[key])+"</b> = <i>"+obj[key]+"</i>";
  304.                                 // include members from the first tier of child objects
  305.                                 if(typeof obj[key] === 'object' && obj[key] != null)
  306.                                 {
  307.                                         strResult += "["+Object.keys(obj[key]).length+"]";
  308.                                         if(indentLevel < maxIndentLevel)
  309.                                         {
  310.                                                 strResult += childText[j];
  311.                                                 j++;
  312.                                         }
  313.                                 }
  314.                                 strResult += "\n";
  315.                         }
  316.                         for(i = 0; i < functions.length; ++i)
  317.                         {
  318.                                 key = functions[i];
  319.                                 var codestr = obj[key].toString();
  320.                                 codestr = codestr.slice(0, codestr.indexOf('{'));
  321.                                 if(printInConsole) console.log(indentation+key+" : "+typeof obj[key]+" = "+codestr);
  322.                                 codestr = codestr.replaceAll(" ", "&nbsp");
  323.                                 codestr = codestr.replaceAll("\n", "<br>");
  324.                                 strResult += "<li><a href=\"/?"+javaScriptObjectTraversalParameter+"="+pathStr+"."+key+"\">"+key+"</a> : <font face=\"courier\">"+codestr+"</font>\n";
  325.                         }
  326.                         strResult += "</ul>";
  327.                 }
  328.                 else
  329.                 {
  330.                         strResult += "<b>null</b>";
  331.                 }
  332.         }
  333.         else
  334.         {
  335.                 strResult += "unknown JavaScriptObject<pre>"+obj+"</pre>";
  336.                 if(printInConsole) console.log("unknown type ("+(typeof obj)+"): "+obj);
  337.         }
  338.         return strResult;
  339. }
  340.  
  341. /**
  342.  * @nosideeffects
  343.  * @param {?Object=} obj what object to print out
  344.  * @param {?String=} name how to label this object when printing in the console
  345.  * @param {Number=} depth how many children deep to print out. if null, {@link #maxIndentLevel} is used.
  346.  */
  347.  function printReflectionInConsole(obj, name, depth)
  348. {
  349.         if(name != null)
  350.         {
  351.                 console.log("[["+name+"]]");
  352.         }
  353.         var oldMax = maxIndentLevel;
  354.         if(depth != null)
  355.         {
  356.                 maxIndentLevel = depth;
  357.         }
  358.         printInConsole = true;
  359.         createReflectedHtmlForJso(obj);
  360.         printInConsole = false;
  361.         if(oldMax != maxIndentLevel)
  362.         {
  363.                 maxIndentLevel = oldMax;
  364.         }
  365. }
  366.  
  367. function jsoNavigation(nameToEvaluate, localVariables)
  368. {
  369.         var whatObjectToLookAt = null;
  370.         var explicitlyNull = false;
  371.         var strOutput = "";
  372.         var jso = localVariables;
  373.         if(nameToEvaluate != null && nameToEvaluate !== "")
  374.         {
  375.                 try{
  376.                         //whatObjectToLookAt = eval(nameToEvaluate); // breaks if member has a strange character in the name
  377.                         var p = nameToEvaluate.split('.');
  378.                         var cursor = eval(p[0]);
  379.                         for(var i = 1; i < p.length; ++i)
  380.                         {
  381.                                 cursor = cursor[p[i]];
  382.                         }
  383.                         whatObjectToLookAt = cursor;
  384.                         //console.log("found <"+nameToEvaluate+">");
  385.                         explicitlyNull = true;
  386.                 }catch(err){
  387.                         console.log("couldn't parse \""+nameToEvaluate+"\" : "+err);
  388.                 }
  389.         }
  390.         //console.log(whatObjectToLookAt+" "+explicitlyNull)
  391.         if(whatObjectToLookAt == null && !explicitlyNull)
  392.         {
  393.                 strOutput += "<a href=\"/?"+javaScriptObjectTraversalParameter+
  394.                         "=jso\">jso</a><br>\n";
  395.                 //console.log(strOutput);
  396.         }
  397.         else
  398.         {
  399.                 var reflectedString = createReflectedHtmlForJso(whatObjectToLookAt, nameToEvaluate);
  400.                 strOutput += reflectedString;
  401.         }
  402.         return strOutput;
  403. }
  404.  
  405.  /**
  406.  * @param request {HTTPRequest} the HTTP request
  407.  * @param request {HTTPResponse} the HTTP response
  408.  */
  409.  function jsoNavHtml(request, response)
  410. {
  411.         extraVariables = ["./mvaganov", "./router", "./helloserver", "./requesthandler", "./db",
  412.                 // "formidable", "express"
  413.                 // "http", "fs", "sys", "util", "querystring" // these are in process.moduleLoadList
  414.                 ];
  415.         // get native modules from 'process'
  416.         var i;
  417.         var loadListModules = process.moduleLoadList;
  418.         var nativeModleLabel = "NativeModule ";
  419.         var entry;
  420.         for(i = 0; i < loadListModules.length; ++i)
  421.         {
  422.                 entry = loadListModules[i];
  423.                 if(entry.startsWith(nativeModleLabel))
  424.                 {
  425.                         extraVariables.push(entry.slice(nativeModleLabel.length, entry.length));
  426.                 }
  427.         }
  428.         // load the basic (known) modules up...
  429.         var localVariables = [];
  430.         for(i = 0; i < extraVariables.length; ++i)
  431.         {
  432.                 var str = extraVariables[i];
  433.                 var name = str;
  434.                 if(name.startsWith("./"))
  435.                 {
  436.                         name = name.slice(2, name.length);
  437.                 }
  438.                 var loadedModule;
  439.                 try
  440.                 {
  441.                         loadedModule = require(str);
  442.                 }
  443.                 catch(err)
  444.                 {
  445.                         loadedModule = "ERROR: could not load module."
  446.                 }
  447.                 localVariables[name] = loadedModule;
  448.         }
  449.         // add the global process, and the request/response of this HTTP event
  450.         localVariables["process"] = process;
  451.         localVariables["request"] = request;
  452.         localVariables["response"] = response;
  453.         // try to get other installed modules from the npm command line tool
  454.         gatherNpmListing();
  455.         if(npmListing != null)
  456.         {
  457.                 localVariables["npmListing"] = npmListing;
  458.         }
  459.  
  460.         //var q = querystring.parse(request.url);
  461.         var parsedURL = url.parse(request.url);
  462.         var fullpath = parsedURL.path;
  463.         var argsIndex = fullpath.indexOfOneOfThese(['?', '&'])[0];
  464.         //var pathname = fullpath.slice(0, argsIndex);
  465.         var pathargs = fullpath.slice(argsIndex+1, fullpath.length);
  466.         var arguments = pathargs.splitIntoTable(["&", "?"], ["="]);
  467.         //printReflectionInConsole(arguments, "---ARGUMENTS---");
  468.         var nameToEvaluate = arguments[javaScriptObjectTraversalParameter];
  469.         if(nameToEvaluate != null)
  470.         {
  471.                 return jsoNavigation(nameToEvaluate, localVariables);
  472.         }
  473.         return "";
  474. }
  475.  
  476. function jsoNav(request, response, next)
  477. {
  478.         var htmlOutput = jsoNavHtml(request, response);
  479.         if(htmlOutput && htmlOutput.length > 0)
  480.         {
  481.                 response.setHeader("Content-Type", "text/html");
  482.                 response.write(htmlOutput);
  483.                 response.end();
  484.         }
  485.         else
  486.         {
  487.                 next();
  488.         }
  489. }
  490.  
  491. /** TODO reasearch the following
  492.  
  493. * for a game loop on the server: http://nodejs.org/api/globals.html#globals_settimeout_cb_ms
  494.  
  495. * for heavy lifty C code, call exec:
  496. function execCbPageTest()
  497. {
  498.         var exec = require("child_process").exec;
  499.         exec("dir",
  500.                 function resultCallBack(error, stdout, stderr)
  501.                 {
  502.                         if (error !== null) {
  503.                                 console.log('exec error: ' + error);
  504.                         }
  505.                         stdout = stdout.toString();
  506.                         stdout = stdout.replaceAll("<", "&lt");
  507.                         stdout = stdout.replaceAll(">", "&gt");
  508.                         response.writeHead(200, {"Content-Type": "text/html"});
  509.                         response.write("<pre>"+stdout+"</pre>");
  510.                         router.writeHtmlComponents(response, htmlComponentsToAdd);
  511.                         response.end();
  512.                 }
  513.         );
  514. }
  515.  
  516. * learn more about Express via this tutorial: http://expressjs.com/guide.html
  517.  
  518. * define object prototypes by using the prototype keyword, or __proto__ (would that work?)
  519.  
  520. * events, and event emitters http://www.sitepoint.com/nodejs-events-and-eventemitter/
  521.  
  522. * https://npmjs.org/browse/keyword/middleware/0/
  523.   * gauth Middleware component to authenticate users through their Google account
  524.   * authenticated ensure a request is authenticated
  525.  
  526. * JSDocs used by Google:
  527.   https://developers.google.com/closure/compiler/docs/js-for-compiler#tags
  528.   http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Comments#Comments
  529.  
  530.  
  531. * modules to check out
  532.   socket.io - real-time network communication
  533.   request - simplified http reqeust client. has authentication, and other cool stuff?
  534.   grunt - large scale automation
  535.   mocha - testing framework
  536.   async - more powerful asynchronous tools & structures for node.js
  537.   mongoose - ORM (object relational managed) database
  538.   passport - simple authentication for node.js
  539.  
  540. * modules that seem bad
  541.   redis - big fancy data structure store... like a global variables crutch?
  542. */
  543. exports.jsoNav = jsoNav;
  544. exports.jsoNavHtml = jsoNavHtml;
  545. exports.printReflectionInConsole = printReflectionInConsole;
  546. exports.logjso = printReflectionInConsole;
  547. exports.toJSONWithFuncs = toJSONWithFuncs;
RAW Paste Data
Top