diff --git a/README.md b/README.md index c773f1f..60782d5 100644 --- a/README.md +++ b/README.md @@ -480,6 +480,22 @@ Since there is no `finished_state` in the `fanout_message` spec, the task will b While this example is a little contrived since you could perform the sanitization and fanout in a single task, creating multiple specs for our tasks allows us to do things like add selective retries to certain tasks more likely to fail, put additional workers on more expensive tasks, or add expressive error states. +## Custom references to tasks and specs + +In order to use custom paths to tasks and specs, specify them explicitly. Instead of a single reference to the queue, use an object containing the keys `tasksRef` and `specsRef`. + +```js +var Queue = require('firebase-queue'), + Firebase = require('firebase'); + +var jobsRef = new Firebase('https://.firebaseio.com/jobs'); +var specsRef = new Firebase('https://.firebaseio.com/specs'); + +var queue = new Queue({ tasksRef: jobsRef, specsRef: specsRef }, function(data, progress, resolve, reject) { + // process task +}); +``` + ## Wrap Up As you can see, Firebase Queue is a powerful tool that allows you to securely and robustly perform background work on your Firebase data, from sanitization to data fanout and more. We'd love to hear about how you're using Firebase-Queue in your project! Let us know on [Twitter](https://twitter.com/firebase), [Facebook](https://www.facebook.com/Firebase), or [G+](https://plus.google.com/115330003035930967645). If you have any questions, please direct them to our [Google Group](https://groups.google.com/forum/#!forum/firebase-talk) or [support@firebase.com](mailto:support@firebase.com). diff --git a/src/queue.js b/src/queue.js index 367aa96..fe41f05 100644 --- a/src/queue.js +++ b/src/queue.js @@ -24,7 +24,10 @@ var DEFAULT_NUM_WORKERS = 1, /** * @constructor - * @param {Firebase} ref A Firebase reference to the queue. + * @param {(Firebase | Object)} ref A Firebase reference to the queue + * or object containing both keys: + * - tasksRef: {Firebase} A Firebase reference to tasks. + * - specsRef: {Firebase} A Firebase reference to specs. * @param {Object} options (optional) Object containing possible keys: * - specId: {String} the task specification ID for the workers. * - numWorkers: {Number} The number of workers to create for this task. @@ -67,10 +70,8 @@ function Queue() { logger.debug('Queue(): Error during initialization', error); throw new Error(error); } else if (constructorArguments.length === 2) { - self.ref = constructorArguments[0]; self.processingFunction = constructorArguments[1]; } else if (constructorArguments.length === 3) { - self.ref = constructorArguments[0]; var options = constructorArguments[1]; if (!_.isPlainObject(options)) { error = 'Options parameter must be a plain object.'; @@ -122,12 +123,25 @@ function Queue() { logger.debug('Queue(): Error during initialization', error); throw new Error(error); } + + if (_.has(constructorArguments[0], 'tasksRef') && _.has(constructorArguments[0], 'specsRef')) { + self.tasksRef = constructorArguments[0].tasksRef; + self.specsRef = constructorArguments[0].specsRef; + } else if (_.isPlainObject(constructorArguments[0])) { + error = 'When ref is an object it must contain both keys ' + + '\'tasksRef\' and \'specsRef\''; + logger.debug('Queue(): Error during initialization', error); + throw new Error(error); + } else { + self.tasksRef = constructorArguments[0].child('tasks'); + self.specsRef = constructorArguments[0].child('specs'); + } self.workers = []; for (var i = 0; i < self.numWorkers; i++) { var processId = (self.specId ? self.specId + ':' : '') + i; self.workers.push(new QueueWorker( - self.ref.child('tasks'), + self.tasksRef, processId, self.sanitize, self.suppressStack, @@ -141,7 +155,7 @@ function Queue() { } self.initialized = true; } else { - self.specChangeListener = self.ref.child('specs').child(self.specId).on( + self.specChangeListener = self.specsRef.child(self.specId).on( 'value', function(taskSpecSnap) { var taskSpec = { @@ -177,7 +191,7 @@ Queue.prototype.shutdown = function() { logger.debug('Queue: Shutting down'); if (!_.isNull(self.specChangeListener)) { - self.ref.child('specs').child(self.specId).off('value', + self.specsRef.child(self.specId).off('value', self.specChangeListener); self.specChangeListener = null; } diff --git a/test/queue.spec.js b/test/queue.spec.js index 98ed222..498c274 100644 --- a/test/queue.spec.js +++ b/test/queue.spec.js @@ -27,6 +27,14 @@ describe('Queue', function() { }).to.throw; }); }); + + _.forEach([{}, { foo: 'bar' }, { tasksRef: th.testRef}, { specsRef: th.testRef }], function(invalidRefConfigurationObject) { + it('should not create a Queue with ref configuration object that contains keys: {' + _.keys(invalidRefConfigurationObject).join(", ") + '}.', function() { + expect(function() { + new th.Queue(invalidRefConfigurationObject, _.noop); + }).to.throw('When ref is an object it must contain both keys \'tasksRef\' and \'specsRef\''); + }); + }); _.forEach(['', 'foo', NaN, Infinity, true, false, 0, 1, ['foo', 'bar'], { foo: 'bar' }, null, { foo: 'bar' }, { foo: { bar: { baz: true } } }], function(nonFunctionObject) { it('should not create a Queue with a non-function callback: ' + JSON.stringify(nonFunctionObject), function() { @@ -39,6 +47,10 @@ describe('Queue', function() { it('should create a default Queue with just a Firebase reference and a processing callback', function() { new th.Queue(th.testRef, _.noop); }); + + it('should create a default Queue with tasks and specs Firebase references and a processing callback', function() { + new th.Queue({tasksRef: th.testRef, specsRef: th.testRef}, _.noop); + }); _.forEach(['', 'foo', NaN, Infinity, true, false, 0, 1, ['foo', 'bar'], null, _.noop], function(nonPlainObject) { it('should not create a Queue with a Firebase reference, a non-plain object options parameter (' + JSON.stringify(nonPlainObject) + '), and a processingCallback', function() {