From ae5594a853525c06bed5591cf4289d9f49a07d89 Mon Sep 17 00:00:00 2001 From: Joe Fiorini Date: Fri, 15 May 2015 16:46:34 -0400 Subject: [PATCH] Add some promise helpers to lodash --- awx/ui/static/js/shared/lodash-as-promised.js | 115 ++++++++++++++++++ awx/ui/static/js/shared/main.js | 2 + .../unit/shared/lodash-as-promised_test.js | 75 ++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 awx/ui/static/js/shared/lodash-as-promised.js create mode 100644 awx/ui/tests/unit/shared/lodash-as-promised_test.js diff --git a/awx/ui/static/js/shared/lodash-as-promised.js b/awx/ui/static/js/shared/lodash-as-promised.js new file mode 100644 index 0000000000..196248db8d --- /dev/null +++ b/awx/ui/static/js/shared/lodash-as-promised.js @@ -0,0 +1,115 @@ +function lodashAsPromised($q) { + + var lodash = _.runInContext(); + var _aw = {}; + var toExtend = + [ 'map', + 'filter', + 'reduce', + 'pluck', + 'compact', + 'xor', + 'groupBy', + 'thru' + ]; + + function log(label, obj) { + /* jshint ignore:start */ + console.log(label, obj); + /* jshint ignore:end */ + return obj; + } + + function _reject(value, reason) { + return $q.reject(reason); + } + + function _promise(value) { + return $q.when(value); + } + + function _then(promise, fn) { + return promise.then(fn); + } + + function _catch(promise, fn) { + return promise.catch(fn); + } + + function _finally(promise, fn) { + return promise.finally(fn); + } + + function thenAll(arr, then, deep) { + var doAll = function (arr) { + var promise = $q.all(arr).then(function (resolvedArr) { + if (deep) { + return lodash(resolvedArr) + .thenMap(function (elem) { + if (_.isArray(elem)) { + return thenAll(elem, null, true); + } else { + return elem; + } + }) + .thenAll() + .value(); + } else { + return resolvedArr; + } + }); + return then ? promise.then(then) : promise; + }; + if (arr.then) { + return arr.then(doAll); + } else { + return doAll(arr); + } + } + + function thenFlatten(array, isShallow, callback, thisArg) { + return thenAll(array, _.partialRight(_.flatten, isShallow, callback, thisArg), true); + } + + function wrapCallback(method, callback, thisArg, collection) { + return method(collection, function(value, index, collection) { + if (_.isFunction(value.then)) { + return value.then(_.partialRight(callback, index, collection)); + } else { + return callback(value, index, collection); + } + }, thisArg); + } + + function promiseWrapper(method, collection, callback, thisArg) { + if (_.isFunction(collection.then)) { + return collection.then( + _.partial(wrapCallback, method, callback, thisArg)); + } else { + return $q.all(collection) + .then(function(unwrappedResults) { + return method(unwrappedResults, callback, thisArg); + }); + } + } + + toExtend.forEach(function(fnName) { + var wrappedName = 'then' + _.capitalize(fnName); + _aw[wrappedName] = _.partial(promiseWrapper, _[fnName]); + }); + + _aw.promise = _promise; + _aw.reject = _reject; + _aw.then = _then; + _aw.catch = _catch; + _aw.finally = _finally; + _aw.thenAll = thenAll; + _aw.thenFlatten = thenFlatten; + _aw.log = log; + + lodash.mixin(_aw); + + return lodash; +} + +export default ['$q', lodashAsPromised]; diff --git a/awx/ui/static/js/shared/main.js b/awx/ui/static/js/shared/main.js index d49318bc7f..e0bdc363c1 100644 --- a/awx/ui/static/js/shared/main.js +++ b/awx/ui/static/js/shared/main.js @@ -1,6 +1,8 @@ import listGenerator from './list-generator/main'; import title from './title.directive'; +import lodashAsPromised from './lodash-as-promised'; export default angular.module('shared', [listGenerator.name]) + .factory('lodashAsPromised', lodashAsPromised) .directive('title', title); diff --git a/awx/ui/tests/unit/shared/lodash-as-promised_test.js b/awx/ui/tests/unit/shared/lodash-as-promised_test.js new file mode 100644 index 0000000000..d0d86a4f44 --- /dev/null +++ b/awx/ui/tests/unit/shared/lodash-as-promised_test.js @@ -0,0 +1,75 @@ +import 'tower/shared/main'; + +describe('LodashAsPromised', function() { + + var _; + var $q; + + function addOne(num) { + return num + 1; + } + + function isEven(value) { + return value % 2 === 0; + } + + function sum(memo, value) { + return memo + value; + } + + beforeEach(module('shared')); + + beforeEach(inject(['lodashAsPromised', '$q', function(_lodash, _$q) { + _ = _lodash; + $q = _$q; + }])); + + function checkPromiseAndArray(fnName, cb, coll, result) { + context(fnName, function() { + // var itFn = fnName === 'compact' ? it : xit; + var itFn = it; + + itFn('works with a promise', function() { + var values = coll.map($q.when); + var methodName = 'then' + _.capitalize(fnName); + var promise; + // _.log('promises for _', values); + if (fnName === 'reduce') { + promise = _[methodName](values, cb, 0); + } else { + promise = _[methodName](values, cb); + } + + inject(['$rootScope', function($rootScope) { + setTimeout(function() { + $rootScope.$apply(); + }, 1); + }]); + + return expect(promise).to.eventually.deep.equal(result); + }); + + itFn('works with an array', function() { + var value = _[fnName](coll, cb, 0); + expect(value).to.deep.equal(result); + }); + }); + } + + checkPromiseAndArray('map', addOne, [1,2,3,4], [2,3,4,5]); + checkPromiseAndArray('filter', isEven, [1,2,3,4,5,6,7,8], [2,4,6,8]); + checkPromiseAndArray('reduce', sum, [1,2,3,4], 10); + checkPromiseAndArray('pluck', 'blah', [{ blah: 'diddy' }, { blah: 'doo' }], ['diddy', 'doo']); + checkPromiseAndArray('compact', null, ['blah', null, 'diddy', false, 'doo', undefined], ['blah', 'diddy', 'doo']); + checkPromiseAndArray('xor', [4,2], [1,2], [1,4]); + checkPromiseAndArray('groupBy', Math.floor, [4.2,6.1,6.4], { '4': [4.2], '6': [6.1,6.4] } ); + + it('allows chaining', function() { + function dub(n) { return n * 2; } + + var arr = [1,2,3,4].map($q.when); + + expect(_(arr).thenMap(dub)).to.eventually.deep.equal([2,4,6,8]); + }); + +});