verbotenjs

A general application development framework for node.js, browsers, and other hosts

VerbotenJS Development Framework

Because everybody will think it is verboten anyway, I present to you VerbotenJS.

"Maintainable JavaScript: Don't modify objects you don't own." - Nicholas C. Zakas

I, Marcus Pope, by imperial proclamation hereby declare ownership of Object.prototype. As sole owner I am the only authorized person to modify the prototype definition. Nope, too late I already called it and double stamped it. You can't tripple stamp a double stamp!

VerbotenJS is a general application development framework desined for NodeJS & Browser JavaScript hosts. In addition to a bunch of base prototype enhancements and terse programming patterns, the framework also includes extension libraries for various domains such as databases, filesystems, shell scripts etc.

VerbotenJS strives to abstract the minutia of JavaScript development into helper functions on the various base prototype chains like Function, Date, Number, Object etc. I also tend to normalize interfaces between async and sync nodejs functions by checking for the existence of a callback argument.

Nobody, it's verboten remember?!? No I'm kidding, you can use it for any personal project you want. I don't recommend using it for production systems since it is version 1.0 and I'm just one man trying to support his own personal JS framework. I'll probably change framework logic on a whim too, because that too is considered verboten right?

Well, let's talk licensing and target market. But really you should probably wait until 2.0 at least before considering something for production.

No, I don't, it's verboten to do so remember? Actually here's the deal. jQuery is not compatible with Object.prototype extensions. I had intended on releasing my own DOM, Event and Ajax wrapper but I ran out of time. So I decided to inject a forked version of jQuery which added almost 350k to the footprint. My DOM wrapper will probably put the library in the 500k realm for browser hosts. For NodeJS hosts modules are dynamically included on access so the runtime memory usage requirements are dynamic based on the modules you use.

But by today's standards 750k is not really that bad except for mobile devices and cellular networks. If you must you can compress and gzip it down to 75k but with modern bandwidth and caching infrastructures it really isn't that bad.

Good for you. Play around a little, find out how good it feels to be bad for once in your life :D

First grab a copy of verbotenjs from npm.

npm install verbotenjs

In node hosts use:

require('verbotenjs');

For browser hosts include a reference to this file

[verbotenjs]/web/verboten.js

Well, I'm not really in a position to coordinate a team of developers at the moment. I have a newborn daughter and a full time job, so to avoid the frustration of not hearing from me for a few weeks at a time, which totally happens, send me small patches, <500 lines, and I'll review them. Since I doubt this will be much of an issue either way, I'll leave it at that for now.

Jesus, what isn't in a state of almost complete! Documentation, Bugs, Dom.js, Cross Platform Compatibility, JSDom, Unit Tests, etc. You know, all the stuff you'd expect from a professional open source project. The same stuff you'd expect to be missing from a project named VerbotenJS.

Documentation

Node Framework: http://nodejs.org/docs/latest/api/index.html

Fonts used for prototype headers

//http://www.network-science.de/ascii/

  • Using "Slant" font

Things to do for VerbotenJS

Array.prototype.thread = function(f, status_handler, params, callback) {
    //I've had this idea for a while now, since web workers now exist this is probably a less than ideal solution, but it could
    //serve as a worker shim maybe?

    //This is some pseudo code for implementing threaded processing in a browser
    //A suitable counterpart to threaded server side operations in node,
    //when the client needs to process millions of records quickly
    //essentially map reduce via popups
    //not all supporting functions are built or built to spec for this to work

    var x = this.inGroupsOf(this.size().squared().limit(200)); //100,000 == 36, 100,000,000 == 81 up to 200 browsers

    var threads = [];
    x.ea(function(g, i) {
        //spawn each browser with a bootstrap of the q framework
        threads.push(window.open('thread' + i, 'qbootstrap.html'));
        //create special eval async function that passes a stringified version of the function to run
        //and the group of data to run it on to the child browser window for processing
        threads.last().evalAsync(f.toString(), g.toString()); //async eval occurs in child window
    });

    var res = [];
    var remaining = threads.length;

    var id = setInterval(function() {
        //check on results ever second or so
        threads.ea(function(t,i) {
            if (t.finished) { //evalAsync function should set global finished & result variables
                res[i] = t.result; //get result from child processing and insert into original index
                t.close(); //close browser
                remaining--;
            }
        });

        if (remaining <= 0) {
            //don't need to check results anymore, all browsers are closed
            clearInterval(id);
            //now reduce the results into a flat array
            callback(res.flatten(1)); //but only flatten the first layer
        }
    }, 1000);
};

Coding Standards:

Good: if (test == 1) { return msg; } Bad: if (test == 1) return msg; Reason: Setting a break point to inspect msg when test == 1 is difficult when they are on one line. Having curly brackets ensures future code modifications are understood and doesn't cost much.

Good: (function() { //self executing function var x = 1; })(); Bad: var x = 1; Reason: 'x' is public meaning others can modify it or you could be modifying someone else's x if they used it already. Global namespace collisions can be reduced by wrapping a self executing function

Good: var x = function() { }; Bad: var x = function() { } Reason: Missing semicolon on function assignment can cause code to fail in ways you probably wouldn't expect. This will throw an exception: var literal = { say: function(msg) { alert(msg); } }

    (function() {
    })();
Unless you add a semicolon to the literal object definition.
And the error you receive is "undefined is not a function" on the last line at the start of the open paren.

For a great explanation of semicolon insertion rules see this article:
http://mattbriggs.net/blog/2012/04/16/why-i-dont-use-semicolons/

Good: for (var x in obj) { if (obj.hasOwnProperty(x)) { //use obj.x } } Bad: for (var x in obj) { //use obj.x } Reason: Prototype extensions will be included in the second for loop. You most likely did not want those references if you are just trying to access the properties associated with just the object. Ideally you would not iterate over objects to begin with, but alternatively you should just use the .ea iterator method which handles this for you. In the event that you do want the prototype introspection, then by all means continue to use it.


Alternative: Object.prototype.hasOwnProperty.apply(obj, key);

Reason: Although many will tell you to do this to avoid problems with a person overwrites hasOwnProperty on a local object, that's a bad reason. If anyone were to override the hasOwnProperty() method on an object it's probably for a damn good reason. Bypassing this option would be like preventing a function from being overriden in a subclass. And while strict proponents think this is ideal, the entire premise of dynamic overrides is that you can temporally adjust what is considered a local property of an object simply by overriding the hasOwnProperty method and selectively including or excluding specific keys.

The real reason to do this is because not all objects inherit from the Object.prototype chain in JS.  IE is notorious for this
design when it comes to DOM and COM objects.  But applying the Object.prototype method to the host object will still work as
expected.  

Just Bad:

(function(undefined) {
    //locally overwriting undefined to be undefined.
})();

Reason: In the event that a programmer wants to temporally alter the value of undefined, they can do so because JavaScript is dynamic. Precluding this option makes your code less dynamic. You should probably be using another language if that's what you want. In the event that someone alters the value of undefined and your code breaks, this is obviously the fault of the coder, not you. So don't limit the coding options for those who would use the dynamic nature of the language for good use because someone might hang themselves.

Good: var i = parseInt("08", 10); //let's assume 08 was part of a date string //i == 8 Bad: var i = parseInt("08"); //i == 0 Reason: parseInt does not always assume a base 10 radix. In this example it will assume 08 is octal (base 8) which doesn't have an 8, so it results in 0;

Good: function() { var myVar = 'local'; } Bad: function() { myVar = 'local'; } Reason: Always use var to define a variable, in the second case a global myVar will be created instead of a local myVar.

Good: var x; if (FB && FB.api && FB.api.getCurrentUser) { x = FB.api.getCurrentUser(); } ----------or------------ try { var x = FB.api.getCurrentUser(); } catch(e) { } Bad: var x = FB.api.getCurrentUser(); Reason: Always check an object chain before accessing its children or calling a function. If FB isn't defined, accessing FB.api will throw an exception. And if FB.api is defined, but getCurrentUser is not, calling it as a function will also throw an exception. Alternatively you can, and in the case of Facebook probably should, EAFP (Easier to Ask Forgiveness than Permission) or just try catch the exception.

Great References:

http://kangax.github.com/nfe/
http://perfectionkills.com/
http://bonsaiden.github.com/JavaScript-Garden/

Interesting Reads:

https://github.com/BonsaiDen/JavaScript-Garden/issues/84
http://perfectionkills.com/category/ecma-262/

Troubleshooting:

Object Prototypes you should avoid overriding
    Object.prototype.sort = function() { }; //messes with mongodb in node.js
    Object.prototype.toString = function() { }; //messes with jQuery & other internals
    Object.prototype.set = function() { }; //messes with require('child_process');
    Object.prototype.size = function() { }; //breaks node.js url.parse method ??? weird.
    Array.prototype.reduce = function() { }; //map reduce much?
    Array.prototype.map = function(f) { };