Skip to content

Instantly share code, notes, and snippets.

@pwmckenna
Created October 1, 2012 07:38
Show Gist options
  • Save pwmckenna/3810124 to your computer and use it in GitHub Desktop.
Save pwmckenna/3810124 to your computer and use it in GitHub Desktop.
Wrap a potentially unavailable Btapp function to make it immediately callable
/**
Usage:
var add = btapp.func('add', 'torrent');
...or to provide a callback for dht activity, you might try the following:
var get_any_hash = btapp.func('dht', 'get_any_hash');
// While the function won't be available immediately, it is abstracted away,
// allowing you to treat the future function as immedidately callable
get_any_hash(function(hash) {
//Just saw a hash in the dht!
});
**/
(function() {
function assert(condition, msg) { if(!condition) throw msg; }
assert(_, 'Requires underscore/lodash');
assert(jQuery, 'Requires jQuery');
assert(jQuery.Deferred, 'Requires jQuery Deferred objects. Make sure you are using jQuery version > 1.5');
assert(Btapp, 'Requires Btapp.js. Visit http://btappjs.com for help');
assert('live' in Backbone.Model.prototype && 'live' in Backbone.Collection.prototype, 'Requires Backbrace.js.');
var onObjectFunctionAvailable = function(obj, funcName, trigger) {
assert(funcName in obj.bt, 'expected ' + funcName + ' to be a function');
trigger.resolve(obj.bt[funcName]);
}
var onObjectAvailable = function(obj, funcName, trigger) {
var cleanup, cb;
if(funcName in obj.bt) {
onObjectFunctionAvailable(obj, funcName, trigger);
} else {
cleanup = function() {
obj.off('add:bt:' + funcName, cb);
}
cb = function() {
onObjectFunctionAvailable(obj, funcName, trigger);
};
obj.on('add:bt:' + funcName, cb);
}
};
var waitForObjectAvailability = function(btapp, path, trigger) {
var selector, cleanup, cb;
// Generate the live selector for the parent object that will
// contain the the desired function
selector = _.chain(path).initial().reduce(function(memo, elem) {
var next = memo + (memo.length > 0 ? ' ' : '') + elem;
return next;
}, '').value();
cleanup = function() {
btapp.die(selector, cb);
};
cb = function(obj) {
onObjectAvailable(obj, _.last(path), trigger);
};
// Now we need to wait and listen for the parent object
btapp.live(selector, cb);
};
var generateTriggeredFunction = function(trigger) {
return function() {
// Create a wrapper deferred object that we'll return immediately
// once we actually get our hands on the function we'll call it
// and pipe the deferred object from that call into this one
var ret = new jQuery.Deferred;
// Hold on to the args until the function is available
var args = _.toArray(arguments);
trigger.done(function(func) {
// All btapp functions return a deferred object,
// so we just need to make sure we pipe that object
// into the deferred object that we returned immediately
func.apply(this, args).then(
function() {
ret.resolve.apply(this, arguments);
}, function() {
ret.reject.apply(this, arguments);
}
);
});
// By dangling done/fail callbacks onto this return value,
// the caller's code will be executed when the actual function's
// return Deferred object is resolved.
return ret;
};
};
Btapp.prototype.func = function(path) {
var trigger, btapp;
// Support both of the following argument styles
// .call('btapp', 'add', 'torrent')
// .call(['btapp', 'add', 'torrent'])
if(typeof path === 'string') {
path = _.toArray(arguments);
}
_(path).each(function(segment) {
assert(segment !== '*', 'unlike Backbrace, func only wraps a single function, so wildcards are not supported');
});
// We're going to use the trigger deferred object to hang all
// calls to our function on...that way we can just resolve it
// when the desired function is available
trigger = new jQuery.Deferred;
// We should verify that the btapp we've been dangled off of has the correct paths
// Check if we're looking for a function on the base object
if(path.length === 1) {
onObjectAvailable(this, _.last(path), trigger);
} else {
waitForObjectAvailability(this, path, trigger);
}
return generateTriggeredFunction(trigger);
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment