Created
October 1, 2012 07:38
-
-
Save pwmckenna/3810124 to your computer and use it in GitHub Desktop.
Wrap a potentially unavailable Btapp function to make it immediately callable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
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