diff --git a/spec/store/events/off-spec.js b/spec/store/events/off-spec.js index 60eba2c..a48bf7d 100644 --- a/spec/store/events/off-spec.js +++ b/spec/store/events/off-spec.js @@ -11,7 +11,7 @@ describe("off", function() { }; context = {}; spyOn(listener, "handler"); - store.define("products", {}); + store.define([ "products", "product" ], {}); }); it("must remove event handlers", function () { @@ -61,4 +61,14 @@ describe("off", function() { }).toThrowError("Unknown type 'foo'"); }); + it("must remove listeners added with a different pseudonym", function () { + store.on("added", "products", listener.handler, context); + store.off("added", "product", listener.handler); + store.add({ + "type": "products", + "id": "1" + }); + expect(listener.handler.calls.count()).toEqual(0); + }); + }); diff --git a/spec/store/events/on-spec.js b/spec/store/events/on-spec.js index bccdb00..68d1b5e 100644 --- a/spec/store/events/on-spec.js +++ b/spec/store/events/on-spec.js @@ -11,7 +11,7 @@ describe("on", function() { }; context = {}; spyOn(listener, "handler"); - store.define("products", {}); + store.define([ "products", "product" ], {}); }); it("must fire an added event when the resource with the given type & id is added to the store", function () { @@ -106,4 +106,63 @@ describe("on", function() { }).toThrowError("Unknown type 'foo'"); }); + it("must call listeners that were added using a different pseudonym", function () { + store.on("added", "product", listener.handler, context); + store.add({ + "type": "products", + "id": "1" + }); + expect(listener.handler.calls.count()).toEqual(1); + expect(listener.handler).toHaveBeenCalledWith(store.find("products", "1")); + }); + + it("must not add the same listener multiple times for the same type", function () { + store.on("added", "products", listener.handler, context); + store.on("added", "products", listener.handler); + store.add({ + "type": "products", + "id": "1" + }); + expect(listener.handler.calls.count()).toEqual(1); + expect(listener.handler).toHaveBeenCalledWith(store.find("products", "1")); + }); + + it("must not add the same listener multiple times for the same type and id", function () { + store.on("added", "products", "1", listener.handler, context); + store.on("added", "products", "1", listener.handler); + store.add({ + "type": "products", + "id": "1" + }); + expect(listener.handler.calls.count()).toEqual(1); + expect(listener.handler).toHaveBeenCalledWith(store.find("products", "1")); + }); + + it("must add the same listener multiple times if the id is different", function () { + store.on("added", "products", "1", listener.handler, context); + store.on("added", "products", "2", listener.handler, context); + store.add({ + "type": "products", + "id": "1" + }); + store.add({ + "type": "products", + "id": "2" + }); + expect(listener.handler.calls.count()).toEqual(2); + expect(listener.handler).toHaveBeenCalledWith(store.find("products", "1")); + expect(listener.handler).toHaveBeenCalledWith(store.find("products", "2")); + }); + + it("must not add the same listener multiple times for different pseudonyms of the same type", function () { + store.on("added", "products", listener.handler, context); + store.on("added", "product", listener.handler, context); + store.add({ + "type": "products", + "id": "1" + }); + expect(listener.handler.calls.count()).toEqual(1); + expect(listener.handler).toHaveBeenCalledWith(store.find("products", "1")); + }); + }); diff --git a/src/store.js b/src/store.js index 70e276c..29f217c 100644 --- a/src/store.js +++ b/src/store.js @@ -210,17 +210,20 @@ export default class Store { if (id && ({}).toString.call(id) === '[object Function]') { this.off.call(this, event, type, null, id, callback); } else { - if (id) { - if (this._resourceListeners[event][type] && this._resourceListeners[event][type][id]) { - this._resourceListeners[event][type][id] = this._resourceListeners[event][type][id].filter(x => { + // TODO: Performance-wise, this can be made way better. There shouldn't be a need to maintain separate lists. + this._types[type]._names.forEach(type => { + if (id) { + if (this._resourceListeners[event][type] && this._resourceListeners[event][type][id]) { + this._resourceListeners[event][type][id] = this._resourceListeners[event][type][id].filter(x => { + return x[0] !== callback; + }); + } + } else if (this._collectionListeners[event][type]) { + this._collectionListeners[event][type] = this._collectionListeners[event][type].filter(x => { return x[0] !== callback; }); } - } else if (this._collectionListeners[event][type]) { - this._collectionListeners[event][type] = this._collectionListeners[event][type].filter(x => { - return x[0] !== callback; - }); - } + }); } } else { throw new Error(`Unknown type '${type}'`); @@ -247,14 +250,21 @@ export default class Store { if (id && ({}).toString.call(id) === '[object Function]') { this.on.call(this, event, type, null, id, callback); } else { - if (id) { - this._resourceListeners[event][type] = this._resourceListeners[event][type] || {}; - this._resourceListeners[event][type][id] = this._resourceListeners[event][type][id] || []; - this._resourceListeners[event][type][id].push([ callback, context ]); - } else { - this._collectionListeners[event][type] = this._collectionListeners[event][type] || []; - this._collectionListeners[event][type].push([ callback, context ]); - } + // TODO: Performance-wise, this can be made way better. There shouldn't be a need to maintain separate lists. + this._types[type]._names.forEach(type => { + if (id) { + this._resourceListeners[event][type] = this._resourceListeners[event][type] || {}; + this._resourceListeners[event][type][id] = this._resourceListeners[event][type][id] || []; + if (!this._resourceListeners[event][type][id].find(x => x[0] === callback)) { + this._resourceListeners[event][type][id].push([ callback, context ]); + } + } else { + this._collectionListeners[event][type] = this._collectionListeners[event][type] || []; + if (!this._collectionListeners[event][type].find(x => x[0] === callback)) { + this._collectionListeners[event][type].push([ callback, context ]); + } + } + }); } } else { throw new Error(`Unknown type '${type}'`);