-
-
Save eimfach/f0cc3ccc533ed75605c37a9c05251aef to your computer and use it in GitHub Desktop.
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
/** | |
* Copyright by Robin Gruenke | |
* License: NOT for public or commercial usage permitted. Third parties are not allowed to reuse this software without permission. | |
* @author Robin Gruenke | |
* @module ns.libs.effects | |
* Service to generate event receivers which specifically manipulate data sets | |
* to trigger certain external sideeffects (like managed time sequenced animations). | |
* These Factories should be depedency free (deps are passed on create), enabliing | |
* to control dynamics via passed functions / promise chains. | |
* | |
* | |
* | |
* @typedef {Object} Options | |
* @property {Function} waitForSomethingBeforeRemoval - returns a Promise | |
* @property {Function} promiseSequencePart - returns a Promise | |
* @property {Function} reject - returns a Promise as resolved | |
* @property {Array.<Notification>} notifications - array reference which will be mutated. Represents shared state (used in e.g. view state to render notifications) | |
* @typedef {Function} receiver | |
* @typedef {Object} Notification | |
* @property {String} id - can be any | |
* @property {*} add any properties for usage in e.g. ui | |
*/ | |
/* | |
* Usage example in directive | |
* | |
* | |
var notificationListener = $rootScope.$on( | |
'NsNotification', | |
nsEffects.notificationReceiverFactory( | |
{ | |
waitForSomethingBeforeRemoval: function () { | |
return deferRemoval(); | |
function deferRemoval() { | |
return nsLibsSystemQueue.waitForAnyNext() | |
.then(function () { | |
// wait at least one second to give | |
// the ui a chance to display the new price to the user | |
// or to defer the removal if the user picks a | |
// new product within the given time | |
return $timeout(1000); | |
}) | |
.then(function () { | |
if (nsLibsSystemQueue.isInProgress('*')) { | |
return deferRemoval(); | |
} | |
}); | |
} | |
}, | |
promiseSequencePart: function () { | |
return $timeout(500); | |
}, | |
notifications: vm.notifications, | |
reject: $q.reject | |
} | |
) | |
); | |
$scope.$on('$destroy', notificationListener); | |
*/ | |
(function () { | |
'use strict'; | |
angular | |
.module('ns.libs.effects', []) | |
.factory('NsEffects', nsEffects); | |
function nsEffects() { | |
return { | |
notificationReceiverFactory: notificationReceiverFactory | |
}; | |
/** | |
* notificationReceiverFactory- creates a function to use as listener for | |
* triggering notifications | |
* @return {receiver} Listener function invoked with Notification object | |
* @param {Options} options | |
* @memberof module:ns.libs.effects | |
* | |
*/ | |
function notificationReceiverFactory(options) { | |
var waitForSomethingBeforeRemoval = options.waitForSomethingBeforeRemoval; | |
var promiseSequencePart = options.promiseSequencePart; | |
var reject = options.reject; | |
var notifications = options.notifications; | |
var managerRunning = false; | |
var removalOngoing = false; | |
/** | |
* receiver - event callback which adds/replaces incoming Notification Objects to | |
* given list and starts a manager which will remove the items in given list | |
* according to a promise resolve | |
* | |
* @param {Object} event | |
* @param {Notification} notification | |
*/ | |
return function receiver(event, notification) { | |
if (removalOngoing === false && !!notification.id) { | |
// Check if a matching notification (by id) exists and replace its values | |
var index = _.findIndex(notifications, function (element) { | |
return notification.id === element.id; | |
}); | |
if (index > -1) { | |
_.assign(notifications[index], notification); | |
return; | |
} | |
} | |
notifications.push(_.assign({}, notification)); | |
if (!managerRunning) { | |
managerRunning = true; | |
waitForSomethingBeforeRemoval() | |
.then(function () { | |
managerRunning = false; | |
removalOngoing = true; | |
return sequencedRemoval(promiseSequencePart, reject, notifications); | |
}) | |
.then(function () { | |
removalOngoing = false; | |
}); | |
} | |
}; | |
} | |
/** | |
* timeSequencedRemoval - takes an array and removes every instance sequentially | |
* based on a resolve of a promise returned by the invokation of `promiseSequencePart` | |
* | |
* @param {function} promiseSequencePart returns a Promise | |
* @param {function} reject callback which returns a rejected promise | |
* @param {Array} list Array to work on | |
* @return {Promise} | |
*/ | |
function sequencedRemoval(promiseSequencePart, reject, list) { | |
if (list.length === 0) { | |
return reject(); | |
} | |
var promiseSequences = list.map(function () { | |
return function () { | |
return promiseSequencePart(); | |
}; | |
}); | |
// remove notification objects sequentially after given time | |
var promiseLastTimeout = promiseSequences.reduce( | |
function (currentSequencePart, nextSequencePart) { | |
return currentSequencePart.then(function () { | |
list.shift(); | |
return nextSequencePart(); | |
}); | |
}, | |
promiseSequences[0]() | |
); | |
return promiseLastTimeout; | |
} | |
} | |
})(); | |
describe('ns.libs.effects', function () { | |
'use strict'; | |
var $q; | |
var $timeout; | |
var nsEffects; | |
var createReceiver; | |
beforeEach(function () { | |
module('ns.libs.effects'); | |
inject(function ($injector) { | |
$q = $injector.get('$q'); | |
$timeout = $injector.get('$timeout'); | |
nsEffects = $injector.get('nsEffects'); | |
createReceiver = function (notifications) { | |
return nsEffects.notificationReceiverFactory( | |
{ | |
waitForSomethingBeforeRemoval: function () { | |
return $q.reject(); | |
}, | |
promiseSequencePart: function () { | |
return $q.resolve(); | |
}, | |
notifications: notifications, | |
reject: $q.reject | |
} | |
); | |
}; | |
}); | |
}); | |
describe('notificationReceiverFactory()', function () { | |
it('returns a function', function () { | |
var receiver = createReceiver([]); | |
expect(_.isFunction(receiver)).toBe(true); | |
}); | |
}); | |
describe('receiver()', function () { | |
var randomVal = _.random(100000); | |
var randomProp = _.random(100000); | |
it('pushes new notification at the end of given array', function () { | |
var notifications = [{}, {}]; | |
var notificationOneRef = notifications[0]; | |
var notificationTwoRef = notifications[1]; | |
var receiver = createReceiver(notifications); | |
var notificationRef = {}; | |
notificationRef[randomProp] = randomVal; | |
receiver({}, notificationRef); | |
var newInstanceRef = notifications[notifications.length - 1]; | |
expect(notifications.length).toEqual(3); | |
expect(notificationOneRef === newInstanceRef).toBe(false); | |
expect(notificationTwoRef === newInstanceRef).toBe(false); | |
}); | |
it('pushes new notification which must be a shallow copy', function () { | |
var notifications = [{}, {}]; | |
var receiver = createReceiver(notifications); | |
var notificationRef = {}; | |
notificationRef[randomProp] = randomVal; | |
receiver({}, notificationRef); | |
var newInstanceRef = notifications[notifications.length - 1]; | |
expect(newInstanceRef !== notificationRef).toBe(true); | |
expect(randomProp in newInstanceRef).toBe(true); | |
expect(newInstanceRef[randomProp] === randomVal).toBe(true); | |
}); | |
it('replaces properties of same id instances which must still be part of the array', function () { | |
var notifications = [{id: 'a', some: 'b'}, {}]; | |
var instanceRef = notifications[0]; | |
var receiver = createReceiver(notifications); | |
var newNotification = {id: 'a', some: 'c'}; | |
newNotification[randomProp] = randomVal; | |
receiver({}, newNotification); | |
expect(notifications.indexOf(instanceRef)).toBeGreaterThan(-1); | |
}); | |
it('replaces properties of same id instances which must copy over everything', function () { | |
var notifications = [{id: 'a', some: 'b'}, {}]; | |
var instanceRef = notifications[0]; | |
var receiver = createReceiver(notifications); | |
var newNotification = {id: 'a', some: 'c'}; | |
newNotification[randomProp] = randomVal; | |
receiver({}, newNotification); | |
expect(randomProp in instanceRef).toBe(true); | |
expect(instanceRef[randomProp] === randomVal); | |
expect(instanceRef.some === 'c'); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment