From 137016ab9487840292de1c509dc66d3a6d9a0804 Mon Sep 17 00:00:00 2001 From: kirrg001 Date: Wed, 13 Dec 2017 22:20:02 +0100 Subject: [PATCH 1/4] Moved pipeline/sequence to lib/promise refs #9178 - continue with killing our global utils folder - i haven't found any better naming for lib/promise - so, require single files for now - instead of doing `promiseLib = require('../lib/promise')` - we can optimise the requires later --- ghost/promise/lib/pipeline.js | 31 +++++++++++++++++++++++++++++++ ghost/promise/lib/sequence.js | 17 +++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 ghost/promise/lib/pipeline.js create mode 100644 ghost/promise/lib/sequence.js diff --git a/ghost/promise/lib/pipeline.js b/ghost/promise/lib/pipeline.js new file mode 100644 index 000000000000..18d99b5dc304 --- /dev/null +++ b/ghost/promise/lib/pipeline.js @@ -0,0 +1,31 @@ +/** + * # Pipeline Utility + * + * Based on pipeline.js from when.js: + * https://github.com/cujojs/when/blob/3.7.4/pipeline.js + */ +var Promise = require('bluebird'); + +function pipeline(tasks /* initial arguments */) { + var args = Array.prototype.slice.call(arguments, 1), + + runTask = function (task, args) { + // Self-optimizing function to run first task with multiple + // args using apply, but subsequent tasks via direct invocation + runTask = function (task, arg) { + return task(arg); + }; + + return task.apply(null, args); + }; + + // Resolve any promises for the arguments passed in first + return Promise.all(args).then(function (args) { + // Iterate through the tasks passing args from one into the next + return Promise.reduce(tasks, function (arg, task) { + return runTask(task, arg); + }, args); + }); +} + +module.exports = pipeline; diff --git a/ghost/promise/lib/sequence.js b/ghost/promise/lib/sequence.js new file mode 100644 index 000000000000..34d6d4245667 --- /dev/null +++ b/ghost/promise/lib/sequence.js @@ -0,0 +1,17 @@ +var Promise = require('bluebird'); + +/** + * expects an array of functions returning a promise + */ +function sequence(tasks /* Any Arguments */) { + var args = Array.prototype.slice.call(arguments, 1); + + return Promise.reduce(tasks, function (results, task) { + return task.apply(this, args).then(function (result) { + results.push(result); + return results; + }); + }, []); +} + +module.exports = sequence; From b7b4026fd5b1de0341d686a85f84f5e4d1dba9a7 Mon Sep 17 00:00:00 2001 From: kirrg001 Date: Wed, 3 Oct 2018 00:06:16 +0200 Subject: [PATCH 2/4] Extended sequence utility no issue - support promise and none promise tasks - helpful if you create an array of operations and not all of the operations/tasks are async - `response instanceof Promise` does not work for all cases e.g. some usages return a transaction/bookshelf chain --- ghost/promise/lib/sequence.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/ghost/promise/lib/sequence.js b/ghost/promise/lib/sequence.js index 34d6d4245667..c00461200809 100644 --- a/ghost/promise/lib/sequence.js +++ b/ghost/promise/lib/sequence.js @@ -1,16 +1,25 @@ -var Promise = require('bluebird'); +const Promise = require('bluebird'); /** * expects an array of functions returning a promise */ function sequence(tasks /* Any Arguments */) { - var args = Array.prototype.slice.call(arguments, 1); + const args = Array.prototype.slice.call(arguments, 1); return Promise.reduce(tasks, function (results, task) { - return task.apply(this, args).then(function (result) { - results.push(result); - return results; - }); + const response = task.apply(this, args); + + if (response && response.then) { + return response.then(function (result) { + results.push(result); + return results; + }); + } else { + return Promise.resolve().then(() => { + results.push(response); + return results; + }); + } }, []); } From 7e20949956c1b2704775f88d263ea52ab2f5f48f Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Mon, 30 Mar 2020 16:26:47 +0100 Subject: [PATCH 3/4] Move tests from core to root (#11700) - move all test files from core/test to test/ - updated all imports and other references - all code inside of core/ is then application code - tests are correctly at the root level - consistent with other repos/projects Co-authored-by: Kevin Ansfield --- ghost/promise/test/pipeline_spec.js | 97 +++++++++++++++++++++++++++++ ghost/promise/test/sequence_spec.js | 28 +++++++++ 2 files changed, 125 insertions(+) create mode 100644 ghost/promise/test/pipeline_spec.js create mode 100644 ghost/promise/test/sequence_spec.js diff --git a/ghost/promise/test/pipeline_spec.js b/ghost/promise/test/pipeline_spec.js new file mode 100644 index 000000000000..ada7531fea46 --- /dev/null +++ b/ghost/promise/test/pipeline_spec.js @@ -0,0 +1,97 @@ +var should = require('should'), + sinon = require('sinon'), + Promise = require('bluebird'), + + // Stuff we are testing + pipeline = require('../../../../core/server/lib/promise/pipeline'); + +// These tests are based on the tests in https://github.com/cujojs/when/blob/3.7.4/test/pipeline-test.js +function createTask(y) { + return function (x) { + return x + y; + }; +} + +describe('Pipeline', function () { + afterEach(function () { + sinon.restore(); + }); + + it('should execute tasks in order', function () { + return pipeline([createTask('b'), createTask('c'), createTask('d')], 'a').then(function (result) { + result.should.eql('abcd'); + }); + }); + + it('should resolve to initial args when no tasks supplied', function () { + return pipeline([], 'a', 'b').then(function (result) { + result.should.eql(['a', 'b']); + }); + }); + + it('should resolve to empty array when no tasks and no args supplied', function () { + return pipeline([]).then(function (result) { + result.should.eql([]); + }); + }); + + it('should pass args to initial task', function () { + var expected = [1, 2, 3], + tasks = [sinon.spy()]; + + return pipeline(tasks, 1, 2, 3).then(function () { + tasks[0].calledOnce.should.be.true(); + tasks[0].firstCall.args.should.eql(expected); + }); + }); + + it('should allow initial args to be promises', function () { + var expected = [1, 2, 3], + tasks = [sinon.spy()], + Resolver = Promise.resolve; + + return pipeline(tasks, new Resolver(1), new Resolver(2), new Resolver(3)).then(function () { + tasks[0].calledOnce.should.be.true(); + tasks[0].firstCall.args.should.eql(expected); + }); + }); + + it('should allow tasks to be promises', function () { + var expected = [1, 2, 3], + tasks = [ + sinon.stub().returns(new Promise.resolve(4)), + sinon.stub().returns(new Promise.resolve(5)), + sinon.stub().returns(new Promise.resolve(6)) + ]; + + return pipeline(tasks, 1, 2, 3).then(function (result) { + result.should.eql(6); + tasks[0].calledOnce.should.be.true(); + tasks[0].firstCall.args.should.eql(expected); + tasks[1].calledOnce.should.be.true(); + tasks[1].firstCall.calledWith(4).should.be.true(); + tasks[2].calledOnce.should.be.true(); + tasks[2].firstCall.calledWith(5).should.be.true(); + }); + }); + + it('should allow tasks and args to be promises', function () { + var expected = [1, 2, 3], + tasks = [ + sinon.stub().returns(new Promise.resolve(4)), + sinon.stub().returns(new Promise.resolve(5)), + sinon.stub().returns(new Promise.resolve(6)) + ], + Resolver = Promise.resolve; + + return pipeline(tasks, new Resolver(1), new Resolver(2), new Resolver(3)).then(function (result) { + result.should.eql(6); + tasks[0].calledOnce.should.be.true(); + tasks[0].firstCall.args.should.eql(expected); + tasks[1].calledOnce.should.be.true(); + tasks[1].firstCall.calledWith(4).should.be.true(); + tasks[2].calledOnce.should.be.true(); + tasks[2].firstCall.calledWith(5).should.be.true(); + }); + }); +}); diff --git a/ghost/promise/test/sequence_spec.js b/ghost/promise/test/sequence_spec.js new file mode 100644 index 000000000000..30d8b5bf63c8 --- /dev/null +++ b/ghost/promise/test/sequence_spec.js @@ -0,0 +1,28 @@ +const should = require('should'); +const sinon = require('sinon'); +const Promise = require('bluebird'); +const sequence = require('../../../../core/server/lib/promise/sequence'); + +describe('Unit: lib/promise/sequence', function () { + afterEach(function () { + sinon.restore(); + }); + + it('mixed tasks: promise and none promise', function () { + const tasks = [ + function a() { + return Promise.resolve('hello'); + }, + function b() { + return 'from'; + }, + function c() { + return Promise.resolve('chio'); + } + ]; + return sequence(tasks) + .then(function (result) { + result.should.eql(['hello','from', 'chio']); + }); + }); +}); From 119985957116b2d7503cfb200ada221b6b34ab23 Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Wed, 29 Apr 2020 16:44:27 +0100 Subject: [PATCH 4/4] Updated var declarations to const/let and no lists - All var declarations are now const or let as per ES6 - All comma-separated lists / chained declarations are now one declaration per line - This is for clarity/readability but also made running the var-to-const/let switch smoother - ESLint rules updated to match How this was done: - npm install -g jscodeshift - git clone https://github.com/cpojer/js-codemod.git - git clone git@github.com:TryGhost/Ghost.git shallow-ghost - cd shallow-ghost - jscodeshift -t ../js-codemod/transforms/unchain-variables.js . -v=2 - jscodeshift -t ../js-codemod/transforms/no-vars.js . -v=2 - yarn - yarn test - yarn lint / fix various lint errors (almost all indent) by opening files and saving in vscode - grunt test-regression - sorted! --- ghost/promise/lib/pipeline.js | 20 ++++++------ ghost/promise/test/pipeline_spec.js | 49 +++++++++++++++-------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/ghost/promise/lib/pipeline.js b/ghost/promise/lib/pipeline.js index 18d99b5dc304..98be6651cba6 100644 --- a/ghost/promise/lib/pipeline.js +++ b/ghost/promise/lib/pipeline.js @@ -4,21 +4,21 @@ * Based on pipeline.js from when.js: * https://github.com/cujojs/when/blob/3.7.4/pipeline.js */ -var Promise = require('bluebird'); +const Promise = require('bluebird'); function pipeline(tasks /* initial arguments */) { - var args = Array.prototype.slice.call(arguments, 1), + const args = Array.prototype.slice.call(arguments, 1); - runTask = function (task, args) { - // Self-optimizing function to run first task with multiple - // args using apply, but subsequent tasks via direct invocation - runTask = function (task, arg) { - return task(arg); - }; - - return task.apply(null, args); + let runTask = function (task, args) { + // Self-optimizing function to run first task with multiple + // args using apply, but subsequent tasks via direct invocation + runTask = function (task, arg) { + return task(arg); }; + return task.apply(null, args); + }; + // Resolve any promises for the arguments passed in first return Promise.all(args).then(function (args) { // Iterate through the tasks passing args from one into the next diff --git a/ghost/promise/test/pipeline_spec.js b/ghost/promise/test/pipeline_spec.js index ada7531fea46..11a72456e16e 100644 --- a/ghost/promise/test/pipeline_spec.js +++ b/ghost/promise/test/pipeline_spec.js @@ -1,9 +1,9 @@ -var should = require('should'), - sinon = require('sinon'), - Promise = require('bluebird'), +const should = require('should'); +const sinon = require('sinon'); +const Promise = require('bluebird'); - // Stuff we are testing - pipeline = require('../../../../core/server/lib/promise/pipeline'); +// Stuff we are testing +const pipeline = require('../../../../core/server/lib/promise/pipeline'); // These tests are based on the tests in https://github.com/cujojs/when/blob/3.7.4/test/pipeline-test.js function createTask(y) { @@ -36,8 +36,8 @@ describe('Pipeline', function () { }); it('should pass args to initial task', function () { - var expected = [1, 2, 3], - tasks = [sinon.spy()]; + const expected = [1, 2, 3]; + const tasks = [sinon.spy()]; return pipeline(tasks, 1, 2, 3).then(function () { tasks[0].calledOnce.should.be.true(); @@ -46,9 +46,9 @@ describe('Pipeline', function () { }); it('should allow initial args to be promises', function () { - var expected = [1, 2, 3], - tasks = [sinon.spy()], - Resolver = Promise.resolve; + const expected = [1, 2, 3]; + const tasks = [sinon.spy()]; + const Resolver = Promise.resolve; return pipeline(tasks, new Resolver(1), new Resolver(2), new Resolver(3)).then(function () { tasks[0].calledOnce.should.be.true(); @@ -57,12 +57,13 @@ describe('Pipeline', function () { }); it('should allow tasks to be promises', function () { - var expected = [1, 2, 3], - tasks = [ - sinon.stub().returns(new Promise.resolve(4)), - sinon.stub().returns(new Promise.resolve(5)), - sinon.stub().returns(new Promise.resolve(6)) - ]; + const expected = [1, 2, 3]; + + const tasks = [ + sinon.stub().returns(new Promise.resolve(4)), + sinon.stub().returns(new Promise.resolve(5)), + sinon.stub().returns(new Promise.resolve(6)) + ]; return pipeline(tasks, 1, 2, 3).then(function (result) { result.should.eql(6); @@ -76,13 +77,15 @@ describe('Pipeline', function () { }); it('should allow tasks and args to be promises', function () { - var expected = [1, 2, 3], - tasks = [ - sinon.stub().returns(new Promise.resolve(4)), - sinon.stub().returns(new Promise.resolve(5)), - sinon.stub().returns(new Promise.resolve(6)) - ], - Resolver = Promise.resolve; + const expected = [1, 2, 3]; + + const tasks = [ + sinon.stub().returns(new Promise.resolve(4)), + sinon.stub().returns(new Promise.resolve(5)), + sinon.stub().returns(new Promise.resolve(6)) + ]; + + const Resolver = Promise.resolve; return pipeline(tasks, new Resolver(1), new Resolver(2), new Resolver(3)).then(function (result) { result.should.eql(6);