diff --git a/lib/index.js b/lib/index.js index b3cc6709..105bad6b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -674,6 +674,56 @@ class Generator extends EventEmitter { ); } + queueComposedMethod(name, options) { + const property = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), name); + if (!property) { + throw new Error(`Property ${name} not found at ${this.options.namespace}`); + } + + const method = property.value; + if (typeof method !== 'function') { + throw new Error(`Property ${name} is not a function at ${this.options.namespace}`); + } + + const tasks = method.call(this, options); + if (typeof tasks !== 'object') { + throw new Error( + `Function ${name} is not a composed method at ${this.options.namespace}` + ); + } + + const self = this; + const queueTask = function(task) { + if (!task.method) { + throw new Error( + `Invalid task at composed method ${name} at ${self.options.namespace}` + ); + } + + if (task.priorityName) { + const priority = self._queues[task.priorityName]; + if (!priority) { + throw new Error(`Priority ${task.priorityName} is not defined`); + } + + // If priority is defined, use it configs as defaults; + task = { ...priority, ...task }; + } + + // If run in not defined, set to false to don't start the loop if not running yet + task.run = task.run === undefined ? false : task.run; + self.queueTask(task); + }; + + if (Array.isArray(tasks)) { + tasks.filter(task => task.method && task.taskName).forEach(queueTask); + return; + } + + tasks.taskName = tasks.taskName || name; + queueTask(tasks); + } + /** * Runs the generator, scheduling prototype methods on a run queue. Method names * will determine the order each method is run. Methods without special names diff --git a/test/base.js b/test/base.js index ec5e20ac..d7800ba3 100644 --- a/test/base.js +++ b/test/base.js @@ -1519,6 +1519,117 @@ describe('Base', () => { }); }); + describe('#queueComposedMethod', () => { + it('correctly run queueComposedMethod', function() { + const commonConfiguring = sinon.spy(); + const configuring = sinon.spy(); + + const TestGenerator = class extends Base { + constructor(args, opts) { + super(args, { + ...opts, + customPriorities: [{ name: 'commonConfiguring', once: true }] + }); + } + }; + _.extend(TestGenerator.prototype, { + commonConfiguring: commonConfiguring, + + _composed(options) { + return [ + { + method: this.commonConfiguring, + taskName: 'commonConfiguring', + priorityName: 'commonConfiguring' + }, + { + method: this.commonConfiguring, + taskName: 'commonConfiguring', + priorityName: 'commonConfiguring' + }, + { + method: function() { + configuring(options.callNumber); + }, + taskName: 'configuring' + } + ]; + } + }); + + const testGen = new TestGenerator([], { + resolved: 'unknown', + namespace: 'dummy', + env: this.env, + 'skip-install': true + }); + + testGen.queueComposedMethod('_composed', { callNumber: 1 }); + testGen.queueComposedMethod('_composed', { callNumber: 2 }); + + return testGen.run().then(() => { + sinon.assert.calledOnce(commonConfiguring); + sinon.assert.calledTwice(configuring); + assert.equal(1, configuring.getCall(0).args[0]); + assert.equal(2, configuring.getCall(1).args[0]); + }); + }); + + it("throws if the method doesn't exists", function() { + assert.throws(() => this.dummy.queueComposedMethod('dontExists'), /not found/); + }); + + describe('throws on errors', function() { + before(function() { + const TestGenerator = class extends Base {}; + TestGenerator.prototype.notAFunction = {}; + TestGenerator.prototype.returnUndefined = () => {}; + TestGenerator.prototype.invalidComposedMethod = () => { + return {}; + }; + + TestGenerator.prototype.invalidPriority = () => { + return { method: () => {}, priorityName: 'dontExists' }; + }; + + this.gen = new TestGenerator([], { + resolved: 'unknown', + namespace: 'dummy', + env: this.env, + 'skip-install': true + }); + }); + + it('throws if not a function', function() { + assert.throws( + () => this.gen.queueComposedMethod('notAFunction'), + /not a function/ + ); + }); + + it('throws if passed an invalid priority', function() { + assert.throws( + () => this.gen.queueComposedMethod('invalidPriority'), + /Priority (.*) is not defined/ + ); + }); + + it('throws if the function returns not an object', function() { + assert.throws( + () => this.gen.queueComposedMethod('returnUndefined'), + /not a composed method/ + ); + }); + + it('throws if the function returns a invalid task', function() { + assert.throws( + () => this.gen.queueComposedMethod('invalidComposedMethod'), + /Invalid task at composed method/ + ); + }); + }); + }); + describe('Custom priorities errors', () => { it('error is thrown with duplicate custom queue', function() { const TestGenerator = class extends Base {