From 0471aa6815439b44c3c01370c6f79f2df345d297 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Fri, 2 Oct 2015 10:24:48 +0200 Subject: [PATCH 01/17] Clarifies the second setup case --- test/test.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.html b/test/test.html index 5aab32a..6f8d11e 100644 --- a/test/test.html +++ b/test/test.html @@ -85,7 +85,7 @@ }); - it("Uses 'this' for the collection, like a regular Backbone member function", function(done) { + it("Can be used with .call instead of as mixin", function(done) { var element1 = document.querySelector('#collection1'); From e707740824139dbbf25d4c0125e47e304ea0fc93 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Tue, 6 Oct 2015 06:52:29 +0200 Subject: [PATCH 02/17] Adds test that can run with command line mocha --- test/backbone-emulated-events-spec.js | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/backbone-emulated-events-spec.js diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js new file mode 100644 index 0000000..145c0b2 --- /dev/null +++ b/test/backbone-emulated-events-spec.js @@ -0,0 +1,60 @@ + +var expect = require('chai').expect; +var BackbonePolymerAttach = require('../backbone-polymer-mixin'); +var Backbone = require('yobo').Backbone; + +var PolymerElementMock = function() { + + this.splice = function(path, index, removeCount, items) { + console.log('Polymer got splice', arguments); + }; + + this.notifyPath = function() { + console.log('Polymer got notifyPath', arguments); + }; + +}; + +describe("Array modification through Polymer splices, emulate backbone events", function() { + + describe("Backbone events before backbone-polymer", function() { + + it("add model", function() { + var c = new Backbone.Collection(); + var m = new Backbone.Model({id: 'a1', type: 'testmodel'}) + c.on('add', function(model, collection, options) { + console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options)); + }); + c.add(m); + }); + + it("add model at index", function() { + var c = new Backbone.Collection(); + c.add({id: 'a2', type: 'testmodel2'}); + c.add({id: 'a3', type: 'testmodel3'}); + var m = new Backbone.Model({id: 'a1', type: 'testmodel'}) + c.on('add', function(model, collection, options) { + console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options)); + }); + var added = c.add(m, {at: 1}); + console.log('add returned', m === added ? '(model)' : model); + }); + + }); + + describe("#add", function() { + + it("Is transparent to backbone add event listener", function() { + var e = new PolymerElementMock(); + var c = new Backbone.Collection(); + var adds = []; + c.on('add', function() { adds.push(arguments); }); + BackbonePolymerAttach.call(c, e, 'edit.units'); + var m = c.add(new Backbone.Model({id: 'add1', type: 'test'})); + expect(m.get('type')).to.equal('test'); + expect(adds).to.have.length(1); + }); + + }); + +}); From ef752783c4fb71786bfb560ef58b89333bd5da8e Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Tue, 6 Oct 2015 06:54:16 +0200 Subject: [PATCH 03/17] Reduces backbone-polymer to only run notifications on changes to existing attributes, no splices support --- backbone-polymer-mixin.js | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index 33e5997..26094b9 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -14,43 +14,7 @@ var BackbonePolymerAttach = function(element, pathPrefix) { }); }; - var splicesObject = this.models; - - var addNotify = function(model) { - var ix = indexOf(model); - - // https://www.polymer-project.org/1.0/docs/devguide/properties.html#array-observation - var change = {keySplices:[], indexSplices:[]}; - change.keySplices.push({ - index: ix, - removed: [], - removedItems: [], - added: [ix] - }); - change.indexSplices.push({ - index: ix, - addedCount: 1, - removed: [], - object: splicesObject, - type: 'splice', - addedKeys: [ix] - }); - - element.notifyPath(pathPrefix + '.models.splices', change); - - // remove this timeout and the rendered element goes blank - window.setTimeout(function() { - // TODO would it be possible to notify .* here? - for (var attribute in model.attributes) { - // we could reuse code with modelSetup here - var value = model.get(attribute); - element.notifyPath(pathPrefix + '.models.' + ix + '.attributes.' + attribute, value); - } - }, 1); - }; - this.each(modelSetup.bind(this)); - this.on('add', addNotify.bind(this)); this.on('add', modelSetup.bind(this)); }; From 8da661e5ecae5333dffccf42f4c3afc4d5eedecf Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Tue, 6 Oct 2015 07:07:55 +0200 Subject: [PATCH 04/17] Moves modelSetup to our overridden .add --- backbone-polymer-mixin.js | 14 +++++++++++++- test/backbone-emulated-events-spec.js | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index 26094b9..3a555f1 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -15,7 +15,19 @@ var BackbonePolymerAttach = function(element, pathPrefix) { }; this.each(modelSetup.bind(this)); - this.on('add', modelSetup.bind(this)); + + var _add = this.add; + this.add = function(models, options) { + if (typeof models.length !== 'undefined') { + throw new Error('backbone-polymer only accepts add of single model'); + } + // we should probably operate on the Collection.set level to be more allowing + if (!this._isModel(models)) { + throw new Error('backbone-polymer requires model instances, not just attributes'); + } + + modelSetup.bind(models); + }; }; if (typeof module !== 'undefined') { diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 145c0b2..902fbd5 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -44,6 +44,22 @@ describe("Array modification through Polymer splices, emulate backbone events", describe("#add", function() { + it("ATM accepts only a single item", function() { + expect(function() { + var c = new Backbone.Collection(); + BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units'); + c.add([]); + }).to.throw(/single/); + }); + + it("ATM requires that item to be a real model", function() { + expect(function() { + var c = new Backbone.Collection(); + BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units'); + c.add({id: 'add1', type: 'test'}); + }).to.throw(/requires model instance/); + }); + it("Is transparent to backbone add event listener", function() { var e = new PolymerElementMock(); var c = new Backbone.Collection(); From 5ce4ad029e46df92052c2d72ffda5ba477d2bf13 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Tue, 6 Oct 2015 07:34:35 +0200 Subject: [PATCH 05/17] First attempt at running a regular Polymer splice and an emulated Backbone add --- backbone-polymer-mixin.js | 19 +++++++++++++++---- test/backbone-emulated-events-spec.js | 12 ++++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index 3a555f1..f60eef0 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -16,17 +16,28 @@ var BackbonePolymerAttach = function(element, pathPrefix) { this.each(modelSetup.bind(this)); + // override Backbone add var _add = this.add; - this.add = function(models, options) { - if (typeof models.length !== 'undefined') { + var addOptions = {add: true, remove: false}; // from backbone source + this.add = function(model, options) { + if (_.isArray(model)) { throw new Error('backbone-polymer only accepts add of single model'); } // we should probably operate on the Collection.set level to be more allowing - if (!this._isModel(models)) { + if (!this._isModel(model)) { throw new Error('backbone-polymer requires model instances, not just attributes'); } - modelSetup.bind(models); + var options = _.extend({merge: false}, options, addOptions); + var ix = options.at || 0; + + element.splice(pathPrefix + '.models', ix, 0, [model]); + if (!options.silent) { + this.trigger('add', model, this, options); + } + + modelSetup.bind(model); + return model; }; }; diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 902fbd5..d7c3e3d 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -1,12 +1,19 @@ var expect = require('chai').expect; -var BackbonePolymerAttach = require('../backbone-polymer-mixin'); + +// still undecided on how the mixin gets dependencies +_ = require('yobo')._; + var Backbone = require('yobo').Backbone; +var BackbonePolymerAttach = require('../backbone-polymer-mixin'); + var PolymerElementMock = function() { + var spliced = this.spliced = []; + this.splice = function(path, index, removeCount, items) { - console.log('Polymer got splice', arguments); + spliced.push({path: path, index: index, removeCount: removeCount, items: items}); }; this.notifyPath = function() { @@ -69,6 +76,7 @@ describe("Array modification through Polymer splices, emulate backbone events", var m = c.add(new Backbone.Model({id: 'add1', type: 'test'})); expect(m.get('type')).to.equal('test'); expect(adds).to.have.length(1); + expect(e.spliced).to.have.length(1); }); }); From b363b42ee8c9e09b9299e6067b76aba7dca3558b Mon Sep 17 00:00:00 2001 From: Jonathan Andersson Date: Tue, 6 Oct 2015 09:37:19 +0200 Subject: [PATCH 06/17] tests for Options object. --- test/backbone-emulated-events-spec.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index d7c3e3d..734db1f 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -71,12 +71,35 @@ describe("Array modification through Polymer splices, emulate backbone events", var e = new PolymerElementMock(); var c = new Backbone.Collection(); var adds = []; + c.on('add', function() { adds.push(arguments); }); BackbonePolymerAttach.call(c, e, 'edit.units'); var m = c.add(new Backbone.Model({id: 'add1', type: 'test'})); + expect(m.get('type')).to.equal('test'); expect(adds).to.have.length(1); expect(e.spliced).to.have.length(1); + + //Expects on the options obj. + expect(adds[0][2]).to.be.an('object'); + expect(adds[0][2]).to.have.property('add').and.equal(true); + expect(adds[0][2]).to.have.property('merge').and.equal(false); + expect(adds[0][2]).to.have.property('remove').and.equal(false); + + var m1 = new Backbone.Model({id: 'add2', type: 'test'}); + c.add(m1, {at: 1}); + + //Adding at an index, expects Options to have 'at' + expect(adds[1][2]).to.have.property('add').and.equal(true); + expect(adds[1][2]).to.have.property('at').and.equal(1); + expect(e.spliced).to.have.length(2); + + //Adding again at index 2 + var m2 = new Backbone.Model({id: 'add3', type: 'test'}); + c.add(m2, {at: 2}); + + expect(e.spliced[2]).to.have.property('index').and.equal(2); + }); }); From 99bfa393589a96e44c513c011ff1b624f15537fe Mon Sep 17 00:00:00 2001 From: Jonathan Andersson Date: Wed, 7 Oct 2015 07:24:10 +0200 Subject: [PATCH 07/17] The length property is now set in the add override, and tests for it. --- backbone-polymer-mixin.js | 4 +++- test/backbone-emulated-events-spec.js | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index f60eef0..6a236e3 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -20,6 +20,8 @@ var BackbonePolymerAttach = function(element, pathPrefix) { var _add = this.add; var addOptions = {add: true, remove: false}; // from backbone source this.add = function(model, options) { + this.length = this.length + 1; + console.log('add (model)', model, '(options)', options); if (_.isArray(model)) { throw new Error('backbone-polymer only accepts add of single model'); } @@ -31,7 +33,7 @@ var BackbonePolymerAttach = function(element, pathPrefix) { var options = _.extend({merge: false}, options, addOptions); var ix = options.at || 0; - element.splice(pathPrefix + '.models', ix, 0, [model]); + element.splice(pathPrefix + '.models', ix, 0, model); if (!options.silent) { this.trigger('add', model, this, options); } diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 734db1f..37db30d 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -85,6 +85,7 @@ describe("Array modification through Polymer splices, emulate backbone events", expect(adds[0][2]).to.have.property('add').and.equal(true); expect(adds[0][2]).to.have.property('merge').and.equal(false); expect(adds[0][2]).to.have.property('remove').and.equal(false); + expect(adds[0][1]).to.have.property('length').and.equal(1); var m1 = new Backbone.Model({id: 'add2', type: 'test'}); c.add(m1, {at: 1}); @@ -92,16 +93,15 @@ describe("Array modification through Polymer splices, emulate backbone events", //Adding at an index, expects Options to have 'at' expect(adds[1][2]).to.have.property('add').and.equal(true); expect(adds[1][2]).to.have.property('at').and.equal(1); + expect(adds[0][1]).to.have.property('length').and.equal(2); expect(e.spliced).to.have.length(2); //Adding again at index 2 var m2 = new Backbone.Model({id: 'add3', type: 'test'}); c.add(m2, {at: 2}); - - expect(e.spliced[2]).to.have.property('index').and.equal(2); + expect(e.spliced[2]).to.have.property('index').and.equal(2); + expect(adds[0][1]).to.have.property('length').and.equal(3); }); - }); - }); From be395a3cd50d090bbb5d9d94c5bf36af4a8381e0 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 15:42:20 +0200 Subject: [PATCH 08/17] Removes debug logging --- backbone-polymer-mixin.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index 6a236e3..faaa5bc 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -1,6 +1,5 @@ var BackbonePolymerAttach = function(element, pathPrefix) { - console.log('BackbonePolymerAttach', this, element, pathPrefix); var indexOf = this.indexOf.bind(this); @@ -21,7 +20,6 @@ var BackbonePolymerAttach = function(element, pathPrefix) { var addOptions = {add: true, remove: false}; // from backbone source this.add = function(model, options) { this.length = this.length + 1; - console.log('add (model)', model, '(options)', options); if (_.isArray(model)) { throw new Error('backbone-polymer only accepts add of single model'); } From 2dd756b3e2c5f9fcfbb2431bd2291e7e0ff084e7 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 15:42:46 +0200 Subject: [PATCH 09/17] Clarifies some test and uncovers a serious flaw in backbone compatibility --- test/backbone-emulated-events-spec.js | 45 +++++++++++++++++---------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 37db30d..1d61f7d 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -17,7 +17,7 @@ var PolymerElementMock = function() { }; this.notifyPath = function() { - console.log('Polymer got notifyPath', arguments); + // our code still does this for attribute changes }; }; @@ -45,13 +45,16 @@ describe("Array modification through Polymer splices, emulate backbone events", }); var added = c.add(m, {at: 1}); console.log('add returned', m === added ? '(model)' : model); + // common Backbone operations + expect(c.get('a1')).to.exist.and.have.property('cid'); + expect(c.at(0)).to.exist.and.have.property('cid'); }); }); - describe("#add", function() { + describe("#add, reduced Backbone functionality", function() { - it("ATM accepts only a single item", function() { + it("Accepts only a single item", function() { expect(function() { var c = new Backbone.Collection(); BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units'); @@ -59,7 +62,7 @@ describe("Array modification through Polymer splices, emulate backbone events", }).to.throw(/single/); }); - it("ATM requires that item to be a real model", function() { + it("Requires item to be a real model", function() { expect(function() { var c = new Backbone.Collection(); BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units'); @@ -70,9 +73,12 @@ describe("Array modification through Polymer splices, emulate backbone events", it("Is transparent to backbone add event listener", function() { var e = new PolymerElementMock(); var c = new Backbone.Collection(); + var adds = []; + c.on('add', function(m, c, o) { + adds.push({model:m, collection:c, options:o}); + }); - c.on('add', function() { adds.push(arguments); }); BackbonePolymerAttach.call(c, e, 'edit.units'); var m = c.add(new Backbone.Model({id: 'add1', type: 'test'})); @@ -81,27 +87,32 @@ describe("Array modification through Polymer splices, emulate backbone events", expect(e.spliced).to.have.length(1); //Expects on the options obj. - expect(adds[0][2]).to.be.an('object'); - expect(adds[0][2]).to.have.property('add').and.equal(true); - expect(adds[0][2]).to.have.property('merge').and.equal(false); - expect(adds[0][2]).to.have.property('remove').and.equal(false); - expect(adds[0][1]).to.have.property('length').and.equal(1); + expect(adds[0].options).to.be.an('object'); + expect(adds[0].options).to.have.property('add').and.equal(true); + expect(adds[0].options).to.have.property('merge').and.equal(false); + expect(adds[0].options).to.have.property('remove').and.equal(false); + expect(adds[0].options).to.not.have.property('at'); + expect(adds[0].collection).to.have.property('length').and.equal(1); var m1 = new Backbone.Model({id: 'add2', type: 'test'}); c.add(m1, {at: 1}); //Adding at an index, expects Options to have 'at' - expect(adds[1][2]).to.have.property('add').and.equal(true); - expect(adds[1][2]).to.have.property('at').and.equal(1); - expect(adds[0][1]).to.have.property('length').and.equal(2); + expect(adds[1].options).to.have.property('add').and.equal(true); + expect(adds[1].options).to.have.property('at').and.equal(1); + expect(adds[0].collection).to.have.property('length').and.equal(2); expect(e.spliced).to.have.length(2); - //Adding again at index 2 + //Adding again at index 1, i.e. insert var m2 = new Backbone.Model({id: 'add3', type: 'test'}); - c.add(m2, {at: 2}); + c.add(m2, {at: 1}); + + expect(e.spliced[2]).to.have.property('index').and.equal(1); + expect(adds[0].collection).to.have.property('length').and.equal(3); - expect(e.spliced[2]).to.have.property('index').and.equal(2); - expect(adds[0][1]).to.have.property('length').and.equal(3); + // common Backbone operations + expect(c.get('add1')).to.exist.and.have.property('cid'); + expect(c.at(1)).to.exist.and.have.property('cid'); }); }); }); From afca1534b302cd51ddd19e3fb2bcac93d060ac93 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 15:54:29 +0200 Subject: [PATCH 10/17] Uncovers another flaw: that we don't trigger the event on model. --- test/backbone-emulated-events-spec.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 1d61f7d..8c844b8 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -39,10 +39,13 @@ describe("Array modification through Polymer splices, emulate backbone events", var c = new Backbone.Collection(); c.add({id: 'a2', type: 'testmodel2'}); c.add({id: 'a3', type: 'testmodel3'}); - var m = new Backbone.Model({id: 'a1', type: 'testmodel'}) + var m = new Backbone.Model({id: 'a1', type: 'testmodel'}); c.on('add', function(model, collection, options) { console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options)); }); + m.on('add', function(model, collection, options) { + console.log('model add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options)); + }); var added = c.add(m, {at: 1}); console.log('add returned', m === added ? '(model)' : model); // common Backbone operations @@ -88,20 +91,26 @@ describe("Array modification through Polymer splices, emulate backbone events", //Expects on the options obj. expect(adds[0].options).to.be.an('object'); - expect(adds[0].options).to.have.property('add').and.equal(true); - expect(adds[0].options).to.have.property('merge').and.equal(false); - expect(adds[0].options).to.have.property('remove').and.equal(false); + expect(adds[0].options).to.have.property('add').that.equals(true); + expect(adds[0].options).to.have.property('merge').that.equals(false); + expect(adds[0].options).to.have.property('remove').that.equals(false); expect(adds[0].options).to.not.have.property('at'); expect(adds[0].collection).to.have.property('length').and.equal(1); var m1 = new Backbone.Model({id: 'add2', type: 'test'}); + modeladd = []; + m1.on('add', function(m, c, o) { + modeladd.push({model:m, collection:c, options:o}); + }); c.add(m1, {at: 1}); //Adding at an index, expects Options to have 'at' - expect(adds[1].options).to.have.property('add').and.equal(true); - expect(adds[1].options).to.have.property('at').and.equal(1); - expect(adds[0].collection).to.have.property('length').and.equal(2); + expect(adds[1].options).to.have.property('add').that.equals(true); + expect(adds[1].options).to.have.property('at').that.equals(1); + expect(adds[1].collection).to.have.length(2); expect(e.spliced).to.have.length(2); + expect(modeladd).to.have.length(1); + expect(modeladd[0]).to.deep.equal(adds[1]); //Adding again at index 1, i.e. insert var m2 = new Backbone.Model({id: 'add3', type: 'test'}); From 5469a386039a2ab0398e83206d7d227e65d4369b Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 16:05:34 +0200 Subject: [PATCH 11/17] Further reduces scope of add, so we don't need to re-implement all of collection.set --- backbone-polymer-mixin.js | 4 +++- test/backbone-emulated-events-spec.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index faaa5bc..76bdbec 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -23,10 +23,12 @@ var BackbonePolymerAttach = function(element, pathPrefix) { if (_.isArray(model)) { throw new Error('backbone-polymer only accepts add of single model'); } - // we should probably operate on the Collection.set level to be more allowing if (!this._isModel(model)) { throw new Error('backbone-polymer requires model instances, not just attributes'); } + if (this.get(model)) { + throw new Error('model already exists as' + this.get(model).cid); + } var options = _.extend({merge: false}, options, addOptions); var ix = options.at || 0; diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 8c844b8..513f1e7 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -73,6 +73,16 @@ describe("Array modification through Polymer splices, emulate backbone events", }).to.throw(/requires model instance/); }); + it("Bails out if the model already exists", function() { + expect(function() { + var c = new Backbone.Collection(); + BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units'); + var m = new Backbone.Model({id: 'add1', type: 'test'}); + c.add(m); + c.add(m); + }).to.throw(/Model add blocked because it already exists as cid \d+/); + }); + it("Is transparent to backbone add event listener", function() { var e = new PolymerElementMock(); var c = new Backbone.Collection(); From 2c89fed653b25deeeb976261cb1ed2f89806bd5a Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 16:10:22 +0200 Subject: [PATCH 12/17] Fixes .get but fails to fix .at --- backbone-polymer-mixin.js | 6 ++++-- test/backbone-emulated-events-spec.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index 76bdbec..5e8482d 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -27,15 +27,17 @@ var BackbonePolymerAttach = function(element, pathPrefix) { throw new Error('backbone-polymer requires model instances, not just attributes'); } if (this.get(model)) { - throw new Error('model already exists as' + this.get(model).cid); + throw new Error('backbone-polymer model already exists as cid ' + this.get(model).cid); } var options = _.extend({merge: false}, options, addOptions); var ix = options.at || 0; element.splice(pathPrefix + '.models', ix, 0, model); + this._addReference(model, options); if (!options.silent) { - this.trigger('add', model, this, options); + model.trigger('add', model, this, options); + this.trigger('update', this, options); } modelSetup.bind(model); diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 513f1e7..9d9724f 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -80,7 +80,7 @@ describe("Array modification through Polymer splices, emulate backbone events", var m = new Backbone.Model({id: 'add1', type: 'test'}); c.add(m); c.add(m); - }).to.throw(/Model add blocked because it already exists as cid \d+/); + }).to.throw(/backbone-polymer model already exists as cid \w+/); }); it("Is transparent to backbone add event listener", function() { From 528552be120aeb85f16b7f503c72727679e6e0bd Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 16:11:43 +0200 Subject: [PATCH 13/17] Something is pretty badly messed up --- test/backbone-emulated-events-spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index 9d9724f..fab4ef2 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -131,6 +131,7 @@ describe("Array modification through Polymer splices, emulate backbone events", // common Backbone operations expect(c.get('add1')).to.exist.and.have.property('cid'); + expect(c.models).to.have.length(3); expect(c.at(1)).to.exist.and.have.property('cid'); }); }); From 80440b7112a54c450753b24afecc673f59b4b30a Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 20:32:33 +0200 Subject: [PATCH 14/17] Does the actual splice from the mock element, so that tests can assert on backbone behavior --- test/backbone-emulated-events-spec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-emulated-events-spec.js index fab4ef2..ee08a0f 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-emulated-events-spec.js @@ -8,12 +8,15 @@ var Backbone = require('yobo').Backbone; var BackbonePolymerAttach = require('../backbone-polymer-mixin'); -var PolymerElementMock = function() { +var PolymerElementMock = function(testArray) { var spliced = this.spliced = []; this.splice = function(path, index, removeCount, items) { spliced.push({path: path, index: index, removeCount: removeCount, items: items}); + if (typeof testArray !== 'undefined') { + testArray.splice.apply(testArray, [index, removeCount].concat(items)); + } }; this.notifyPath = function() { @@ -84,8 +87,8 @@ describe("Array modification through Polymer splices, emulate backbone events", }); it("Is transparent to backbone add event listener", function() { - var e = new PolymerElementMock(); var c = new Backbone.Collection(); + var e = new PolymerElementMock(c.models); var adds = []; c.on('add', function(m, c, o) { From 77dfa29c78eba3a73be72a977f7bdcde8b519d87 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 21:25:57 +0200 Subject: [PATCH 15/17] Converts the backbone test to running in WCT with npm test --- ...ts-spec.js => backbone-compatibility.html} | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) rename test/{backbone-emulated-events-spec.js => backbone-compatibility.html} (88%) diff --git a/test/backbone-emulated-events-spec.js b/test/backbone-compatibility.html similarity index 88% rename from test/backbone-emulated-events-spec.js rename to test/backbone-compatibility.html index ee08a0f..fd9144f 100644 --- a/test/backbone-emulated-events-spec.js +++ b/test/backbone-compatibility.html @@ -1,12 +1,20 @@ - -var expect = require('chai').expect; - -// still undecided on how the mixin gets dependencies -_ = require('yobo')._; - -var Backbone = require('yobo').Backbone; - -var BackbonePolymerAttach = require('../backbone-polymer-mixin'); + + + + + + + + + + + + + + + + + + + From 5a2fcaf97bb00b3040120319e42e5c3bfd54a242 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Wed, 7 Oct 2015 21:27:10 +0200 Subject: [PATCH 16/17] Complies with the new rule to use real models. If we don't like these restrictions on .add we should continue the collection-set-patch branch --- test/test.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.html b/test/test.html index 6f8d11e..1cabfa9 100644 --- a/test/test.html +++ b/test/test.html @@ -106,7 +106,7 @@ expect(renderTimeAllowMs).to.be.at.least(2); // the sequence below is fragile, and there's a timeout in the notification too window.setTimeout(function() { - m1 = c1.add({type: 'text', content: 'As added after setup.'}); + m1 = c1.add(new Backbone.Model({type: 'text', content: 'As added after setup.'})); }, renderTimeAllowMs); window.setTimeout(function() { From 64baa18edbe1f396086d42e2a6276ca26ca718f8 Mon Sep 17 00:00:00 2001 From: Staffan Olsson Date: Thu, 8 Oct 2015 07:04:23 +0200 Subject: [PATCH 17/17] Fixes setup of change notification after collection add. All tests pass now. --- backbone-polymer-mixin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backbone-polymer-mixin.js b/backbone-polymer-mixin.js index 5e8482d..74b716d 100644 --- a/backbone-polymer-mixin.js +++ b/backbone-polymer-mixin.js @@ -40,7 +40,7 @@ var BackbonePolymerAttach = function(element, pathPrefix) { this.trigger('update', this, options); } - modelSetup.bind(model); + modelSetup.call(this, model); return model; }; };