Skip to content

Commit

Permalink
Changed base.attribute.destroy to destroy the attribute's old value w…
Browse files Browse the repository at this point in the history
…hen it is changed.

Added the collection.destroyItems property (false by default) which causes the collection to destroy items when removed or the collection itself is destroyed.
  • Loading branch information
jordandh committed Jan 8, 2016
1 parent 0855f39 commit d7eff18
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 2 deletions.
7 changes: 7 additions & 0 deletions src/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,13 @@ define(["sprout/util", "sprout/pubsub", "sprout/env"], function (_, pubsub, env)
else {
valueChanged = changeAttribute.call(this, attribute, name, oldValue, value);
}

if (valueChanged) {
// If the attribute should auto-destroy the value then do so
if (attribute.destroy && _.isObject(oldValue) && _.isFunction(oldValue.destroy)) {
oldValue.destroy();
}
}
}

return valueChanged;
Expand Down
31 changes: 29 additions & 2 deletions src/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ define(["sprout/util", "sprout/base", "sprout/model", "sprout/data", "sprout/dom
*/
destructor: function ()
{
// Destroy the items if necessary
if (this.destroyItems) {
_.each(this.items, function (item) {
if (item && _.isFunction(item.destroy)) {
item.destroy();
}
});
}

this.items = null;
this.itemsById = null;
this.itemsByCid = null;
Expand Down Expand Up @@ -279,6 +288,14 @@ define(["sprout/util", "sprout/base", "sprout/model", "sprout/data", "sprout/dom
* @type Boolean
*/
sparse: false,

/**
* Whether or not the items should be destroyed when they are removed from the collection.
* An item will be destroyed when removed from the collection or when the collection is destroyed.
* @property
* @type Boolean
*/
destroyItems: false,

/**
* Returns an array of the collection's items for JSON stringification. The results of this function can be used as the argument for collection.parse.
Expand Down Expand Up @@ -449,6 +466,11 @@ define(["sprout/util", "sprout/base", "sprout/model", "sprout/data", "sprout/dom
delete this.itemsById[this.items[index].get("id")];
delete this.itemsByCid[item.get("cid")];
this.items.splice(index, 1);

// Destroy the item if necessary. If this is a move modification then the items are being added back so don't destroy them.
if (this.destroyItems && !options.move && item && _.isFunction(item.destroy)) {
item.destroy();
}
}
}, this);

Expand Down Expand Up @@ -524,10 +546,15 @@ define(["sprout/util", "sprout/base", "sprout/model", "sprout/data", "sprout/dom
*/
reset: createListModifier("reset", function (items)
{
// Detach event handlers
// Detach event handlers and destroy items
this.each(function (item) {
item.detachAfter("sync", afterItemSynced, this);
});

// Destroy the item if necessary
if (this.destroyItems && item && _.isFunction(item.destroy)) {
item.destroy();
}
}, this);

// Set the items array equal to a new empty array
this.items = [];
Expand Down
61 changes: 61 additions & 0 deletions test/base-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,67 @@ TestCase("base", ["sprout/util", "sprout/base", "sprout/env"], function (_, Base
a.set('children', ['a', 'b']);
a.set('children', ['a', 'b']);
},

"test base.attribute.destroy does destroy old value on change": function () {
expectAsserts(2);

var Animal = Base.extend({
attributes: {
head: {
destroy: true
}
}
});

var a = Animal.create();
var head = Base.create();

a.set('head', head);

assert('head is destroyed', !head.get('destroyed'));
a.set('head', null);
assert('head is not destroyed', head.get('destroyed'));
},

"test base.attribute.destroy does not call destroy on old value with no destroy method": function () {
expectAsserts(2);

var Animal = Base.extend({
attributes: {
head: {
destroy: true
}
}
});

var a = Animal.create();
var head = {};

a.set('head', head);

assertObject('head does not exist before', a.get('head'));
a.set('head', null);
assertObject('head does not exist after', head);
},

"test base.attribute.destroy is false does not destroy old value on change": function () {
expectAsserts(2);

var Animal = Base.extend({
attributes: {
head: {}
}
});

var a = Animal.create();
var head = Base.create();

a.set('head', head);

assert('head is destroyed before', !head.get('destroyed'));
a.set('head', null);
assert('head is destroyed after', !head.get('destroyed'));
},

"test base.get with no parameters": function ()
{
Expand Down
183 changes: 183 additions & 0 deletions test/collection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,189 @@ TestCase("collection", ["sprout/util", "sprout/collection", "sprout/model"], fun
assertSame('col[0] has incorrect index value after sort', 0, col.get('0.index'));
assertSame('col[1] has incorrect index value after sort', 1, col.get('1.index'));
assertSame('col[2] has incorrect index value after sort', 2, col.get('2.index'));
},

"test collection.destroyItems on collection.destroy": function ()
{
var col = collection.create();
col.destroyItems = true;

var mod1 = model.create({
id: 1
});
col.add(mod1);

var mod2 = model.create({
id: 2
});
col.add(mod2);

var mod3 = model.create({
id: 3
});
col.add(mod3);

assert('mod1 is destroyed before', !mod1.get('destroyed'));
assert('mod2 is destroyed before', !mod2.get('destroyed'));
assert('mod3 is destroyed before', !mod3.get('destroyed'));

col.destroy();

assert('mod1 is not destroyed after', mod1.get('destroyed'));
assert('mod2 is not destroyed after', mod2.get('destroyed'));
assert('mod3 is not destroyed after', mod3.get('destroyed'));
},

"test collection.destroyItems is false on collection.destroy": function ()
{
var col = collection.create();

var mod1 = model.create({
id: 1
});
col.add(mod1);

var mod2 = model.create({
id: 2
});
col.add(mod2);

var mod3 = model.create({
id: 3
});
col.add(mod3);

assert('mod1 is destroyed before', !mod1.get('destroyed'));
assert('mod2 is destroyed before', !mod2.get('destroyed'));
assert('mod3 is destroyed before', !mod3.get('destroyed'));

col.destroy();

assert('mod1 is destroyed after', !mod1.get('destroyed'));
assert('mod2 is destroyed after', !mod2.get('destroyed'));
assert('mod3 is destroyed after', !mod3.get('destroyed'));
},

"test collection.destroyItems on collection.reset": function ()
{
var col = collection.create();
col.destroyItems = true;

var mod1 = model.create({
id: 1
});
col.add(mod1);

var mod2 = model.create({
id: 2
});
col.add(mod2);

var mod3 = model.create({
id: 3
});
col.add(mod3);

assert('mod1 is destroyed before', !mod1.get('destroyed'));
assert('mod2 is destroyed before', !mod2.get('destroyed'));
assert('mod3 is destroyed before', !mod3.get('destroyed'));

col.reset(null);

assert('mod1 is not destroyed after', mod1.get('destroyed'));
assert('mod2 is not destroyed after', mod2.get('destroyed'));
assert('mod3 is not destroyed after', mod3.get('destroyed'));
},

"test collection.destroyItems is false on collection.reset": function ()
{
var col = collection.create();

var mod1 = model.create({
id: 1
});
col.add(mod1);

var mod2 = model.create({
id: 2
});
col.add(mod2);

var mod3 = model.create({
id: 3
});
col.add(mod3);

assert('mod1 is destroyed before', !mod1.get('destroyed'));
assert('mod2 is destroyed before', !mod2.get('destroyed'));
assert('mod3 is destroyed before', !mod3.get('destroyed'));

col.reset(null);

assert('mod1 is destroyed after', !mod1.get('destroyed'));
assert('mod2 is destroyed after', !mod2.get('destroyed'));
assert('mod3 is destroyed after', !mod3.get('destroyed'));
},

"test collection.destroyItems on collection.remove": function ()
{
var col = collection.create();
col.destroyItems = true;

var mod1 = model.create({
id: 1
});
col.add(mod1);

var mod2 = model.create({
id: 2
});
col.add(mod2);

var mod3 = model.create({
id: 3
});
col.add(mod3);

assert('mod1 is destroyed before', !mod1.get('destroyed'));
assert('mod2 is destroyed before', !mod2.get('destroyed'));
assert('mod3 is destroyed before', !mod3.get('destroyed'));

col.remove(mod2);

assert('mod1 is destroyed after', !mod1.get('destroyed'));
assert('mod2 is not destroyed after', mod2.get('destroyed'));
assert('mod3 is destroyed after', !mod3.get('destroyed'));
},

"test collection.destroyItems is false on collection.remove": function ()
{
var col = collection.create();

var mod1 = model.create({
id: 1
});
col.add(mod1);

var mod2 = model.create({
id: 2
});
col.add(mod2);

var mod3 = model.create({
id: 3
});
col.add(mod3);

assert('mod1 is destroyed before', !mod1.get('destroyed'));
assert('mod2 is destroyed before', !mod2.get('destroyed'));
assert('mod3 is destroyed before', !mod3.get('destroyed'));

col.remove(mod2);

assert('mod1 is destroyed after', !mod1.get('destroyed'));
assert('mod2 is destroyed after', !mod2.get('destroyed'));
assert('mod3 is destroyed after', !mod3.get('destroyed'));
}
};
});

0 comments on commit d7eff18

Please sign in to comment.