Permalink
Browse files

Initial API

  • Loading branch information...
1 parent c98ad83 commit 97090582293a743e9205a1ddf92010a0d6d35360 @dallonf committed May 24, 2012
Showing with 139 additions and 0 deletions.
  1. +1 −0 index.js
  2. +73 −0 lib/async-eval.js
  3. +21 −0 package.json
  4. +44 −0 test/api.test.js
View
@@ -0,0 +1 @@
+module.exports = require('./lib/async-eval.js');
View
@@ -0,0 +1,73 @@
+var vm = require('vm');
+var _ = require('underscore')._;
+var EventEmitter = require('events').EventEmitter;
+
+module.exports = function asyncEval(code, options, callback) {
+ if (!callback) {
+ callback = arguments[1];
+ options = undefined;
+ }
+
+ options = _.defaults(options || {}, {
+ this: {}
+ , asyncFunctions: {}
+ , sandbox: {}
+ });
+
+ var sandbox = _.extend(options.sandbox, {
+ _this: options.this
+ , done: function() {
+ if (callback) callback();
+ }
+ });
+
+ var done = sandbox.done;
+
+ var callbackCount = 0;
+
+ var events = new EventEmitter();
+ wrapAsyncFunctions(options.asyncFunctions, sandbox, events);
+
+ events.on('addCallback', function() {
+ callbackCount++;
+ });
+
+ events.on('finishCallback', function() {
+ callbackCount--;
+ if (callbackCount <= 0) {
+ done();
+ }
+ });
+
+ vm.runInNewContext("(function() {" + code + "\n}).call(_this)", sandbox);
+
+ if (callbackCount <= 0) {
+ done();
+ }
+};
+
+function wrapAsyncFunctions(asyncFunctions, sandbox, events) {
+ Object.keys(asyncFunctions).forEach(function(k) {
+ if (typeof asyncFunctions[k] === 'function') {
+ sandbox[k] = function() {
+ events.emit('addCallback');
+
+ var args = _.toArray(arguments);
+ var callback = _.last(args);
+ if (typeof callback === 'function') {
+ args[args.length - 1] = function() {
+ callback.apply(sandbox._this, arguments);
+ events.emit('finishCallback');
+ };
+ } else {
+ args.push(function() {
+ events.emit('finishCallback');
+ });
+ }
+ asyncFunctions[k].apply(sandbox._this, args);
+ };
+ }
+
+ //TODO: Support nested async functions
+ });
+}
View
@@ -0,0 +1,21 @@
+{
+ "author": "Dallon Feldner",
+ "name": "async-eval",
+ "description": "Execute arbitrary JS with callbacks",
+ "version": "0.1.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/dallonf/async-eval"
+ },
+ "main": "index.js",
+ "engines": {
+ "node": ">= 0.7.0"
+ },
+ "dependencies": {
+ "underscore": ">= 1.3.0"
+ },
+ "devDependencies": {
+ "chai": "*",
+ "mocha": "*"
+ }
+}
View
@@ -0,0 +1,44 @@
+var asyncEval = require('../');
+var expect = require('chai').expect;
+
+var wait = function(callback) {
+ process.nextTick(callback);
+};
+
+describe('asyncEval', function() {
+ it('should run the callback', function(done) {
+ asyncEval('var x = 5;', function() {
+ done();
+ });
+ });
+
+ it('should have access to this', function(done) {
+ var self = {};
+ asyncEval('this.x = 5;', { this: self }, function() {
+ expect(self.x).to.equal(5);
+ done();
+ });
+ });
+
+ it('should have access to sandbox objects', function(done) {
+ var me = {foo: 'bar'};
+ asyncEval('me.baz = me.foo;', { sandbox: {me: me} }, function() {
+ expect(me.baz).to.equal('bar');
+ done();
+ });
+ });
+
+ it('should wait for an async function', function(done) {
+ function test() {
+
+ }
+
+ var self = {};
+
+ asyncEval("var self = this; wait( function() { this.x = 5; } );"
+ , {this: self, asyncFunctions: {wait: wait}}, function() {
+ expect(self.x).to.equal(5);
+ done();
+ })
+ });
+});

0 comments on commit 9709058

Please sign in to comment.