isntitvacant
Joined 16 years ago
Joined 16 years ago
That said, it's hard to program to a spec that does not (officially) exist yet, and doubly so if there's not clear "opt-in/out" path for future versions. Since, as a developer, I can't control what the committee adds or removes from the spec, or what they might break, I can only do the best I can with the language as it is -- not breaking existing code in future versions of the language is the committee's job.
function compile(filename, ready) {
return fs.readFile(filename, make_fn)
function make_fn(err, data) {
if(err) return ready(err)
ready(null, new Function(data))
}
}
Which neatly addresses the desire to retain closures, while avoiding unnecessary nesting.Also, whenever possible, I like to nix callbacks by using Function#bind:
res.on('data', accum.push.bind(accum))
// vs:
res.on('data', function(data) { accum.push(data) })Has that been resolved? I'm kind of nervous about relying on unlicensed javascript.
[0]: https://github.com/beatgammit/deflate-js/blob/master/lib/raw...
In practice, this problem is why you see `Array.isArray`, `jQuery.isArray`, `_.isArray` and friends so often -- they exist to give a canonical, non-"check if any member of X's inheritance chain matches Y.prototype" way to determine if a given instance is actually an array.
So, in summation: yes, it's weird that Node has this, but ultimately it's a lesser-vilified problem of JavaScript's.
[0]: https://github.com/joyent/node/blob/master/lib/module.js#L51 -- `NODE_MODULE_CONTEXTS=1 node <file.js>` will run separate modules in separate global contexts.
I was particularly interested in different storage methods for git repositories in-browser -- like localStorage, or (for fun) writing the git data to a canvas, extracting it as a PNG, and uploading it to a static file server which would then send it back as a "cache forever" image; you could then extract all the data back out of the PNG by writing it to a canvas and running through the image data. Also fun: writing a backend that relies on Github's raw object API [1] Or, on the server, running a SQLite backend for repo data. The possibilities are endless :)
This seems like a tiny gain in readability, but it means that I don't have to make a decision between putting the error/success logic of `fs.readFile` in my method (where it does not go) and my calling function (where it probably does not go, and where it would incur unnecessary nesting -- and the finagling of the `this` variable that that incurs).
This may just be my taste -- but I find designing around (and reasoning about) EventEmitter-type API's much easier.
It can be hard to avoid; and having the community put together a PEP-8 style "best practices" to avoid creating nested code is one of my fondest dreams.
That said, I've written my fair share of [spaghetti code](https://github.com/chrisdickinson/tempisfugit/blob/master/li...) in Node, mostly because JavaScript seems to lend itself to slapping together nested callbacks without slapping you on the wrist for writing unreadable code.
In my experience, the best way to cut down on spaghetti code is to design your API around the [EventEmitter](http://nodejs.org/api/events.html#events.EventEmitter) object. Consider this adaptation of the linked spaghetti code: https://gist.github.com/f8533ff57844b1e54558
Note that in the application code, there are no nested callbacks -- they're merely concerned with validating the incoming data and passing the baton on to the next responder. Bonus points: the methods are in the order of their ideal execution. Clients using the code can just do the following:
var validator = new RepoPathValidator(path);
validator.on('data', function(repoData) {
// do whatever needs to be done with the repo.
});
// redirect all errors to console.error
validator.on('error', console.error.bind(console));
I'm very fond of using this method to destroy nested code -- I'm not sure how it's thought of in the community
at large, though (part of the reason I'm so eager for the community to put together the aforementioned PEP-8
style "best practices" guide!) var session = require('session'),
models = require('models'),
template = require('template');
function main(request,response){
session.start(
models.getEntries.bind(models,
template.render.bind(template, 'blog',
response.write.bind(response))));
}
would be easy to put together with node.js; it's functionally equivalent to the above (though it assumes that data comes into the template.render call as the last argument(s)), and neatly avoids too much nesting.One of Wilson's goals is to be comfortable for Djangonauts who, for whatever reason, need to write Node.js code. To that end, the ORM, view layer, and template system are similar to Django to ease use in that particular case.
The other boon to having a largely cross-compatible template language is that it allows a bit of bootstrapping to occur for Wilson projects -- Wilson could conceivably make use of Django's admin templates at some point, if that's the direction it's going to head.
However, Wilson does diverge pretty rapidly from Django in other areas -- it borrows Django's concept of apps, but in Wilson apps are designed to be pluggable; you can install a single app multiple times under multiple aliases, reopen app instances from the project level and change their settings and models before they're finalized, etc. Within the context of an app, you interact with other apps through "externals" that are resolved at project-level.
The other major difference is that wherever possible, I'm hoping to keep the libraries that constitute Wilson separate -- and wherever it makes sense, to make them also operable in-browser. To that end, Platoon (the testing framework), Plate (the template language), Escaperoute (the router), Pieshop (the ORM), and postpie (the ORM adapter to Postgres) are all kept as separate libraries; Pieshop and Plate are actually designed to be run in-browser as well, in preparation for the eventuality of needing to interface nicely with a Wilson app from the client-side.
So in summary: Wilson isn't a one-to-one port of Django -- it's heavily Django-inspired -- but aims to diverge wherever it is sensible to do so.
I'll push up some updates to the example tonight, and I plan on updating the docs within the week.
EDIT: The docs are updated and the example is too. Whew!
Where you have to be careful is if you have a function that's being called repeatedly and it contains a function instantiation -- that function will be reallocated each time.
Will repeatedly allocate that inner multiplication function, vs: Will only allocate that multiplication function once.