From c668fa041af186d241a4b8c55219b4732246c9f7 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 10:20:49 -0700 Subject: [PATCH] [Core] Allow listening for mutation Allow listeners to register with a domain object's mutation capability to detect changes to that domain object. Allows other components to respond to these changes without resorting to polling on timestamp or similar. WTD-1329. --- .../src/capabilities/MutationCapability.js | 30 +++++++++++++++++-- .../capabilities/MutationCapabilitySpec.js | 22 +++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/platform/core/src/capabilities/MutationCapability.js b/platform/core/src/capabilities/MutationCapability.js index c20c90deab..11f3973e19 100644 --- a/platform/core/src/capabilities/MutationCapability.js +++ b/platform/core/src/capabilities/MutationCapability.js @@ -72,6 +72,13 @@ define( * @constructor */ function MutationCapability(now, domainObject) { + var listeners = []; + + function notifyListeners(model) { + listeners.forEach(function (listener) { + listener(model); + }); + } function mutate(mutator, timestamp) { // Get the object's model and clone it, so the @@ -96,6 +103,7 @@ define( copyValues(model, result); } model.modified = useTimestamp ? timestamp : now(); + notifyListeners(model); } // Report the result of the mutation @@ -107,6 +115,15 @@ define( return fastPromise(mutator(clone)).then(handleMutation); } + function listen(listener) { + listeners.push(listener); + return function unlisten() { + listeners = listeners.filter(function (l) { + return l !== listener; + }); + }; + } + return { /** * Alias of `mutate`, used to support useCapability. @@ -139,10 +156,19 @@ define( * @returns {Promise.} a promise for the result * of the mutation; true if changes were made. */ - mutate: mutate + mutate: mutate, + /** + * Listen for mutations of this domain object's model. + * The provided listener will be invoked with the domain + * object's new model after any changes. To stop listening, + * invoke the function returned by this method. + * @param {Function} listener function to call on mutation + * @returns {Function} a function to stop listening + */ + listen: listen }; } return MutationCapability; } -); \ No newline at end of file +); diff --git a/platform/core/test/capabilities/MutationCapabilitySpec.js b/platform/core/test/capabilities/MutationCapabilitySpec.js index 2dadfa7dfa..99dd3a74c1 100644 --- a/platform/core/test/capabilities/MutationCapabilitySpec.js +++ b/platform/core/test/capabilities/MutationCapabilitySpec.js @@ -83,6 +83,26 @@ define( // Should have gotten a timestamp from 'now' expect(testModel.modified).toEqual(42); }); + + it("notifies listeners of mutation", function () { + var mockCallback = jasmine.createSpy('callback'); + mutation.listen(mockCallback); + mutation.invoke(function (m) { + m.number = 8; + }); + expect(mockCallback).toHaveBeenCalled(); + expect(mockCallback.mostRecentCall.args[0].number) + .toEqual(8); + }); + + it("allows listeners to stop listening", function () { + var mockCallback = jasmine.createSpy('callback'); + mutation.listen(mockCallback)(); // Unlisten immediately + mutation.invoke(function (m) { + m.number = 8; + }); + expect(mockCallback).not.toHaveBeenCalled(); + }); }); } -); \ No newline at end of file +);